2013年1月5日 星期六

[程式語言] 淺談Python的預設參數

這篇文章是要討論python的預設參數


def fun1(a=0):
        print a

def fun2(a=[]):
        a.append(1)
        print a
        print 'address: ', id(a)

class DefaultObject:
        def __init__(self):
                print 'call me'

def fun3(a=DefaultObject()):
        print 'address: ', id(a)


print '--------fun1------------'
fun1()
fun1()
print '--------fun2------------'
fun2()
fun2()
print '--------fun3------------'
fun3()
fun3()


這段code執行結果如下

call me
--------fun1------------
0
0
--------fun2------------
[1]
address:  42163576
[1, 1]
address:  42163576
--------fun3------------
address:  42209688
address:  42209688

第一行居然先出現 call me。恩...先不理他。

fun1的執行結果,符合我們的預期。

fun2的執行結果,第二次結果怪怪的
我不是先把a指派成[],怎麼第二次會出現兩個1呢?
第一次執行和第二次執行a物件所指向的東西,居然是同一個
恩....難怪第二次執行會出現兩個1 ...

接下來看fun3,fun3預設參數是個物件,物件初始化時會印出call me。
可是我呼叫兩次,卻只有一個call me。而且那個call me發生在 fun1 前。
恩 ... 。 很詭異,你仔細看fun3 的輸出結果,你會發現他們的 a 是指向同個物件
難怪 DefaultObject 的建構子只會被呼叫一遍 ...

結論,你只能在python的預設參數放Immutable object。除非你明確知道你在做什麼。

下面附上C++ 和 ruby 對 預設參數的行為

文字測試

#include 
class DefaultObject{
public:
 DefaultObject(){
  printf("Call me, %p\n", this);
 }
};

int test_obj(DefaultObject obj = DefaultObject()){
}

int main(){
 DefaultObject obj;
 test_obj(obj);
 test_obj();
 test_obj();
}

Call me, 0028FEFD
Call me, 0028FEFE
Call me, 0028FEFF
輸出結果如我們預期,建立三個defaultObject,而且他們位址都不一樣。表示不同物件。第一個物件是13行建立的,第二個物件是15行建立的,第三個物件是第16行建立的。沒有像Pyhon會有共用物件的情形。
接下來來看ruby吧

class DefaultObj
    def initialize()
        puts('call me')
    end
end
def fun(a=DefaultObj.new())
        puts a
end

def fun1(a=[])
    a.push(1)
    p a
    return a
end

fun()
fun()

o1 = fun1()
o1.push(2)
o2 = fun1()
o2.push(3)

p o1
p o2

call me
#
call me
#
[1]
[1]
[1, 2]
[1, 3]



ruby建構物件時要呼叫new(),所以不容易搞混。fun1的測試情形,ruby符合每次呼叫fun1時,都是給a一個新的空陣列。所以不會發生共用物件的情形。這個行為也比較符合我們的預期。

沒有留言:

張貼留言