Python裝飾器

1. 函數(shù)

def foo():
    return 1

foo()

2. 作用域

全局作用域與本地作用域

a = "a global variable"

def foo():
    print locals()

print globals()
# {'a': 'a global variable', ...}

foo() # 2
# {}

內(nèi)置函數(shù) globals 返回一個包含所有 Python 解釋器知道的變量名稱的字典忘巧。

在#2調(diào)用了函數(shù)并把內(nèi)部本地作用域里面的內(nèi)容打印出來栋齿。函數(shù)foo有自己獨立的命名空間玛歌。

3. 變量解析規(guī)則

在 Python 的作用域規(guī)則里,創(chuàng)建變量一定會在當(dāng)前作用域里創(chuàng)建一個變量逆济,但是訪問或者修改變量時會先在當(dāng)前作用域查找變量,如果沒有找到匹配的變量會依次向上在閉合的作用域里進行查找。

a = "a global variable"

def foo():
    print a # 1

foo()
# a global variable

在#1處科吭,Python 解釋器會嘗試查找變量 a,在函數(shù)本地作用域是找不到的猴鲫,所以接著會去上層作用域里查找对人。

如果在函數(shù)foo里對全局變量賦值,結(jié)果卻與想的不一樣:

a = "a global variable"

def foo():
    a = "test" # 1
    print locals()

foo()
# {'a': 'test'}
a
# 'a global variable'

全局變量可以在函數(shù)內(nèi)部被訪問到拂共,但賦值不行牺弄。在#1處,實際創(chuàng)建了一個局部變量宜狐,同名的全局變量無法被訪問到势告。

4. 變量生存周期

變量不僅生存在一個個命名空間內(nèi),他們都有自己的生存周期抚恒。

def foo():
    x = 1

foo()
print x #1
# Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
# NameError: name 'x' is not defined

1處發(fā)生的錯誤不僅僅是因為作用域規(guī)則導(dǎo)致的咱台。它還和Python以及其他很多編程語言中函數(shù)調(diào)用實現(xiàn)的機制有關(guān)。在這個地方這個執(zhí)行時間點變量x不存在俭驮。函數(shù)foo的命名空間隨著函數(shù)調(diào)用開始而開始回溺,結(jié)束而銷毀。

5. 函數(shù)參數(shù)

參數(shù)會變成本地變量存在于函數(shù)內(nèi)部混萝。

def foo(x):
    print locals()

foo(1)
# {'x': 1}
def foo(x, y=0): # 1
    return x - y

foo(3, 1) # 2

2
foo(3) # 3
3

foo() # 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes at least 1 argument (0 given)

foo(y=1, x=3) # 5
2

6. 嵌套函數(shù)

Pyhton 允許創(chuàng)建嵌套函數(shù)遗遵。作用域和變量生存周期依舊適用。

def outer():
    x = 1
    print locals()
    def inner():
        print x # 1
        print locals()
    inner() # 2

outer()
1

7. 函數(shù)是 Python 里的一級類對象

Python 里的函數(shù)與其他類型一樣都是對象譬圣。

issubclass(int, object) # Python 中所有對象均繼承自 object
True

def foo():
    pass

foo.__class__
<type 'function'>

issubclass(foo.__class__, object)
True

將函數(shù)作為參數(shù)傳遞:

def add(x, y):
    return x + y

def sub(x, y):
    return x - y

def apply(func, x, y): # 1
    return func(x, y)  # 2

apply(add, 2, 1) # 3
3

apply(sub, 2, 1)
1

將函數(shù)作為返回值傳遞:

def outer():
    def inner():
        print "Inside inner"
    return inner

foo = outer()
foo
<function inner at 0x>
foo()
Inside inner

8. 閉包

def outer():
    x = 1
    def inner():
        print x # 1
    return inner

foo = outer()
foo.func_closure
(<cell at 0x1007397f8: int object at 0x...)

Python支持一個叫做函數(shù)閉包的特性瓮恭,用人話來講就是,嵌套定義在非全局作用域里面的函數(shù)能夠記住它在被定義的時候它所處的封閉命名空間厘熟。這能夠通過查看函數(shù)的func_closure屬性得出結(jié)論屯蹦,這個屬性里面包含封閉作用域里面的值(只會包含被捕捉到的值,比如x绳姨,如果在outer里面還定義了其他的值登澜,封閉作用域里面是不會有的)

9. 裝飾器

裝飾器其實就是一個閉包。

def outer(some_func):
    def inner():
        print "before some_func"
        ret = some_func() # 1
        return ret + 1
    return inner
def foo():
    return 1
decorated = outer(foo) # 2
decorated()
before some_func
2

接下來寫一個有用的裝飾器:

class Coordinate(object):
     def __init__(self, x, y):
         self.x = x
         self.y = y
     def __repr__(self):
         return "Coord: " + str(self.__dict__)
def add(a, b):
     return Coordinate(a.x + b.x, a.y + b.y)
def sub(a, b):
     return Coordinate(a.x - b.x, a.y - b.y)
one = Coordinate(100, 200)
two = Coordinate(300, 200)
add(one, two)
Coord: {'y': 400, 'x': 400}

如果加減函數(shù)需要一些邊界檢查的行為怎么辦飘庄?

