[toc]
第7章 函數(shù)
7.1 任意參數(shù)
*args **kwargs 使用
7.2 只能接受關(guān)鍵字參數(shù) (py3)
- 關(guān)鍵字 參數(shù)只能放在 位置參數(shù)的后面
def recv(maxsize, *, block):
'Receives a message'
pass
recv(1024, True) # TypeError
recv(1024, block=True) # Ok 在*后面的關(guān)鍵字的位置參數(shù)凭需,只能作為關(guān)鍵字參數(shù)才能起作用
# 相當(dāng)于強(qiáng)制啟用關(guān)鍵字參數(shù)矢棚,使recv()的調(diào)用更清晰
7.3 函數(shù)添加注解
-不推薦使用,用3引號(hào)的就可以了
def add(x:int, y:int) -> int:
return x + y
存儲(chǔ)于add.__annotations__
中
7.4 返回多個(gè)的函數(shù)
- 函數(shù)返回多個(gè)值時(shí)候殿衰,其實(shí)際返回的是一個(gè)元組
- ",,"以創(chuàng)建元組
7.5 默認(rèn)參數(shù)
- 1 默認(rèn)參為不可變對(duì)象
def apam (a,b=42):
return a,b
- 2 默認(rèn)參為可變對(duì)象
默認(rèn)參數(shù)是一個(gè)可修改的容器比如一個(gè)列表圾叼、集合或字典,可以使用 None作為默認(rèn)值
def spam(a, b=None):
if b is None:
b = []
原因看下面代碼蛤克,python的變量名稱為一個(gè)對(duì)象的指針,對(duì)象為可變對(duì)象的話夷蚊,內(nèi)部是可以變化的构挤。導(dǎo)致看上去你的默認(rèn)參數(shù)在變。
def func(x,y=[]):
y.append(x)
return x,y
print func(1)
print func(2)
object()返還一個(gè)對(duì)象惕鼓,None是一個(gè)對(duì)象筋现。
_no_value = object()
def spam(a, b=_no_value):
if b is _no_value:
print('No b value supplied')
spam(123,None)
spam(123)
幾點(diǎn)tips:
- 默認(rèn)參數(shù)的值僅僅在函數(shù)定義的時(shí)候賦值一次。
- 默認(rèn)參數(shù)不可箱歧,使用可變對(duì)象(原因看上文)
- 長度為 0 的字符串矾飞、列表、元組呀邢、字典等都會(huì)被當(dāng)做 False洒沦。
def spam(a, b=None):
if not b: # NO! Use 'b is None' instead
b = []
>>>spam(1)
>>>x = []
>>>spam(1,x) # Silent error. x value overwritten b
>>>spam(1,0) # Silent error. 0 ignored
>>>spam(1,'') # Silent error. '' ignored
object 是 python 中所有類的基類。
你可以創(chuàng)建 object 類的實(shí)例,但是這些實(shí)例沒什么實(shí)際用處,因?yàn)樗]有任何有用
的方法,也沒有哦任何實(shí)例數(shù)據(jù) (因?yàn)樗鼪]有任何的實(shí)例字典,你甚至都不能設(shè)置任何
屬性值)价淌。你唯一能做的就是測試同一性申眼。這個(gè)剛好符合我的要求,因?yàn)槲以诤瘮?shù)中就
只是需要一個(gè)同一性的測試而已瞒津。
7.6 匿名函數(shù)lambda
- 常用于排序時(shí)的表達(dá)式
- 惰性調(diào)用,存在
- 讓某個(gè)對(duì)象變成能callable
7.7 匿名函數(shù)捕獲變量值
x = 10
a = lambda y: x+y
x = 20
b = lambda y: x+y
a(10) #30
b(10) #30
這其中的奧妙在于 lambda 表達(dá)式中的 x 是一個(gè)自由變量,在運(yùn)行時(shí)綁定值,而不是定義時(shí)就綁定,這跟函數(shù)的默認(rèn)值參數(shù)定義是不同的括尸。
注意不正確的使用列表推倒
funcs = [lambda x: x+n for n in range(5)]
for f in funcs:
print f(0)
正確的寫成
funcs = [lambda x, n=n: x+n for n in range(5)]
原因解析:lambda 函數(shù)運(yùn)行時(shí)綁定內(nèi)部參數(shù)
funcs=[<lambda object0>,<lambda object1>,<lambda object2>...<lambda object4>]
7.8 減少可調(diào)用對(duì)象的參數(shù)個(gè)數(shù)
functools.partial() 函數(shù)允許你給一個(gè)或多個(gè)參數(shù)設(shè)置固定的值,減少接下來被調(diào)用時(shí)的參數(shù)個(gè)數(shù)仲智。
def spam(a, b, c, d):
print(a, b, c, d)
from functools import partial
s1 = partial(spam, 1) # a = 1
s1(2, 3, 4)
>>>1 2 3 4
partial() 固定某些參數(shù)并返回一個(gè)新的 callable 對(duì)象。這個(gè)新的 callable接受未賦值的參數(shù),然后跟之前已經(jīng)賦值過的參數(shù)合并起來,最后將所有參數(shù)傳遞給原始函數(shù)姻氨。
更進(jìn)一步,partial() 通常被用來微調(diào)其他庫函數(shù)所使用的回調(diào)函數(shù)的參數(shù)钓辆。
def output_result(result, log=None):
if log is not None:
log.debug('Got: %r', result)
# A sample function
def add(x, y):
return x + y
if __name__ == '__main__':
import logging
from multiprocessing import Pool
from functools import partial
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger("__file__")
p = Pool()
p.apply_async(add, (3, 4), callback=partial(output_result, log=log))
p.close() # 調(diào)用join之前,先調(diào)用close函數(shù)肴焊,否則會(huì)出錯(cuò)前联。
p.join() # 執(zhí)行完close后不會(huì)有新的進(jìn)程加入到pool,join函數(shù)等待所有子進(jìn)程結(jié)束
partial在類實(shí)例化的使用
class EchoHandler(StreamRequestHandler):
# ack is added keyword-only argument. *args, **kwargs are
# any normal parameters supplied (which are passed on)
def __init__(self, *args, ack, **kwargs):
self.ack = ack
super(EchoHandler,self).__init__(*args, **kwargs)
def handle(self):
for line in self.rfile:
self.wfile.write(self.ack + line)
from functools import partial
serv = TCPServer(('', 15000), partial(EchoHandler, ack=b'RECEIVED:'))
7.9 兩種保留函數(shù)外部環(huán)境變量的方法
- 1 為函數(shù)的閉包
def urltemplate(template):
def opener(**kwargs):
return urlopen(template.format_map(kwargs))
return opener
yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
- 2 為類的實(shí)現(xiàn)
from urllib.request import urlopen
class UrlTemplate:
def __init__(self, template):
self.template = template
def open(self, **kwargs):
return urlopen(self.template.format_map(kwargs))
$這其實(shí)也是兩類裝飾器的實(shí)現(xiàn)原理$
7.10 帶額外狀態(tài)信息的回調(diào)函數(shù)(*)
一般回調(diào)函數(shù)娶眷,載回調(diào)后似嗤,內(nèi)部的變量,會(huì)被內(nèi)存回收届宠。有時(shí)候烁落,需要回調(diào)函數(shù)狀態(tài)變量。
用人話:如有一個(gè)“內(nèi)部”變量保存回調(diào)函數(shù)的調(diào)用次數(shù)
我們先定義如下一個(gè)需要調(diào)用回調(diào)函數(shù)的函數(shù):
def apply_async(func, args, *, callback):
# Compute the result
result = func(*args)
# Invoke the callback with the result
callback(result)
使用這個(gè)函數(shù)
def print_result(result):
print('Got:', result)
def add(x, y):
return x + y
apply_async(add, (2, 3), callback=print_result) # Got: 5
apply_async(add, ('hello', 'world'), callback=print_result) # Got: helloworld
注意到 print result() 函數(shù)僅僅只接受一個(gè)參數(shù) result 豌注。不能再傳入其他信息伤塌。
而當(dāng)你想讓回調(diào)函數(shù)訪問其他變量或者特定環(huán)境的變量值的時(shí)候就會(huì)遇到麻煩。
- 1 類方法的實(shí)現(xiàn)
class ResultHandler:
def __init__(self):
self.sequence = 0
def handler(self, result):
self.sequence += 1
print('[{}] Got: {}'.format(self.sequence, result))
如何使用這個(gè)方法
r = ResultHandler()
apply_async(add, (2, 3), callback=r.handler)
#[1] Got: 5
apply_async(add, ('hello', 'world'), callback=r.handler)
#[2] Got: helloworld
- 2 閉包捕獲狀態(tài)值
def make_handler():
OneClass = type(repr('OneClass'), (object,), {"squence": 0}) #或者用字典
def handler(result):
#nonlocal sequence 這個(gè)nonlocal方法py2 沒有,用類屬性來傳遞變量
OneClass.squence += 1
print('[{}] Got: {}'.format(sequence, result))
return handler
如何使用這個(gè)方法
handler = make_handler()
apply_async(add, (2, 3), callback=handler)
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler)
# [2] Got: helloworld
總結(jié):函數(shù)內(nèi)部的變量轧铁,不會(huì)隨函數(shù)結(jié)束每聪,被回收。用兩種方法被外部函數(shù)所記錄齿风。其實(shí)和7.9節(jié)差不多
- 3 還有個(gè)更高級(jí)的辦法药薯,攜程
def make_handler():
sequence = 0
while True:
result = yield
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
如何使用這個(gè)方法
handler = make_handler() # 產(chǎn)生一個(gè)生成器
handler.next() # 或者 handler.send(None) 運(yùn)行里面的代碼,遇到y(tǒng)ield停下
apply_async(add, (2, 3), callback=handler.send)
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler.send)
# [2] Got: helloworld
7.11 內(nèi)聯(lián)回調(diào)函數(shù)(*)
def apply_async(func, args, callback):
# Compute the result
result = func(*args)
# Invoke the callback with the result
callback(result)
from Queue import Queue
from functools import wraps
def inlined_async(func):
@wraps(func)
def wrapper(*args):
f = func(*args)
result_queue = Queue()
result_queue.put(None)
while True:
result = result_queue.get()
try:
a = f.send(result)
apply_async(a.func, a.args, callback=result_queue.put)
except StopIteration:
break
return wrapper
def add(x, y):
return x + y
@inlined_async
def test():
r = yield Async(add, (2, 3))
print r
r = yield Async(add, ('hello', 'world'))
print r
for n in range(10):
r = yield Async(add, (n, n))
print r
print "Goodbye"
class Async:
def __init__(self, func, args):
self.func = func
self.args = args
if __name__ == '__main__':
import multiprocessing
pool = multiprocessing.Pool()
apply_async = pool.apply_async
# Run the test function
test()
只能明白基礎(chǔ)的流程救斑,同一個(gè)回調(diào)函數(shù)童本,一系列的任務(wù)函數(shù)。
7.12 訪問閉包中定義的變量
你想要擴(kuò)展函數(shù)中的某個(gè)閉包脸候,允許它能訪問和修改函數(shù)的內(nèi)部變量穷娱。
def sample():
N = type(repr('OneClass'), (object,), {"value": 0}) #用類屬性傳遞值
#N = {"value":0}
# Closure function
def func():
print'n=', N.value #N["value"]
# Accessor methods for n
def get_n():
return N.value
def set_n(x):
N.value = x
# Attach as function attributes
func.get_n = get_n
func.set_n = set_n
return func
f = sample()
f()
f.set_n(10)
f()
f.get_n()
在閉包的函數(shù)中定義,定義一些函數(shù),并將這些函數(shù)變?yōu)閮?nèi)部函數(shù)的屬性,來查看設(shè)置閉包函數(shù)的變量
同樣,也可以用來的實(shí)現(xiàn),內(nèi)部的函數(shù).
import sys
class ClosureInstance:
def __init__(self, locals=None):
if locals is None:
locals = sys._getframe(1).f_locals
# Update instance dictionary with callables
self.__dict__.update((key,value) for key, value in locals.items()
if callable(value) )
# Redirect special methods
def __len__(self):
return self.__dict__['__len__']()
# Example use
def Stack():
items = []
def push(item):
items.append(item)
def pop():
return items.pop()
def __len__():
return len(items)
return ClosureInstance()
-用閉包函數(shù)的外層函數(shù),記住內(nèi)部函數(shù)的變量,避免被回收.添加將定義的方法添加為屬性.用來在全局中使用.查看或者改變閉包內(nèi)的變量.