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