成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

陷阱!python參數(shù)默認(rèn)值

Jason / 2216人閱讀

摘要:所有默認(rèn)參數(shù)值則存儲(chǔ)在函數(shù)對(duì)象的屬性中,它的值為一個(gè)列表,列表中每一個(gè)元素均為一個(gè)默認(rèn)參數(shù)的值。你可以定義一個(gè)對(duì)象作為占位符,如下面例子雖然應(yīng)該避免默認(rèn)參數(shù)值為可變對(duì)象,不過(guò)有時(shí)候使用可變對(duì)象作為默認(rèn)值會(huì)收到不錯(cuò)的效果。

原文地址

在stackoverflow上看到這樣一個(gè)程序:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

class demo_list:
    def __init__(self, l=[]):
        self.l = l

    def add(self, ele):
        self.l.append(ele)

def appender(ele):
    obj = demo_list()
    obj.add(ele)
    print obj.l

if __name__ == "__main__":
    for i in range(5):
        appender(i)

輸出結(jié)果是

[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]

有點(diǎn)奇怪,難道輸出不應(yīng)該是像下面這樣嗎?

[0]
[1]
[2]
[3]
[4]

其實(shí)想要得到上面的輸出,只需要將obj = intlist()替換為obj = intlist(l=[])。

默認(rèn)參數(shù)工作機(jī)制

上面怪異的輸出簡(jiǎn)單來(lái)說(shuō)是因?yàn)椋?/p>

Default values are computed once, then re-used.

因此每次調(diào)用__init__(),返回的是同一個(gè)list。為了驗(yàn)證這一點(diǎn),下面在__init__函數(shù)中添加一條語(yǔ)句,如下:

def __init__(self, l=[]):
    print id(l),
    self.l = l

輸出結(jié)果為:

4346933688 [0]
4346933688 [0, 1]
4346933688 [0, 1, 2]
4346933688 [0, 1, 2, 3]
4346933688 [0, 1, 2, 3, 4]

可以清晰看出每次調(diào)用__init__函數(shù)時(shí),默認(rèn)參數(shù)l都是同一個(gè)對(duì)象,其id為4346933688。

關(guān)于默認(rèn)參數(shù),文檔中是這樣說(shuō)的:

Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.

為了能夠更好地理解文檔內(nèi)容,再來(lái)看一個(gè)例子:

def a():
    print "a executed"
    return []

def b(x=a()):
    print "id(x): ", id(x)
    x.append(5)
    print "x: ", x

for i in range(2):
    print "-" * 15, "Call b()", "-" * 15
    b()
    print b.__defaults__
    print "id(b.__defaults__[0]): ", id(b.__defaults__[0])

for i in range(2):
    print "-" * 15, "Call b(list())", "-" * 15
    b(list())
    print b.__defaults__
    print "id(b.__defaults__[0]): ", id(b.__defaults__[0])

注意,當(dāng)python執(zhí)行def語(yǔ)句時(shí),它會(huì)根據(jù)編譯好的函數(shù)體字節(jié)碼和命名空間等信息新建一個(gè)函數(shù)對(duì)象,并且會(huì)計(jì)算默認(rèn)參數(shù)的值。函數(shù)的所有構(gòu)成要素均可通過(guò)它的屬性來(lái)訪(fǎng)問(wèn),比如可以用func_name屬性來(lái)查看函數(shù)的名稱(chēng)。所有默認(rèn)參數(shù)值則存儲(chǔ)在函數(shù)對(duì)象的__defaults__屬性中,它的值為一個(gè)列表,列表中每一個(gè)元素均為一個(gè)默認(rèn)參數(shù)的值。

好了,你應(yīng)該已經(jīng)知道上面程序的輸出內(nèi)容了吧,一個(gè)可能的輸出如下(id值可能為不同):

a executed
--------------- Call b() ---------------
id(x): 4316528512
x: [5]
([5],)
id(b.__defaults__[0]): 4316528512
--------------- Call b() ---------------
id(x): 4316528512
x: [5, 5]
([5, 5],)
id(b.__defaults__[0]): 4316528512
--------------- Call b(list()) ---------------
id(x): 4316684872
x: [5]
([5, 5],)
id(b.__defaults__[0]): 4316528512
--------------- Call b(list()) ---------------
id(x): 4316684944
x: [5]
([5, 5],)
id(b.__defaults__[0]): 4316528512

我們看到,在定義函數(shù)b(也就是執(zhí)行def語(yǔ)句)時(shí),已經(jīng)計(jì)算出默認(rèn)參數(shù)x的值,也就是執(zhí)行了a函數(shù),因此才會(huì)打印出a executed。之后,對(duì)b進(jìn)行了4次調(diào)用,下面簡(jiǎn)單分析一下:

