pythoncookbook 第7章 函數(shù)

[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)的變量.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纪他,隨后出現(xiàn)的幾起案子鄙煤,更是在濱河造成了極大的恐慌,老刑警劉巖茶袒,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梯刚,死亡現(xiàn)場離奇詭異,居然都是意外死亡薪寓,警方通過查閱死者的電腦和手機(jī)亡资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門澜共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锥腻,你說我怎么就攤上這事嗦董。” “怎么了瘦黑?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵京革,是天一觀的道長。 經(jīng)常有香客問我幸斥,道長匹摇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任甲葬,我火速辦了婚禮廊勃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘经窖。我一直安慰自己坡垫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布画侣。 她就那樣靜靜地躺著冰悠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棉钧。 梳的紋絲不亂的頭發(fā)上屿脐,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音宪卿,去河邊找鬼。 笑死万栅,一個(gè)胖子當(dāng)著我的面吹牛佑钾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烦粒,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼休溶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扰她?” 一聲冷哼從身側(cè)響起兽掰,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎徒役,沒想到半個(gè)月后孽尽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忧勿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年杉女,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞻讽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡熏挎,死狀恐怖速勇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坎拐,我是刑警寧澤烦磁,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站哼勇,受9級(jí)特大地震影響都伪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猴蹂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一院溺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧磅轻,春花似錦珍逸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撮躁,卻和暖如春漱病,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背把曼。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工杨帽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗤军。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓注盈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叙赚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子老客,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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