>>a='globalvar'>>>deffoo():print(a)>>>foo()globalvar然后,在一個嵌套函數中,內層函數能夠訪問在外層函數中定義的局部變量:>>>deffoo():a='freevar'defbar():print(a)returnbar>>>foo()()freevar閉包上面的嵌套函數就是" />

欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

實例詳解Python裝飾器與閉包

系統 1611 0

閉包是Python裝飾器的基礎。要理解閉包,先要了解Python中的變量作用域規則。

變量作用域規則

首先,在函數中是能訪問全局變量的:

            
>>> a = 'global var'
>>> def foo():
 print(a)
>>> foo()
global var
          

然后,在一個嵌套函數中,內層函數能夠訪問在外層函數中定義的局部變量:

            
>>> def foo():
 a = 'free var'
 def bar():
  print(a)
 return bar

>>> foo()()
free var
          

閉包

上面的嵌套函數就是閉包。 閉包 是指延伸了作用域的函數,在其中能夠訪問未在函數定義體中定義的非全局變量。未在函數定義體中定義的非全局變量一般都是在嵌套函數中出現的。

上述示例中的變量a就是一個并未在函數bar中定義的非全局變量。對于bar來說,它有個專業名字,叫做 自由變量 。

自由變量的名稱可以在字節碼對象中查看:

            
>>> bar = foo()
>>> bar.__code__.co_freevars
('a',)
          

自由變量的值綁定在函數的__closure__屬性中:

            
>>> bar.__closure__
(
            
              ,)
            
          

其中保存了對應自由變量的cell對象的序列,cell對象的cell_contents屬性保存了變量的值:

            
>>> bar.__closure__[0].cell_contents
'free var'
          

這與JavaScript中閉包的行為是類似的,JavaScript中嵌套函數會將外層函數的活動對象添加到它的作用域鏈中。但與JavaScript不同的是,當Python函數中的全局變量或者自由變量是不可變對象(數字、字符串、元組等)時,是只能讀取,無法更新的:

            
>>> a = 1
>>> def foo():
 print(a)
 a += 1

>>> foo()
UnboundLocalError: local variable 'a' referenced before assignment

>>> def foo():
 a = 1
 def bar():
  print(a)
  a += 1
 return bar

>>> foo()()
UnboundLocalError: local variable 'a' referenced before assignment
          

兩種情況下,都會報錯。這并不是缺陷,而是Python的設計選擇。Python不要求聲明變量,但是會假定在函數定義體中賦值的變量是局部變量,以避免在不知情的情況下修改全局變量。

a += 1 與 a = a + 1 相同,編譯函數的定義體時,會將a當做局部變量,不會當做自由變量保存。然后嘗試獲取a的值時,發現a并沒有綁定值,于是報錯。

解決這個問題的辦法,一是將變量置于一些可變對象,如列表、字典中:

            
def foo():
 ns = {}
 ns['a'] = 1
 def bar():
  ns['a'] += 1
  print (ns['a'])
 return bar
          

另外的方法就是使用 global 或者 nonlocal 將變量聲明為全局變量或者自由變量:

            
>>> def foo():
 a = 1
 def bar():
  nonlocal a
  a += 1
  print(a)
 return bar

>>> foo()()
2
          

當自由變量本身是可變對象時,是可以直接進行操作的:

            
def make_avg():
 ls = []
 def avg(x):
  ls.append(x)
  print(sum(ls)/len(ls))
 return avg
          

裝飾器

裝飾器是可調用對象,參數一般是另一個函數。裝飾器可以以某種方式增強被裝飾函數的行為,然后返回被裝飾的函數或者將其替換成一個新的函數。

一個最簡單的不做任何額外行為的裝飾器:

            
def decorate(func):
 return func
          

decorate 函數就是一個最簡單的裝飾器,使用方法:

            
def target():
 pass
target = decorate(target)
          

Python為裝飾器的使用提供了語法糖,可以簡便的寫為:

            
@decorate
def target():
 pass
          

導入時運行

裝飾器一個很重要的特性是它是導入時(加載模塊時)運行的:

            
def decorate(func):
 print('running decorator when import')
 return func
@decorate
def foo():
 print('running foo')
 pass
if __name__ == '__main__':
 print('start foo')
 foo()
          

結果:

running decorator when import start foo running foo

可以看到,裝飾器是導入時運行的,而被裝飾的函數是明確調用時運行的。

裝飾器可以返回被裝飾的函數本身,和運行時導入的特性結合起來,可以實現簡單的注冊器功能:

            
view_registry = []
def register(func):
 view_registry.append(func)
 return func
@register
def view1():
 pass
@register
def view2():
 pass
def main():
 print(view_registry)
if __name__ == '__main__':
 main()
          

返回新函數

上述裝飾器的例子都返回了被裝飾的原函數,但裝飾器的典型行為還是返回一個新函數:把被裝飾的函數替換成新函數,新函數接受與原函數相同的參數,并且返回原函數本該返回的值。寫法類似于:

            
def deco(func):
 def new_func(*args, **kwargs):
  return func(*args, **kwargs)
 return new_func
          

