摘要:重寫內建名字空間中的函數閉包閉包是詞法閉包的簡稱。另一種說法認為閉包是由函數和與其相關的引用環(huán)境組合而成的實體。
Python 中的 Decorator(裝飾器) 是對一個函數或者方法的封裝,從而使其可以完成一些與自身功能無關的工作。
預備知識 一切皆對象在 Python 中,所有的一切都被視為對象,任何的變量、函數、類等都是 object 的子類。因此除了變量之外,函數和類等也可以被指向和傳遞。
>>> def foo(): ... pass ... >>> def Foo(): ... pass ... >>> v = foo >>> v命名空間>>> v = Foo >>> v
Python 通過提供 namespace 來實現重名函數/方法、變量等信息的識別。其大致可以分為三種 namespace,分別為:
local namespace: 局部空間,作用范圍為當前函數或者類方法
global namespace: 全局空間,作用范圍為當前模塊
build-in namespace: 內建空間,在 Python 解釋器啟動時就已經具有的命名空間,作用范圍為所有模塊。例如:abs,all,chr,cmp,int,str 等內建函數,它們在解釋器啟動時就被自動載入。
當函數/方法、變量等信息發(fā)生重名時,Python 會按照 local namespace -> global namespace -> build-in namespace 的順序搜索用戶所需元素,并且以第一個找到此元素的 namespace 為準。
>>> # 重寫內建名字空間中的 str 函數 >>> def str(obj): ... print "This is str" ... >>> str(1) This is str閉包
閉包(Closure)是詞法閉包(Lexical Closure)的簡稱。簡單地說,閉包就是根據不同的配置信息得到不同的結果。對閉包的具體定義有很多種說法,大致可以分為兩類:
一種說法認為閉包是符合一定條件的函數,比如一些參考資源中這樣定義閉包:閉包是在其詞法上下文中引用了自由變量的函數。
另一種說法認為閉包是由函數和與其相關的引用環(huán)境組合而成的實體。比如一些參考資源中這樣來定義:在實現深約束時,需要創(chuàng)建一個能顯式表示引用環(huán)境的東西,并將它與相關的子程序捆綁在一起,這樣捆綁起來的整體被稱為閉包。
以上兩種定義從某種意義上來說是對立的,一個認為閉包是函數,另一個認為閉包是函數和引用環(huán)境組成的整體。閉包確實可以認為就是函數,但第二種說法更確切些。閉包只是在形式和表現上像函數,但實際上不是函數。函數是一些可執(zhí)行的代碼,這些代碼在函數被定義后就確定了,不會在執(zhí)行時發(fā)生變化,所以一個函數只有一個實例。
閉包在運行時可以有多個實例,不同的引用環(huán)境和相同的函數組合可以產生不同的實例。所謂引用環(huán)境是指在程序執(zhí)行中的某個點所有處于活躍狀態(tài)的約束所組成的集合。其中的約束是指一個變量的名字和其所代表的對象之間的聯(lián)系。那么為什么要把引用環(huán)境與函數組合起來呢?這主要是因為在支持嵌套作用域的語言中,有時不能簡單直接地確定函數的引用環(huán)境。
在 Python 語言中,可以這樣簡單的理解閉包:一個閉包就是調用了一個函數 A,這個函數 A 返回了一個函數 B。這個返回的函數 B 就叫做閉包。在調用函數 A 的時候傳遞的參數就是對不同引用環(huán)境所做的配置。如下示例所示:
>>> def make_adder(addend): ... def adder(augend): ... return augend + addend ... return adder ... >>> add1 = make_adder(11) >>> add2 = make_adder(22) >>> add1(100) 111 >>> add2(100) 122函數式編程
函數式編程指使用一系列的函數解決問題。函數僅接受輸入并產生輸出,不包含任何能影響產生輸出的內部狀態(tài)。任何情況下,使用相同的參數調用函數始終能產生同樣的結果。
函數式編程就是一種抽象程度很高的編程范式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變量的程序設計語言,由于函數內部的變量狀態(tài)不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
可以認為函數式編程剛好站在了面向對象編程的對立面。對象通常包含內部狀態(tài)(字段),和許多能修改這些狀態(tài)的函數,程序則由不斷修改狀態(tài)構成;函數式編程則極力避免狀態(tài)改動,并通過在函數間傳遞數據流進行工作。但這并不是說無法同時使用函數式編程和面向對象編程,事實上,復雜的系統(tǒng)一般會采用面向對象技術建模,但混合使用函數式風格也能體現函數式風格的優(yōu)點。
高階函數高階函數即能接受函數作為參數的函數。因為在 Python 中一切皆對象,變量可以指向函數,函數名其實也是指向函數的變量。也就是說,我們可以將函數賦給其他變量,也就可以將函數作為參數傳遞給其他函數。
>>> def add(x, y, f): ... return f(x) + f(y) ... >>> add(6, -9, abs) 15裝飾器(decorator)
Python 裝飾器的作用就是為已經存在的對象添加額外的功能。例如裝飾器可以用來 引入日志、添加計時邏輯來檢測性能、給函數加入事務處理 等等。其實總體說起來,裝飾器也就是一個函數,一個用來包裝函數的函數。裝飾器在函數申明完成的時候被調用,調用之后申明的函數被換成一個被裝飾器裝飾過后的函數。簡單說,本質上,裝飾器就是一個返回函數的高階函數,也是一個閉包。
裝飾器的語法以 @ 開頭,接著是裝飾器要裝飾的函數的申明。
無參裝飾器先來看一下裝飾器本身沒有參數的情況。例如,我們想要知道一個函數被調用時所花的時間,可以采用如下的方式實現:
# Author: Huoty # Time: 2015-08-12 10:37:10 import time def foo(): print "in foo()" # 定義一個計時器,傳入一個函數,并返回另一個附加了計時功能的方法 def timeit(func): # 定義一個內嵌的包裝函數,給傳入的函數加上計時功能的包裝 def wrapper(): start = time.clock() func() end = time.clock() print "used:", end - start # 將包裝后的函數返回 return wrapper # Script starts from here foo = timeit(foo) foo()
Python 實現裝飾器的目的就是為了讓程序更加簡潔,上邊的代碼可以繼續(xù)用裝飾器來簡化:
# Author: Huoty # Time: 2015-08-12 10:37:10 import time def timeit(func): def wrapper(): start = time.clock() func() end =time.clock() print "used:", end - start return wrapper @timeit def foo(): print "in foo()" # Script starts from here foo()
由上例可以看出,裝飾器 @timeit 的作用等價與 foo = timeit(foo)。被裝飾器裝飾后的執(zhí)行結果取決于裝飾函數的是想,如果裝飾函數返回被裝飾函數本身,就等于沒有裝飾,如果裝飾函數對被裝飾函數進行了包裝,并返回包裝后的函數,那調用函數時執(zhí)行的就是包裝過的函數。
如果被裝飾的函數帶有參數,則在裝飾器中也應該為包裝函數提供對應的參數。如果被裝飾的函數參數不確定,則可以用如下方式實現:
# Author: Huoty # Time: 2015-08-12 10:59:11 import time def log(func): def wrapper(*args, **kw): print "call %s." % func.__name__ return func(*args, **kw) return wrapper @log def now(): print time.asctime() # Script starts from here now()帶參數裝飾器
裝飾器本身也可以帶參數,但是通常對參數會有一定的要求。由于有參數的裝飾器函數在調用時只會使用應用時的參數,而不接收被裝飾的函數做為參數,所以必須在其內部再創(chuàng)建一個函數。如下示例所示:
# Author: Huoty # Time: 2015-08-12 11:13:30 def deco(arg): def _deco(func): def __deco(): print("before %s called [%s]." % (func.__name__, arg)) func() print(" after %s called [%s]." % (func.__name__, arg)) return __deco return _deco @deco("module") def foo(): print(" foo() called.") @deco("module2") def hoo(): print(" hoo() called.") # Script starts from here foo() hoo()
上例中的第一個函數 deco 是裝飾器函數,它的參數是用來加強 加強裝飾 的。由于此函數并非被裝飾的函數對象,所以在內部必須至少創(chuàng)建一個接受被裝飾函數的函數,然后返回這個對象(實際上此時等效于 foo=decomaker(arg)(foo))。
如果裝飾器和被裝飾函數都帶參數,則用如下實現是形式:
def deco(pm): def _deco(func): def __deco(*args, **kw): ret =func(*args, **kw) print "func result: ", ret return ret ** pm return __deco return _deco @deco(2) def foo(x, y, z=1): return x + y + z print "deco_func result: %s" % foo(10, 20)
輸出:
func result: 31 deco_func result: 961類裝飾器
裝飾器不經可以用來裝飾函數,還可以用來裝飾類。例如給類添加一個類方法:
>>> def bar(obj): ... print type(obj) ... >>> def inject(cls): ... cls.bar = bar ... return cls ... >>> @inject ... class Foo(object): ... pass ... >>> foo = Foo() >>> foo.bar()內置裝飾器
內置的裝飾器有三個,分別是 staticmethod、classmethod 和 property,作用分別是把類中定義的實例方法變成靜態(tài)方法、類方法和類屬性。由于模塊里可以定義函數,所以靜態(tài)方法和類方法的用處并不是太多,除非你想要完全的面向對象編程。這三個裝飾器的實現都涉及到 描述符 的概念。
Functools 模塊Python的functools模塊主要功能是對函數進行包裝,增加原有函數的功能,起主要內容包括:cmp_to_key, partial, reduce, total_ordering, update_wrapper, wraps。
函數也是一個對象,它有__name__等屬性。以上我們有一個 callin.py 的例子,我們用裝飾器裝飾之后的 now 函數,當我們用 now.__name__ 查看時,發(fā)現它的 __name__ 已經從原來的"now"變成了"wrapper"。因為返回的那個wrapper()函數名字就是"wrapper",所以,需要把原始函數的name等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執(zhí)行就會出錯。當然,我們可以用wrapper.__name__ = func.__name__來實現,但是我們不必這么麻煩,用 Python 內置的 functools.wraps 便可實現這樣的功能:
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print("%s %s():" % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator用類實現裝飾器
除了可以用函數來實現裝飾器外,類也可以實現。Python 類有一個 __call__ 方法,它能夠讓對象可調用。因此可以用該特性來實現裝飾器。例如實現一個磁盤緩存:
import os import uuid import glob import pickle class DiskCache(object): _NAMESPACE = uuid.UUID("c875fb30-a8a8-402d-a796-225a6b065cad") def __init__(self, func): self._func = func self.__name__ = func.__name__ self.__module__ = func.__module__ self.__doc__ = func.__doc__ self.cache_path = "/tmp/.diskcache" def __call__(self, *args, **kw): params_uuid = uuid.uuid5(self._NAMESPACE, "-".join(map(str, (args, kw)))) key = "{}-{}.cache".format(self._func.__name__, str(params_uuid)) cache_file = os.path.join(self.cache_path, key) if not os.path.exists(self.cache_path): os.makedirs(self.cache_path) try: with open(cache_file, "rb") as f: val = pickle.load(f) except: val = self._func(*args, **kw) try: with open(cache_file, "wb") as f: pickle.dump(val, f) except: pass return val def clear_cache(self): for cache_file in glob.iglob("{}/{}-*".format(self.cache_path, self.__name__)): os.remove(cache_file) @DiskCache def add(x, y): print "add: %s + %s" % (x, y) return x, y
輸出:
add.clear_cache() print add(1, 2) print add(2, 3) print add(1, 2) print "cached files:", os.listdir(add.cache_path) add.clear_cache() print "cached files:", os.listdir(add.cache_path)
本質上,內置的 property 裝飾器也是一個類,只不過它是一個描述符。可以用類似的形式實現一個可緩存的 property 裝飾器:
class CachedProperty(object): def __init__(self, func, name=None, doc=None): self.__name__ = name or func.__name__ self.__module__ = func.__module__ self.__doc__ = doc or func.__doc__ self.func = func def __get__(self, obj, type=None): if obj is None: return self value = obj.__dict__.get(self.__name__) if value is None: value = self.func(obj) obj.__dict__[self.__name__] = value return value class Foo(object): @CachedProperty def foo(self): print "first calculate" result = "this is result" return result f = Foo() print f.foo print f.foo
輸出:
first calculate this is result this is result多重裝飾
一個函數可以同時被多個裝飾器裝飾。裝飾的初始化在函數定義時完成,初始化順序為離函數定義最近的裝飾器首先被初始化,最遠的則最后被初始化,初始化只進行一次。而裝飾器的執(zhí)行順序則跟初始化順序相反。
def decorator_a(func): print "decorator_a" def wrapper(*args, **kw): print "call %s in decorator_a" % func.__name__ return func() return wrapper def decorator_b(func): print "decorator_b" def wrapper(*args, **kw): print "call %s in decorator_b" % func.__name__ return func() return wrapper @decorator_a @decorator_b def foo(): print "foo" print "-"*10 foo() foo()
輸出:
decorator_b decorator_a ---------- call wrapper in decorator_a call foo in decorator_b foo call wrapper in decorator_a call foo in decorator_b foo
有以上示例可以看出,離函數定義最近的 decorator_b 裝飾器首先被初始化,在執(zhí)行時則是里函數定義最遠的 decorator_a 首先被執(zhí)行。
參考資料http://developer.51cto.com/ar...
http://www.cnblogs.com/ma6174...
http://www.cnblogs.com/huxi/a...
http://www.cnblogs.com/rollen...
http://blog.csdn.net/mdl13412...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43088.html
摘要:列表越界的列表類似于動態(tài)數組,沒有長度的限制。比如對將要傳進內層函數的參數進行檢測等,從而實現對參數的類型進行限制。對二維列表的每一維列表進行長度限制,不足指定長度,自動補充指定元素。 前言 作為一名python的腦殘粉,請先跟我念一遍python大法好。 其作為動態(tài)語言的靈活,簡介的代碼,確實在某些情況下確實比其他編程語言要好。但你有沒有想過,有時這些靈活的語法,可能會造成一些糟糕的...
摘要:函數內省的內容到此結束。函數式編程并不是一個函數式編程語言,但通過和等包的支持,也可以寫出函數式風格的代碼。 《流暢的Python》筆記。本篇主要講述Python中函數的進階內容。包括函數和對象的關系,函數內省,Python中的函數式編程。 1. 前言 本片首先介紹函數和對象的關系;隨后介紹函數和可調用對象的關系,以及函數內省。函數內省這部分會涉及很多與IDE和框架相關的東西,如果平時...
摘要:前言繼續(xù)向下看廖大教程,看到了函數式編程這一節(jié),當時是覺得沒啥用直接跳過了,這次準備要仔細看一遍了,并記錄下一些心得。 前言 繼續(xù)向下看廖大教程,看到了函數式編程這一節(jié),當時是覺得沒啥用直接跳過了,這次準備要仔細看一遍了,并記錄下一些心得。 函數式編程 上學期有上一門叫 人工智能 的課,老師強行要我們學了一個叫做 prolog 的語言,哇那感覺確實難受,思維方式完全和之前學過的不一樣,...
摘要:初步認識裝飾器函數裝飾器用于在源代碼中標記函數,以某種方式增強函數的行為。函數裝飾器在導入模塊時立即執(zhí)行,而被裝飾的函數只在明確調用時運行。只有涉及嵌套函數時才有閉包問題。如果想保留函數原本的屬性,可以使用標準庫中的裝飾器。 《流暢的Python》筆記本篇將從最簡單的裝飾器開始,逐漸深入到閉包的概念,然后實現參數化裝飾器,最后介紹標準庫中常用的裝飾器。 1. 初步認識裝飾器 函數裝飾...
摘要:操作函數的函數叫做高階函數。這一節(jié)展示了高階函數可用作強大的抽象機制,極大提升語言的表現力。新的環(huán)境特性高階函數。這是因為局部函數的函數體的求值環(huán)境擴展于定義處的求值環(huán)境。這種命名慣例并不由解釋器強制,只是函數名稱的一部分。 1.6 高階函數 來源:1.6 Higher-Order Functions 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 我們已經看到,函數實際上是...
閱讀 894·2021-09-03 10:42
閱讀 1511·2019-08-30 15:56
閱讀 1445·2019-08-29 17:27
閱讀 870·2019-08-29 15:25
閱讀 3157·2019-08-26 18:27
閱讀 2480·2019-08-26 13:41
閱讀 1888·2019-08-26 10:39
閱讀 1570·2019-08-23 18:36