我对更有经验的Python程序员在以下样式问题上的看法很好奇。假设我正在构建一个函数,该函数将逐行迭代遍历pandas数据帧或任何类似的用例,其中函数需要访问其先前的状态。在Python中似乎有至少四种实现方式:
- 闭包:
def outer():
previous_state = None
def inner(current_state) :
nonlocal previous_state
#do something
previous_state=current_state
return something
如果您来自JavaScript背景,这对您来说无疑是很自然的。在Python中也感觉非常自然,直到您需要访问封闭作用域时,您将会做类似以下操作:inner.__code__.co_freevars
,它将以元组的形式给出封闭变量的名称,并找到您想要的一个的索引,接着转到inner.__closure__[index].cell_contents
以获取其值。并不是特别优雅,但我想重点通常是隐藏范围,因此难以访问是有道理的。另一方面,当Python放弃了与OOP语言相比几乎所有其他创建私有变量的方法时,它也感觉有点奇怪,却使封闭函数变成了私有。
- 函子
def outer():
def inner(current_state):
#do something
inner.previous_state=current_state
return something
ret = inner
ret.previous_state=None
return ret
这样做“打开了闭包”,因为现在封闭状态完全可见,作为函数的属性。这起作用是因为函数只是伪装成对象。我倾向于它是最Pythonic的方式。它清晰、简洁、易读。
- 对象
这对面向对象编程程序员来说可能最为熟悉。
class Calculator(Object) :
def __init__(self):
self.previous_state=None
def do_something(self, current_state) :
#do_something
self.previous_state = current_state
return something
这里最大的缺点是你往往会得到很多类定义。在完全面向对象的语言比如 Java 中这没问题,因为你可以使用接口等方式来管理它,但在一个鸭子类型的语言中,为了携带一个需要一些状态的函数而写许多简单的类似乎有点奇怪。
全局变量 - 我不会演示这个,因为我特意想避免污染全局命名空间。
装饰器 - 这有点出人意料,但你可以使用装饰器来存储部分状态信息。
@outer
def inner(previous_state, current_state):
#do something
return something
def outer(inner) :
def wrapper(current_state) :
result = inner(wrapper.previous_state, current_state)
wrapper.previous_state = current_state
return result
ret = wrapper
ret.previous_state=None
return result
这种语法对我来说是最不熟悉的,但如果现在我调用
func = inner
实际上,我理解
func = outer(inner)
然后,反复调用func()
就像函数对象的例子一样。其实我非常讨厌这种方式。在我看来,它的语法非常不透明,因为无法确定多次调用inner(current_state)是否会给你相同的结果,或者它是否每次都会给你一个新的装饰函数,因此使用这种方式为函数添加状态的装饰器似乎是不好的做法。
那么正确的方式是什么?我漏掉了哪些优缺点?
inner.__closure__
访问闭包变量;因为只有当你已经在内部函数中主动使用它们时,这些名称才会出现在该结构中。__closure__
结构实际上是一个内部实现细节。 - Martijn Pieters@
漂亮语法的情况下使用装饰器。您不希望使用nonlocal
或global
关键字。而 functor 通常被留给 closure。 - Cyrbil