1. 程式人生 > >閉包 裝飾器

閉包 裝飾器

一 閉包

  只發生在函式的巢狀中,一個內層函式呼叫了外層函式的"變數"
  nonlocal 不是本地作用域的
  檢測是否為閉包的內建屬性 __closure__
  閉包函式可以保留其用到的變數的引用
  外層函式的返回值就是內層函式的引用

def text(k,b):
  k = 1
  b = 2
  def text1(x):
    x = 3
    print(k*x + b)
  return text1    
t = text()
print(t)
一 裝飾器是什麼
  python的裝飾器本質上是一個Python函式,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外功能,裝飾器的返回值也是一個函式物件。簡單的說裝飾器就是一個用來返回函式的函式。
  它經常用於有切面需求的場景,比如:插入日誌、效能測試、事務處理、快取、許可權校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函式功能本身無關的雷同程式碼並繼續重用。
  概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。
  
  1 模板裝飾器
def wrapper(func):
      def inner(*args, **kwargs):
        result = func(*args, **kwargs)
        return result
      return inner
    @wrapper   # text=wrapper(text)
    def text():
      print("python")

  2 多個裝飾器裝飾同一個函式

def wrapper(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        
return "<p>" + ret + "</p>" return inner def decorate(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return "<h1>" + ret + "</h1>" return inner @wrapper @decorate def show(): return " ++love python++ " ret = show() print(ret) result :
"<p><h1> ++love python++ </h1></p>"

  3 裝飾器帶引數

def set_lever(lever_num):
    def wrapper(func):
        def inner(*args, **kwargs):
            if lever_num == 1:
                print("已經獲得1的許可權")
            elif lever_num == 2:
                print("已經獲得2的許可權")
            ret = func(*args, **kwargs)
            return ret
        return inner
    return wrapper

@set_lever("first")
def text1():
    # print("ha111")
    return "love python111"

@set_lever("second")
def text2():
    print("ha222")
    return "love python222"
print(text1)

  4 functools.wraps 

    使用裝飾器極大地複用了程式碼,但是他有一個缺點就是原函式的元資訊不見了,比如函式的docstring、__name__、引數列表,先看例子:
def use_logging(func):
    def _deco(*args,**kwargs):
        print("%s is running" % func.__name__)
        func(*args,**kwargs)
    return _deco

@use_logging
def bar():
    print('i am bar')
    print(bar.__name__)


bar()

#bar is running
#i am bar
#_deco
#函式名變為_deco而不是bar,這個情況在使用反射的特性的時候就會造成問題。因此引入了functools.wraps解決這個問題。

import functools
  def use_logging(func):
  @functools.wraps(func)
    def _deco(*args,**kwargs):
      print("%s is running" % func.__name__)
      return func(*args,**kwargs)
    return _deco

 
 

  @use_logging
  def bar():
  print('i am bar')
  print(bar.__name__)

  bar()

 
 

  #result:
  #bar is running
  #i am bar
  #bar ,這個結果是我們想要的。OK啦!

 

  5 帶引數和不帶引數的裝飾器

 
 

import functools

 
 

def use_logging(arg):
  if callable(arg):#判斷參入的引數是否是函式,不帶引數的裝飾器呼叫這個分支
  @functools.wraps(arg)
    def _deco(*args,**kwargs):
      print("%s is running" % arg.__name__)
      return arg(*args,**kwargs)
    return _deco
  else:#帶引數的裝飾器呼叫這個分支
  def _deco(func):
  @functools.wraps(func)
    def __deco(*args, **kwargs):
      if arg == "warn":
        print "warn%s is running" % func.__name__
        return func(*args, **kwargs)
    return __deco
  return _deco

 
 


@use_logging("warn")
# @use_logging
def bar():
print('i am bar')
print(bar.__name__)

 
 

bar()

 
 

三 類裝飾器

  1 普通類裝飾器

class loging(object):
    def __init__(self,level="warn"):
        self.level = level

    def __call__(self,func):
        @functools.wraps(func)
        def _deco(*args, **kwargs):
            if self.level == "warn":
                self.notify(func)
            return func(*args, **kwargs)
        return _deco

    def notify(self,func):
        # logit只打日誌,不做別的
        print "%s is running" % func.__name__


@loging(level="warn")#執行__call__方法
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,3)

  2 繼承類裝飾器

class email_loging(Loging):
    '''
    一個loging的實現版本,可以在函式呼叫時傳送email給管理員
    '''
    def __init__(self, email='[email protected]', *args, **kwargs):
        self.email = email
        super(email_loging, self).__init__(*args, **kwargs)

    def notify(self,func):
        # 傳送一封email到self.email
        print "%s is running" % func.__name__
        print "sending email to %s" %self.email


@email_loging(level="warn")
def bar(a,b):
    print('i am bar:%s'%(a+b))

bar(1,3)