日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python 的闭包和装饰器

發(fā)布時間:2025/3/21 python 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 的闭包和装饰器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

翻譯:?TheLover_Z

Part I

原文地址:?http://blaag.haard.se/Python-Closures-and-Decorators–Pt–1/

回想起來,當初我做出了錯誤的選擇,把 Python 的課程削減到了4個小時以至于把裝飾器的部分搞砸了,我答應大家我稍后會對閉包和裝飾器做一個更好的解說 —— 我是這么打算的。

函數(shù)也是對象。實際上,在 Python 中函數(shù)是一級對象——也就是說,他們可以像其他對象一樣使用而沒有什么特別的限制。這給了我們一些有趣的選擇,我會由淺到深解釋這個問題。

關(guān)于函數(shù)就是對象的一個最常見的例子就是 C 中的函數(shù)指針;將函數(shù)傳遞到其他的將要使用它的函數(shù)。為了說明這一點,我們來看看一個重復函數(shù)的實現(xiàn) —— 也就是,一個函數(shù)接受另外一個函數(shù)以及一個數(shù)字當作參數(shù),并且重復調(diào)用指定函數(shù)指定次數(shù):

>>> #A very simple function >>> def greeter(): … print("Hello") >>> #An implementation of a repeat function >>> def repeat(fn, times): … for i in range(times): … fn() >>> repeat(greeter, 3) Hello Hello Hello >>>

這種模式在很多情況下都有用 —— 比如向一個排序算法傳遞比較函數(shù),向一個語法分析器傳遞一個裝飾器函數(shù),通常情況下這些做法可以使一個函數(shù)的行為?更專一化?,或者向已經(jīng)抽象了工作流的函數(shù)傳遞一個待辦的特定部分(比如,?sort()?知道怎么排序,?compare()?知道怎么比較元素)。

函數(shù)也可以在其他函數(shù)的內(nèi)部聲明,這給了我們另一個很重要的工具。在一般情況下,這可以用來隱藏實用函數(shù)的實現(xiàn)細節(jié):

>>> def print_integers(values): … def is_integer(value): … try: … return value == int(value) … except: … return False … for v in values: … if is_integer(v): … print(v) >>> print_integers([1,2,3,"4", "parrot", 3.14]) 1 2 3

這可能是有用的,但它本身并不算是個強大的工具。相比函數(shù)可以當作參數(shù)被傳遞而言,我們可以將它們包裝(wrap)在另外的函數(shù)中,從而向已經(jīng)構(gòu)建好的函數(shù)增加新的行為。一個簡單的例子是向一個函數(shù)增加跟蹤輸出:

>>> def print_call(fn): … def fn_wrap(*args, **args): #take any arguments … print ("Calling %s" % (fn.func_name)) … return fn(*args, **kwargs) #pass any arguments to fn() … return fn_wrap >>> greeter = print_call(greeter) #wrap greeter >>> repeat(greeter, 3) Calling fn_wrap Hello Calling fn_wrap Hello Calling fn_wrap Hello >>> >>> greeter.func_name 'fn_wrap'

正如你看到的那樣,我們可以使用帶日志的函數(shù)來替換掉現(xiàn)有函數(shù)相應的部分,然后調(diào)用原來的函數(shù)。在例子的最后兩行,函數(shù)的名字已經(jīng)反映出了它已經(jīng)被改變,這個改變可能是我們想要的,也可能不是。如果我們想包裝一個函數(shù)同時保持它原來的名字,我們可以增加一行?print_call?函數(shù),代碼如下:

>>> def print_call(fn): … def fn_wrap(*args, **kwargs): #take any arguments … print("Calling %s" % (fn.func_name)) … return fn(*args, **kwargs) #pass any arguments to fn() … fn_wrap.func_name = fn.func_name #Copy the original name … return fn_wrap

因為這是一個很長的話題,我明天會來更新第二部分,我們會講講閉包,偏函數(shù)(partial),還有(終于到它了)裝飾器。

至此,如果這些你之前全部沒有接觸過,可以先用?print_call?函數(shù)作為基礎,來創(chuàng)建一個能夠在正常調(diào)用函數(shù)之前先打印出這個函數(shù)名字的一個修飾器。

Part II

原文地址:?http://blaag.haard.se/Python-Closures-and-Decorators–Pt–2/

在第一部分中,我們學習了以函數(shù)作為參數(shù)調(diào)用其他的函數(shù),還有嵌套函數(shù),最終我們把一個函數(shù)包裝在另外的函數(shù)中。我們先把第一部分的答案給出:

>>> def print_call(fn): … def fn_wrap(*args, **kwargs): … print("Calling %s with arguments: \n\targs: %s\n\tkwargs:%s" %fn.__name__, args, kwargs)) … retval = fn(*args, **kwargs) … print("%s returning '%s'" % (fn.func_name, retval)) … return retval … fn_wrap.func_name = fn.func_name … return fn_wrap >>> def greeter(greeting, what='world'): … return "%s %s!" % (greeting, what) >>> greeter = print_call(greeter) >>> greeter("Hi") Calling greeter with arguments: args: ('Hi',) kwargs:{} greeter returning 'Hi world!' 'Hi world!' >>> greeter("Hi", what="Python") Calling greeter with arguments: args: ('Hi',) kwargs:{'what': 'Python'} greeter returning 'Hi Python!' 'Hi Python!' >>>

