Python 的世界里没有代码块
以前经常写 ruby 和 js, 对于以下的代码非常习惯:
item = {id: 1, name: 'Donald Clinton'}
attrs = [:id, :name]
attr_getters = attrs.map do |attr|
lambda do
"The #{attr} is #{item[attr]}"
end
end
> attr_getters[0][]
"The id is 1"
> attr_getters[1][]
"The name is Donald Clinton"
var item = {id: 1, name: 'Donald Clinton'};
var attrs = ['id', 'name'];
var attr_getters = attrs.map(function (attr) {
return function() {
var value_with_name = 'The ' + attr + ' is ' + item[attr];
return value_with_name;
};
});
> attr_getters[0]()
'The id is 1'
> attr_getters[1]()
'The name is Donald Clinton'
最近的工作开始要写 python, 于是我想当然的也这么写了....
item = {'id': 1, 'name': 'Donald Clinton'}
attrs = ['id', 'name']
attr_getters = []
for attr in attrs:
getter = lambda : 'The {0} is {1}'.format(attr, item[attr])
attr_getters.append(getter)
可是并不 work!
>>> attr_getters[0]()
'The name is Donald Clinton'
>>> attr_getters[1]()
'The name is Donald Clinton'
仔细观察了一下代码... 这 for ... in ...:
虽然看起来很清真, 可是他并不是代码块. 我这傻乎乎的 lambda 的闭包始终绑定着 for 循环所在的环境, 于是当循环完成后, attr
就定格在了 'name'
上.
于是一个蠢呼呼的 fix 是在 lambda 外面再包一个
item = {'id': 1, 'name': 'Donald Clinton'}
attrs = ['id', 'name']
attr_getters = []
for attr in attrs:
getter = (lambda a: lambda : 'The {0} is {1}'.format(a, item[a]))(attr)
attr_getters.append(getter)
鉴于这很蠢...于是代码只能再改一下
item = {'id': 1, 'name': 'Donald Clinton'}
attrs = ['id', 'name']
def build_getter(attr):
def getter():
return 'The {0} is {1}'.format(attr, item[attr])
return getter
attr_getters = map(build_getter, attrs)
lambda, 闭包等等, 这些东西在 python 世界里并不是一等公民, 随手定义函数的编程习惯在 python 中基本是完全不适用的. 单行 lambda 更是残废, 基本上想做点事情都必须预先定义一个函数才行, 没法定义匿名 block.
PS: 在 python 的世界里非常注重 callable 的. 小到一个函数, 大到一个类, 甚至一个实例, 只要定义了 __call__
方法, 他就是 callable 的.
class A(object)
def __init__(self, ...):
...
map(A, some_list_of_init_params) # => list of A