這種情況下裝飾器就使用到了閉包。JavaScript中的防抖與節流函數就是這種典型的裝飾器行為。新函數一般會使用外部裝飾器函數中的變量當做自由變量,對函數作出某種增強行為。

舉個例子,我們知道,當Python函數的參數是個可變對象時,會產生意料之外的行為:

            
def foo(x, y=[]):
  y.append(x)
  print(y)

foo(1)
foo(2)
foo(3)

          

輸出:

[1] [1, 2] [1, 2, 3]

這是因為,函數的參數默認值保存在__defaults__屬性中,指向了同一個列表:

            
>>> foo.__defaults__
([1, 2, 3],)
          

我們就可以用一個裝飾器在函數執行前取出默認值做深復制,然后覆蓋函數原先的參數默認值:

            
import copy
def fresh_defaults(func):
  defaults = func.__defaults__
  def deco(*args, **kwargs):
    func.__defaults__ = copy.deepcopy(defaults)
    return func(*args, **kwargs)
  return deco
@fresh_defaults
def foo(x, y=[]):
  y.append(x)
  print(y)
foo(1)
foo(2)
foo(3)
          

輸出:

[1] [2] [3]

接收參數的裝飾器

裝飾器除了可以接受函數作為參數外,還可以接受其他參數。使用方法是:創建一個裝飾器工廠,接受參數,返回一個裝飾器,再把它應用到被裝飾的函數上,語法如下:

            
def deco_factory(*args, **kwargs):
  def deco(func):
    print(args)
    return func
  return deco
@deco_factory('factory')
def foo():
  pass
          

在Web框架中,通常要將URL模式映射到生成響應的view函數,并將view函數注冊到某些中央注冊處。之前我們曾經實現過一個簡單的注冊裝飾器,只是注冊了view函數,卻沒有URL映射,是遠遠不夠的。

在Flask中,注冊view函數需要一個裝飾器:

            
@app.route('/hello')
def hello():
  return 'Hello, World'
          

原理就是使用了裝飾器工廠,可以簡單的模擬一下實現:

            
class App:
  def __init__(self):
    self.view_functions = {}
  def route(self, rule):
    def deco(view_func):
      self.view_functions[rule] = view_func
      return view_func
    return deco
app = App()
@app.route('/')
def index():
  pass
@app.route('/hello')
def hello():
  pass
for rule, view in app.view_functions.items():
  print(rule, ':', view.__name__)
          

輸出:

/ : index /hello : hello

還可以使用裝飾器工廠來確定view函數可以允許哪些HTTP請求方法:

            
def action(methods):
  def deco(view):
    view.allow_methods = [method.lower() for method in methods]
    return view
  return deco
@action(['GET', 'POST'])
def view(request):
  if request.method.lower() in view.allow_methods:
    ...
          

重疊的裝飾器

裝飾器也是可以重疊使用的:

            
@d1
@d2
def foo():
  pass
          

等同于:

foo = d1(d2(foo))

類裝飾器

裝飾器的參數也可以是一個類,也就是說,裝飾器可以裝飾類:

            
import types
def deco(cls):
  for key, method in cls.__dict__.items():
    if isinstance(method, types.FunctionType):
      print(key, ':', method.__name__)
  return cls
@deco
class Test:
  def __init__(self):
    pass
  def foo(self):
    pass
          

總結

以上所述是小編給大家介紹的實例詳解Python裝飾器與閉包,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美aaa级| 国产精品久久久久久久久久 | 夜干夜干2017最新网站 | 99精品视频在线观看 | 日本三日本三级香港三级 | 欧美成年黄网站色视频 | 成人国产在线观看 | 美国一级片免费看 | 成人app色深夜福利 欧美电影一区 | 日韩精品视频在线观看免费 | 奇米在线播放 | 极品美女一区二区三区视频 | 久草资源 | 欧美videosex性欧美成人 | 你懂的91| 精品福利视频在线观看视频 | 黄色av网站免费看 | 九九精品视频在线 | 色网在线免费观看 | 国产在线精品一区 | 九九精品久久 | 久草小视频 | 一区二区三区免费在线观看 | 性开放的欧美大片按摩 | 北岛玲亚洲一区在线观看 | 欧美成人区| 欧美系列在线播放 | 日本视频免费高清一本18 | 成人一区二区三区 | 黄色片视频在线观看 | 三A级做爰片免费观看国产电影 | 亚洲一区二区三区四区精品 | 日本在线视 | 国产精品国产三级国产aⅴ 精品视频在线播放 | 日韩欧美不卡 | 五月婷六月婷婷 | 爱高潮www亚洲精品 亚洲精品免费网站 | 亚洲综合在线另类色区奇米 | 久久综合久久久久 | 国产一区二区三区在线观看免费 | 免费国产免费福利视频 |