第一次不提供默認(rèn)參數(shù)x的值進(jìn)行調(diào)用,此時(shí)使用函數(shù)b定義時(shí)計(jì)算出來(lái)的值作為x的值。所以id(x)和id(b.__defaults__[0])相等,x追加數(shù)字后,函數(shù)屬性中的默認(rèn)參數(shù)值也變?yōu)閇5];

第二次仍然沒(méi)有提供參數(shù)值,x的值為經(jīng)過(guò)第一次調(diào)用后的默認(rèn)參數(shù)值[5],然后對(duì)x進(jìn)行追加,同時(shí)也對(duì)函數(shù)屬性中的默認(rèn)參數(shù)值追加;

傳遞參數(shù)list()來(lái)調(diào)用b,此時(shí)新建一個(gè)列表作為x的值,所以id(x)不同于函數(shù)屬性中默認(rèn)參數(shù)的id值,追加5后x的值為[5];

再一次傳遞參數(shù)list()來(lái)調(diào)用b,仍然是新建列表作為x的值。

如果上面的內(nèi)容你已經(jīng)搞明白了,那么你可能會(huì)覺(jué)得默認(rèn)參數(shù)值的這種設(shè)計(jì)是python的設(shè)計(jì)缺陷,畢竟這也太不符合我們對(duì)默認(rèn)參數(shù)的認(rèn)知了。然而事實(shí)可能并非如此,更可能是因?yàn)椋?/p>

Functions in Python are first-class objects, and not only a piece of code.

我們可以這樣解讀:函數(shù)也是對(duì)象,因此定義的時(shí)候就被執(zhí)行,默認(rèn)參數(shù)是函數(shù)的屬性,它的值可能會(huì)隨著函數(shù)被調(diào)用而改變。其他對(duì)象不都是如此嗎?

可變對(duì)象作為參數(shù)默認(rèn)值?

參數(shù)的默認(rèn)值為可變對(duì)象時(shí),多次調(diào)用將返回同一個(gè)可變對(duì)象,更改對(duì)象值可能會(huì)造成意外結(jié)果。參數(shù)的默認(rèn)值為不可變對(duì)象時(shí),雖然多次調(diào)用返回同一個(gè)對(duì)象,但更改對(duì)象值并不會(huì)造成意外結(jié)果。

因此,在代碼中我們應(yīng)該避免將參數(shù)的默認(rèn)值設(shè)為可變對(duì)象,上面例子中的初始化函數(shù)可以更改如下:

def __init__(self, l=None):
       if not l:
            self.l = []
       else:
            self.l = l

在這里將None用作占位符來(lái)控制參數(shù)l的默認(rèn)值。不過(guò),有時(shí)候參數(shù)值可能是任意對(duì)象(包括None),這時(shí)候就不能將None作為占位符。你可以定義一個(gè)object對(duì)象作為占位符,如下面例子:

sentinel = object()

def func(var=sentinel):
   if var is sentinel:
        pass
   else:
        print var

雖然應(yīng)該避免默認(rèn)參數(shù)值為可變對(duì)象,不過(guò)有時(shí)候使用可變對(duì)象作為默認(rèn)值會(huì)收到不錯(cuò)的效果。比如我們可以用可變對(duì)象作為參數(shù)默認(rèn)值來(lái)統(tǒng)計(jì)函數(shù)調(diào)用次數(shù),下面例子中使用collections.Counter()作為參數(shù)的默認(rèn)值來(lái)統(tǒng)計(jì)斐波那契數(shù)列中每一個(gè)值計(jì)算的次數(shù)。

def fib_direct(n, count=collections.Counter()):
    assert n > 0, "invalid n"
    count[n] += 1
    if n < 3:
        return n
    else:
        return fib_direct(n - 1) + fib_direct(n - 2)

print fib_direct(10)
print fib_direct.__defaults__[0]

運(yùn)行結(jié)果如下:

89
Counter({2: 34, 1: 21, 3: 21, 4: 13, 5: 8, 6: 5, 7: 3, 8: 2, 9: 1, 10: 1})

我們還可以用默認(rèn)參數(shù)來(lái)做簡(jiǎn)單的緩存,仍然以斐波那契數(shù)列作為例子,如下:

def fib_direct(n, count=collections.Counter(), cache={}):
    assert n > 0, "invalid n"
    count[n] += 1
    if n in cache:
        return cache[n]
    if n < 3:
        value = n
    else:
        value = fib_direct(n - 1) + fib_direct(n - 2)
    cache[n] = value
    return value

print fib_direct(10)
print fib_direct.__defaults__[0]

