python中的习语:封闭vs仿函数vs对象

时间:2015-12-16 16:19:13

标签: python

所以我对更有经验的python程序员对以下样式问题的看法感到好奇。假设我正在构建一个函数,该函数将通过pandas数据框或任何类似的用例逐行迭代,其中函数需要访问其先前的状态。似乎至少有四种方法可以在python中实现它:

1)关闭:

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以获取其值。不完全优雅,但我认为重点往往是隐藏范围,所以它应该很难达到。另一方面,与OOP语言相比,当几乎所有其他方式都有私有变量时,python使封闭函数变为私有,这也感觉有点奇怪。

2)Functor

def outer():
    def inner(current_state):
        #do something
        inner.previous_state=current_state
        return something
    ret = inner
    ret.previous_state=None
    return ret

这"打开关闭"现在,封闭状态作为函数的属性完全可见。这是有效的,因为函数实际上只是伪装的对象。我倾向于这是最pythonic。它清晰,简洁,易读。

3)物体 这可能是OOP程序员最熟悉的

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这样的完全OOP语言中,这很好,你可以使用接口等来管理它,但是在一个鸭式语言中,有许多简单的类只是为了携带一个需要一些状态的函数,这似乎有点奇怪。 / p>

4)全局 - 我不会证明这一点,因为我特别想避免污染全局名称空间

5)装饰器 - 这是一个有点曲线球,但你可以使用装饰器来存储部分状态信息。

@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()就像仿函数示例一样。我其实真的很讨厌这种方式。在我看来,有一个非常不透明的语法,因为很多时候调用内部(current_state)会给你相同的结果,或者每次都会给你一个新装饰的函数,所以这似乎是不好的做法制作以这种方式向函数添加状态的装饰器。

那么哪种方法正确?我错过了哪些优点和缺点?

2 个答案:

答案 0 :(得分:5)

所以对此的正确答案是可调用对象,它基本上取代了python中闭包的习语。

所以解决上面的选项3变化:

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

class Calculator(Object) :
    def __init__(self):
        self.previous_state=None

    def __call__(self, current_state) :
        #do_something
        self.previous_state = current_state
        return something

现在您可以将其称为函数。所以

func = Calculator():
for x in list:
    func(x)

答案 1 :(得分:3)

您可以定义生成器,它是协同处理的受限形式。

def make_gen():
    previous_state = None
    for row in rows:
        # do something
        previous_state = current_state
        yield something

thing = make_gen()
for item in thing:
    # Each iteration, item is a different value
    # "returned" by the yield statement in the generator

不是反复调用thing(它替换你的内部函数),而是迭代它(这与重复调用next(thing)基本相同)。

状态完全包含在发电机的主体内。

如果您不想实际迭代它,您仍然可以通过明确调用next来选择性地“重新进入”协同进程。

thing = make_gen()
first_item = next(thing)
# do some stuff
second_item = next(thing)
# do more stuff
third_item = next(thing)
fourth_item = next(thing)
# etc