返回函數(shù)
函數(shù)作為返回值。
def A(n):
def a():
return n
return a
調用A時,返回函數(shù)a,調用a時,返回值
>>> A(2)
<function A.<locals>.a at 0x1085fcea0>
>>> A(2)()
2
閉包
在函數(shù)里又定義了新函數(shù),內部函數(shù)可以引用外部函數(shù)的參數(shù)和局部變量,當外部函數(shù)返回內部函數(shù)時,相關參數(shù)和變量都存在返回的函數(shù)中
調用A()時,每次調用都會返回一個新的函數(shù),即使傳入相同的參數(shù)
>>> A(2)==A(2)
False
返回的函數(shù)并沒有立刻執(zhí)行,而是直到調用了fs()才執(zhí)行
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
#此時 調用count函數(shù), count()返回三個函數(shù)
>>> count
<function count at 0x108621048>
>>> count()
[<function count.<locals>.f at 0x1085f51e0>, <function count.<locals>.f at 0x108621158>, <function count.<locals>.f at 0x1086210d0>]
#看看調用每一個函數(shù)返回什么值
>>> [x() for x in count()]
[9, 9, 9]
都是9~返回一個函數(shù)時,牢記該函數(shù)并未執(zhí)行,返回函數(shù)中不要引用任何可能會變化的變量。
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執(zhí)行,因此i的當前值被傳入f()
return fs
>>> [x() for x in count()]
[1, 4, 9]
fs.append(f) f最后才被計算
fs.append(f(i)) f(i)立刻被執(zhí)行,因此i的當前值被傳入f()
匿名函數(shù)
lambda 函數(shù)參數(shù):函數(shù)式
匿名函數(shù)有個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果。幾個常見用法:
- 高階函數(shù)
>>> list(map(lambda x: x * x, [1, 2, 3]))
[1, 4, 9]
- 賦值給變量再通過變量調用
>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25
- 作為返回值返回
def build(x, y):
return lambda: x * x + y * y
裝飾器
在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)
decorator就是一個返回函數(shù)的高階函數(shù),接受一個函數(shù)作為參數(shù),并返回一個函數(shù)。
def log(func):
def wrapper():
print('call %s():' % func.__name__)
return func()
return wrapper
- 傳入一個函數(shù)func,返回一個函數(shù)wrapper, wrapper就是裝修后的func。網(wǎng)上有人討論說不需要兩層,一層就可以了,其實不行,因為一層會直接print后返回函數(shù)。我們要的是先拿到這個裝飾器,是個把print含在里面的函數(shù)。
- 在這個例子中,使用方法:
func=log(func)等價于@log加在 func()函數(shù)定義處前
這時func成了wrapper,這時調用func()時,返回print以及原func()
@log
def now():
print('2015-3-25')
#相當于
now = log(now)
要自定義那個"call:"可以再嵌套一層(外面那層是為了注入里層變量text)
def log(text):
def decorator(func):
def wrapper():
print('%s %s():' % (text, func.__name__))
return func()
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
#相當于
now = log('execute')(now)
但是經過裝飾后, func.__name__現(xiàn)在變成了 'wrapper'而不是'func'了,如下
>>> now.__name__
'wrapper'
因為now現(xiàn)在是log(now) 也就是wrapper
所以應該需要wrapper.__name__ = func.__name_這樣一個過程,或者交給functools.wraps
import functools
def log(func):
@functools.wraps(func)
def wrapper():
print('call %s():' % func.__name__)
return func()
return wrapper
練習
請編寫一個decorator,能在函數(shù)調用的前后打印出'begin call'和'end call'的日志。
import functools
def log(func):
@functools.wraps(func)
def wrapper():
print('begin call')
func()
print('end call')
return func()
return wrapper
@log
def now():
print(123)
>>> now()
begin call
123
end call
123
咋辦呢,難道我就return None?這樣豈不是破壞了原來的func函數(shù)
思考
關于能否寫出一個@log的decorator,使它既支持log又支持log()
Opera2952162625 created at 4-8 17:21, Last updated at 4-8 17:21
這個問題的本質是
對于@log而言,以now()函數(shù)為例,變成now=log(now)
對于@log(text)而言,變成now=log(text)(now)
如何使得在函數(shù)log()中能夠檢測到這種區(qū)別是解決問題的關鍵
評論區(qū)中有使用到callable來判斷@log(text)中的text是否為可調用的對象來區(qū)分此問題,顯然將問題的本質理解成了如下:
log()函數(shù)中傳入的是變量如果不是函數(shù)則使用的是@log(text),如果是函數(shù)則使用的是@log
顯然是不對的,如果@log(text)中參數(shù)text就是函數(shù)呢?
在函數(shù)里面去判斷(調用該函數(shù)的語句的執(zhí)行方案),我覺得應該是不能夠寫出來的
偏函數(shù)
偏函數(shù)就是改變一個函數(shù)的默認參數(shù)后作為一個新函數(shù)
functools.partial幫助我們快捷創(chuàng)建一個偏函數(shù)
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85