結(jié)果為:

89
Counter({2: 2, 3: 2, 4: 2, 5: 2, 6: 2, 7: 2, 8: 2, 1: 1, 9: 1, 10: 1})

這樣就快了太多了,fib_direct(n)調(diào)用次數(shù)為o(n),這里也可以用裝飾器來(lái)實(shí)現(xiàn)計(jì)數(shù)和緩存功能。

參考
Python instances and attributes: is this a bug or i got it totally wrong?
Default Parameter Values in Python
“Least Astonishment” in Python: The Mutable Default Argument
A few things to remember while coding in Python
Using Python"s mutable default arguments for fun and profit

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37439.html

相關(guān)文章

  • 當(dāng)心Python函數(shù)可變默認(rèn)參數(shù)(list,set,dict…)的陷阱

    摘要:下面我們看看使用可變默認(rèn)參數(shù)時(shí)會(huì)出現(xiàn)什么莫名其妙的狀況。這就意味著如果你使用可變默認(rèn)參數(shù)并改變它,所有調(diào)用該函數(shù)的結(jié)果都是這個(gè)可變對(duì)象。同時(shí)也有注意,該函數(shù)要傳遞一個(gè)對(duì)象作為第二個(gè)參數(shù),因?yàn)楹瘮?shù)中對(duì)它進(jìn)行了操作。 showImg(https://segmentfault.com/img/bVbrFS3?w=762&h=505); 絕大多數(shù)情況下,Python是一個(gè)干凈具有一致性的語(yǔ)言。...

    Integ 評(píng)論0 收藏0
  • Proxy 的巧用

    摘要:為了保證的可讀性,本文采用意譯而非直譯。對(duì)象的所有用法,都是上面的這種形式。其中用來(lái)生成實(shí)例,是表示所要攔截的對(duì)象,是用來(lái)定制攔截行為的對(duì)象。雖然不同的創(chuàng)建模式支持類(lèi)似的功能,但無(wú)法用隱式初始值包裝對(duì)象。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! Proxy 介紹 使用Proxy,你可以將一只貓偽裝成一只老虎。下面大...

    feng409 評(píng)論0 收藏0
  • Proxy 的巧用

    摘要:為了保證的可讀性,本文采用意譯而非直譯。對(duì)象的所有用法,都是上面的這種形式。其中用來(lái)生成實(shí)例,是表示所要攔截的對(duì)象,是用來(lái)定制攔截行為的對(duì)象。雖然不同的創(chuàng)建模式支持類(lèi)似的功能,但無(wú)法用隱式初始值包裝對(duì)象。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! Proxy 介紹 使用Proxy,你可以將一只貓偽裝成一只老虎。下面大...

    FreeZinG 評(píng)論0 收藏0
  • Python編程規(guī)范筆記(上)

    摘要:編程規(guī)范筆記上寫(xiě)在前面從語(yǔ)言開(kāi)始,自己陸續(xù)學(xué)習(xí)了,但是自從研究生做畢設(shè)接觸以來(lái),就愛(ài)不釋手,再也沒(méi)有動(dòng)力嘗試其他語(yǔ)言。一與的一大優(yōu)勢(shì)就是具備優(yōu)秀的可讀性,而這基于一套較為完整的公認(rèn)編程規(guī)范。如原本希望的結(jié)果是,結(jié)果卻完全一樣。 Python編程規(guī)范筆記(上) 寫(xiě)在前面: 從C語(yǔ)言開(kāi)始,自己陸續(xù)學(xué)習(xí)了C++/Java,但是自從研究生做畢設(shè)接觸Python以來(lái),就愛(ài)不釋手,再也沒(méi)有動(dòng)力嘗試...

    Kross 評(píng)論0 收藏0
  • 使用Python和Java調(diào)用Shell腳本時(shí)的死鎖陷阱

    摘要:一般使用或者調(diào)用外部腳本需要注意的是,這里的方向是相對(duì)于主程序的,所以就是子進(jìn)程的輸出,而是子進(jìn)程的輸入?;谕瑯拥脑?,假如調(diào)用了方法等待子進(jìn)程執(zhí)行完畢而沒(méi)有及時(shí)處理輸出的話(huà),就會(huì)造成死鎖。 最近有一項(xiàng)需求,要定時(shí)判斷任務(wù)執(zhí)行條件是否滿(mǎn)足并觸發(fā) Spark 任務(wù),平時(shí)編寫(xiě) Spark 任務(wù)時(shí)都是封裝為一個(gè) Jar 包,然后采用 Shell 腳本形式傳入所需參數(shù)執(zhí)行,考慮到本次判斷條件...

    freewolf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<