這稍微有那么點兒用了,但它可以變的更好!你可能聽說過或者沒有聽說過*閉包*,你可能聽說過成千上萬種閉包定義中的某一種或者某幾種 —— 我不會那么挑剔,我只是說閉包就是一個捕捉了(或者關(guān)閉)非本地變量(自由變量)的代碼塊(比如一個函數(shù))。如果你不清楚我在說什么,你可能需要進修一下 CS 的相關(guān)課程,但是不要擔心 —— 我會給你演示例子。閉包的概念很簡單:一個可以引用在函數(shù)閉合范圍內(nèi)變量的函數(shù)。

比如說,看一下這個代碼:

>>> a = 0 >>> def get_a(): … return a >>> get_a() 0 >>> a = 3 >>> get_a() 3

正如你看到的那樣,?get_a?函數(shù)可以取得?a?的值,并且可以讀取更新后的值。然而這里有一個限制 —— 被捕獲的變量(captured variable,下同)不能被寫入。

>>> def set_a(val): … a = val >>> set_a(4) >>> a 3

為什么會這樣?由于閉包不能寫入任何被捕獲的變量,?a?=?val?這個語句實際上寫入了本地變量?a?從而隱藏了模塊級別的?a?,這正是我們想寫入的內(nèi)容。為了解決這個限制(也許這并不是一個好主意),我們可以用一個容器類型:

>>> class A(object): pass >>> a = A() >>> a.value = 1 >>> def set_a(val): … a.value = val >>> a.value 1 >>> set_a(5) >>> a.value 5

因此,我們已經(jīng)知道了函數(shù)從它的閉合范圍內(nèi)捕捉變量,我們最終可以接觸到有趣的東西了,我們先實現(xiàn)一個偏函數(shù)(partial,下同)。一個偏函數(shù)是一個你已經(jīng)填充了部分或者全部參數(shù)的函數(shù)的實例;比如說你有一個存儲了用戶名和密碼的會話,和一個查詢后端的函數(shù),這個函數(shù)有不同的參數(shù)但是*總是*需要身份驗證。與其說每次都手動傳遞身份驗證信息,我們可以用偏函數(shù)來預填充那些信息。

>>> #Our 'backend' function … def get_stuff(user, pw, stuff_id): … """Here we would presumably fetch data using the … credentials and id""" … print("get_stuff called with user: %s, pw: %s, stuff_id: %s" % (user, pw, stuff_id)) >>> def partial(fn, *args, **kwargs): … def fn_part(*fn_args, **fn_kwargs): … kwargs.update(fn_kwargs) … return fn(*args + fn_args, **kwargs) … return fn_part >>> my_stuff = partial(get_stuff, 'myuser', 'mypwd') >>> my_stuff(3) get_stuff called with user: myuser, pw: mypwd, stuff_id: 3 >>> my_stuff(67) get_stuff called with user: myuser, pw: mypwd, stuff_id: 67

偏函數(shù)可以用在許多地方來消除代碼的重復。當然,你沒有必要自己手動實現(xiàn)它,只需要?from?functools?import?partial?就可以了。

最后,我們來看看函數(shù)裝飾器(未來可能有類裝飾器)。函數(shù)裝飾器接收一個函數(shù)作為參數(shù)然后返回一個新的函數(shù)。聽起來很熟悉吧?我們已經(jīng)實現(xiàn)過一個?print_call?裝飾器了。

>>> @print_call … def will_be_logged(arg): … return arg*5 >>> will_be_logged("!") Calling will_be_logged with arguments: args: ('!',) kwargs:{} will_be_logged returning '!!!!!' '!!!!!'

使用@符號標記是一個很方便的方法。

>>> def will_be_logged(arg): … return arg*5 >>> will_be_logged = print_call(will_be_logged)

但是如果我們想要確定裝飾器的參數(shù)呢?在這種情況下,作為裝飾器的函數(shù)會接收參數(shù),并且返回一個包裝(wrap)了裝飾器函數(shù)的函數(shù)。

>>> def require(role): … def wrapper(fn): … def new_fn(*args, **kwargs): … if not role in kwargs.get('roles', []): … print("%s not in %s" % (role, kwargs.get('roles', []))) … raise Exception("Unauthorized") … return fn(*args, **kwargs) … return new_fn … return wrapper >>> @require('admin') … def get_users(**kwargs): … return ('Alice', 'Bob') >>> get_users() admin not in [] Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 7, in new_fn Exception: Unauthorized >>> get_users(roles=['user', 'editor']) admin not in ['user', 'editor'] Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 7, in new_fn Exception: Unauthorized >>> get_users(roles=['user', 'admin']) ('Alice', 'Bob')

就是這樣。你現(xiàn)在會寫裝飾器了,也許你會用這些知識去寫面向方面(aspect-oriented)的編程。加入?@cache,?@trace,?@throttle?都是微不足道的(在你添加?@cache?之前,一定要檢查?functools?,如果你用的是 Python 3 的話!)


from:?http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue3/python-closures-and-decorators.html

總結(jié)

以上是生活随笔為你收集整理的Python 的闭包和装饰器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。