代码改变世界

Python tricks(2) -- method默认参数和闭包closure

2014-01-23 00:24  会被淹死的鱼  阅读(627)  评论(0编辑  收藏  举报

Python的method可以设置默认参数, 默认参数如果是可变的类型, 比如list, map等, 将会影响所有的该方法调用. 

下面是一个简单的例子

def f(a=None, l=[]):
    if not a:
        return l
    l.append(a)
    return l

if __name__ == "__main__":
    print f("a")
    print f("b")
    print f("b")
    print f(l=[])
    print f()

输出结果如下:

['a']
['a', 'b']
['a', 'b', 'b']
[]
['a', 'b', 'b']

我们可以看到f的默认参数l在所有的方法调用中都受到影响了, 这个可能并不一定符合默认参数设置的初衷. 

想要避免这种影响, 可以这样做.

def f(a=None, l=None):
    if not l:
        l = []
    if not a:
        return l
    l.append(a)
    return l

if __name__ == "__main__":
    print f("a")
    print f("b")
    print f("b")
    print f(l=[])
    print f()

将l设置为None, 在方法内部判断, 并初始化为空, 这样改变后的输出为:

['a']
['b']
['b']
[]
[]

具体可以查阅官方文档的说明[参考1].

下面再来说说闭包.

我个人理解, 闭包的形式是方法的嵌套, 主要的作用是为了在整个调用过程中保存方法的某些状态. 有些时候method的默认参数也可以做到保存方法状态.

用dict保存一些变量信息, 类似context的东西.

闭包实现:

def context():
    data = {}

    def _context(key, value=None):
        if value:
            data[key] = value
            return data
        else:
            return data.get(key, None), data

    return _context

c = context()
print c("key")
print c("key", "value")
print c("key")

方法默认参数的实现

def d(key, value=None, data={}):
    if value:
        data[key] = value
        return data
    else:
        return data.get(key, None), data

print d("key")
print d("key", "value")
print d("key")

通过实现类的magic method来实现

class e(object):
    def __init__(self):
        self.data = {}

    def __call__(self, key, value=None):
        if value:
            self.data[key] = value
            return self.data
        else:
            return self.data.get(key, None), self.data

f = e()
print f("key")
print f("key", "value")
print f("key")

三种实现的输出结果都是一样的

(None, {})
{'key': 'value'}
('value', {'key': 'value'})

在python中方法本身也是一个对象, 可以有自己的属性等, 通过第三种实现, 我们可以理解默认参数是方法对象的一个属性, 伴随其生命周期.

使用def func()定义的时候就是定义了一个方法的类, 之后获取了一个名为func的实例化对象.

 

可能有人会奇怪, 这种东西哪里有使用的场景了? 我能想到的一个场景就是, 我想通过查找字典的方式获取一个信息, 这个字典是一个大文件(加载耗时), 我只希望加载一次, 这个时候闭包可以发挥其特殊的作用, 而且代码也比较优雅.

 

本文是笔者对于python方法的一些理解. 水平有限, 欢迎拍砖!

 

参考文档:

  1. Python Docs: http://docs.python.org/2/tutorial/controlflow.html#default-argument-values