現(xiàn)在期望是這樣:

one = Coordinate(100, 200)
two = Coordinate(300, 200)
three = Coordinate(-100, -100)
sub(one, two)
Coord: {'y': 0, 'x': -200}
add(one, three)
Coord: {'y': 100, 'x': 0}

邊界檢查的裝飾器:

def wrapper(func):
     def checker(a, b): # 1
         if a.x < 0 or a.y < 0:
             a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
         if b.x < 0 or b.y < 0:
             b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
         ret = func(a, b)
         if ret.x < 0 or ret.y < 0:
             ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
         return ret
     return checker
add = wrapper(add)
sub = wrapper(sub)
sub(one, two)
Coord: {'y': 0, 'x': 0}
add(one, three)
Coord: {'y': 200, 'x': 100}

10. 使用 @ 標識符將裝飾器應(yīng)用到函數(shù)

Python 2.4 支持使用標識符 @ 將裝飾器應(yīng)用在函數(shù)上脑蠕。

以前的寫法:

add = wrapper(add)

使用@:

@wrapper
def add(a, b):
    return Coordinate(a.x + b.x, a.y + b.y)

使用 @ 與先前簡單的用包裝方法替代原有方法是一模一樣的。只是用了一個語法糖讓裝飾的行為更優(yōu)雅。

11. *args**kwargs

arg 和 kwargs 并不是 Python 語法的一部分谴仙,但在定義函數(shù)的時候迂求,使用這樣的變量名算是一個不成文的約定。

def one(*args):
    print args # 1

one()
()

one(1, 2, 3)
(1, 2, 3)

def two(x, y, *args): # 2
    print x, y, args

two('a', 'b', 'c')
a b ('c',)
def foo(**kwargs):
     print kwargs
foo()
{}
foo(x=1, y=2)
{'y': 2, 'x': 1}
dct = {'x': 1, 'y': 2}
def bar(x, y):
     return x + y
bar(**dct)
3

12. 更通用的裝飾器

def logger(func):
     def inner(*args, **kwargs): #1
         print "Arguments were: %s, %s" % (args, kwargs)
         return func(*args, **kwargs) #2
     return inner

@logger
def foo1(x, y=1):
    return x * y

@logger
def foo2():
    return 2

foo1(5, 4)
Arguments were: (5, 4), {}
20

foo1(1)
Arguments were: (1,), {}
1

foo2()
Arguments were: (), {}
2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晃跺,一起剝皮案震驚了整個濱河市揩局,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掀虎,老刑警劉巖凌盯,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異烹玉,居然都是意外死亡驰怎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門二打,熙熙樓的掌柜王于貴愁眉苦臉地迎上來县忌,“玉大人,你說我怎么就攤上這事址儒∏奂希” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵莲趣,是天一觀的道長鸳慈。 經(jīng)常有香客問我,道長喧伞,這世上最難降的妖魔是什么走芋? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮潘鲫,結(jié)果婚禮上翁逞,老公的妹妹穿的比我還像新娘。我一直安慰自己溉仑,他們只是感情好挖函,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浊竟,像睡著了一般怨喘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上振定,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天必怜,我揣著相機與錄音,去河邊找鬼后频。 笑死梳庆,一個胖子當(dāng)著我的面吹牛暖途,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膏执,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼驻售,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了胧后?” 一聲冷哼從身側(cè)響起芋浮,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壳快,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镇草,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡眶痰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了梯啤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竖伯。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖因宇,靈堂內(nèi)的尸體忽然破棺而出七婴,到底是詐尸還是另有隱情,我是刑警寧澤察滑,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布打厘,位于F島的核電站,受9級特大地震影響贺辰,放射性物質(zhì)發(fā)生泄漏户盯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一饲化、第九天 我趴在偏房一處隱蔽的房頂上張望莽鸭。 院中可真熱鬧,春花似錦吃靠、人聲如沸硫眨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽礁阁。三九已至,卻和暖如春夕冲,著一層夾襖步出監(jiān)牢的瞬間氮兵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工歹鱼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泣栈,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像南片,于是被迫代替她去往敵國和親掺涛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 呵呵疼进!作為一名教python的老師薪缆,我發(fā)現(xiàn)學(xué)生們基本上一開始很難搞定python的裝飾器,也許因為裝飾器確實很難懂...
    TypingQuietly閱讀 19,541評論 26 186
  • 原文出處: dzone 譯文出處:Wu Cheng(@nullRef) 1. 函數(shù) 在python中伞广,函數(shù)通過...
    DraculaWong閱讀 516評論 0 3
  • Python的裝飾器的英文名叫Decorator拣帽,要對一個已有的模塊做一些“修飾工作”,所謂修飾工作就是想給現(xiàn)有的...
    Spareribs閱讀 674評論 1 11
  • 每個人都有的內(nèi)褲主要功能是用來遮羞嚼锄,但是到了冬天它沒法為我們防風(fēng)御寒减拭,咋辦?我們想到的一個辦法就是把內(nèi)褲改造一下区丑,...
    chen_000閱讀 1,360評論 0 3
  • 看到一則小新聞:http://news.jstv.com/a/20161015/1476523590712.sht...
    水田夏木閱讀 261評論 0 0