Python - 默认参数的一次性求值

    和很多高级编程语言一样,Python也有默认参数,当默认参数是数值类型时,一切都很美好:

>>> def function(a, b = 1000000):
	b +=a
	return b
   如果你喜欢,你可以在一段代码中无数次的调用这个函数,只要你参数一样,结果应该都一样。比如:

function(1)总是会返回1000001。但是默认参数是其他类型(如列表)时就没那么美好了:

>>> def function(a, b = []):
	b.append(a)
	print(b)


     这时你如果在一段代码中持续的调用该函数,将会发生或许令人意外的情况:第一次调用function(1)的时候,很正常,会打印出[1],但是第二次再调用function(1),将会打印出[1,1]。这是为什么呢?不要紧,使用Python我们有办法检查一下是哪里出了毛病。这里我们可以在每一次调用函数的时候打印出b的ID。Python中一个对象的ID在其生命周期中是唯一的,和其他高级语言中所说的对象的地址一样。如果第二段代码中的b对象其ID一样,说明两次调用都使用的同一个对象,换句话说,Python函数对默认参数的求值操作在其生命周期中只发生一次(第一次)。可以使用以下的代码测试我们的想法:

def function1(a,b=100000):
    b+=a
    print("b = {0} with the id of {1}".format(b,id(b)))
def function2(a,b=[]):
    b.append(a)
    print("b = {0} with the id of {1}".format(b,id(b)))
    
def test():
    function1(1)
    function1(1)
    function2(1)
    function2(1)
    
if __name__ == '__main__':
    test()

    得到的输出如下:

b = 100001 with the id of 33384304
b = 100001 with the id of 33384304
b = [1] with the id of 33341848
b = [1, 1] with the id of 33341848

      果然,从后面两条结果中可以看到列表b在两次调用时都是使用的同一个对象,看来之前的猜想是正确的。对非数值类型的默认参数,只会在第一次调用时进行求值(取地址)操作。后面的所有调用都发生在同一个位置的对象上。只有字符串类型不受此限制,因为string本身是不可变的(immutable)的,每一次修改它都会创建一个新的对象。

      Python的这个小陷阱和它的灵活性是分不开的,在其他的强类型语言如C#中,类似Python的情况是不会发生的,C#4.0严格将引用类型的默认参数值限定为Null(除了String类型),否则会在编译时报错:

cs4namedarg     那么在Python中有办法使得每一次函数调用时都会使用最初设定的默认值么?办法有两种(有其他的办法欢迎在留言中告诉我),要么把默认值设为一个不可变(immutable)的值,比如string或者None,要么就每次调用的时候保留最初的默认值,并赋给调用函数。

     第一种方法很简单,在此不再赘述,不过需要注意以字符串为默认值时,如果频繁的调用函数可能会导致性能问题,因为每一次发生在该默认值上的操作,会创建一个新的string对象。对于第二种办法,可以考虑用Python的装饰器(decorator)实现,下面的代码演示了一个每一次调用都保存默认参数的装饰器:

def keepDefault(f):
    defArgs = f.__defaults__
    def keeper(*args,**kwArgs):
        f.__defaults__ = deepcopy(defArgs)
        return f(*args,**kwArgs)
    keeper.__name__ = f.__name__
    return keeper

然后我们将该装饰器应用到之前定义的function2中:

@keepDefault
def function2(a,b=[]):
    b.append(a)
    print("b = {0} with the id of {1}".format(b,id(b)))

然后我们像先前一样连续的调用function2,结果输出如下:

b = [1] with the id of 33892912
b = [1] with the id of 33892592

哈~ 我们如愿得到了结果。而且注意这里两次b对象的的ID不一样,这是因为每一次调用时,函数的参数都被deepcopy完整的克隆一遍。重新构造了新对象b。

Enjoy python!   ;-)

posted @ 2010-03-07 15:49 Freesc Huang 阅读(418) 评论(0) 编辑
<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

公告

These postings are provided "AS IS" with no warranties
and confer no rights.


Locations of visitors to this page

Blog Keywords
Embedded System,Visual Studio,.Net Framework,.Net Compact Framework,.Net Micro Framework,Windows Mobile,Windows Embedded CE,Emulator,WCF,CLR,Design & Pattern,C/C++,C#,Matlab,Algorithms
昵称:Freesc Huang
园龄:4年11个月
粉丝:32
关注:4

统计

  • 随笔 - 197
  • 文章 - 0
  • 评论 - 840

搜索

 

随笔分类(227)

随笔档案(197)

Blogs

Link

积分与排名

最新评论

阅读排行榜

评论排行榜

推荐排行榜