是否有更“pythonic”的方式来编写这个闭包?

时间:2017-01-30 08:39:51

标签: python closures

在JavaScript中,我可能会写一个这样的函数:

function counter() {
    foo = 0;
    function increment() {
    foo += 1
    console.log(foo);
  }
  function printVal() {
    console.log(foo);
  }

  return {
                increment: increment,
          printVal: printVal,
                  }    
}

func = counter();

func.increment()
func.increment()
func.printVal()

我想尝试使用python实现类似的功能。特别是我如何通过外部函数的返回来访问两个内部函数。

这是python中一个工作但很有趣的版本:

def counter():
    foo = {'bar': 0}
    def increment():
        foo['bar'] += 1
        return foo['bar']
    def printVal():
        return foo['bar']
    return {'increment': increment, 'printVal': printVal}

func = counter()
func['increment']()
func['printVal']()

是否有某种更优雅或“pythonic”的方式来编写这样的闭包?

3 个答案:

答案 0 :(得分:3)

Python在闭包中没有其他语言那么强大。首先,它只支持读取“闭合”变量,其次,return语句使它更加尴尬。

另一方面,它对类非常简洁,所以如果你想要一个有两个函数的数据成员,我会用一个类来做:

class Counter:
    def __init__(self, c=0):
        self.count = c
    def increment(self):
        self.count += 1
    def printVal(self):
        return self.count


c = Counter()
c.increment()
print(c.printVal())

我敢说,在这种情况下,这将是pythonic方式。

修改

在看到关于跟踪函数调用的评论后,我添加了这个。你可以通过一个闭包来实现它:

# this is the tracked function
def add2(a, b):
    return a + b

# this is the count tracker
def counterize(func):
    c = [0]
    def counter_func(*args, **kw):
        c[0] += 1
        counter_func.count = c[0]
        return func(*args, **kw)
    return counter_func


cadd2 = counterize(add2)

print(cadd2(1, 2))
print(cadd2(3, 4))
print('Called %s times' % cadd2.count)

>>>
3
7
Called 2 times

但这不是Python中的惯用语。将count保留在函数对象中是一个很好的技巧,但这就是它的本质。一个恶作剧。另一方面,在LISP或Scala中,闭包会更自然,但是,我认为不可能将count保留为字段,而是将其与结果一起返回。 / p>

我想说的是,在这种情况下,再次,惯用的Python代码将通过类,它是更容易理解的IMO,并且具有相同的代码长度:

class Counterize:
    def __init__(self, func):
        self.func = func
        self.count = 0
    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.func(*args, **kwargs)


cadd2 = Counterize(add2)
print(cadd2(1, 2))
print(cadd2(3, 4))
print('Called %s times' % cadd2.count)

输出结果是一样的。 __call__的目的是允许通过括号调用将对象视为函数。

答案 1 :(得分:1)

只需实施__getitem__

即可
class Foo:
    class_attributes = {'a': 3,
                        'b': 5}

    def __getitem__(self, name):
        return Foo.class_attributes[name]


f = Foo()
print f['a']

输出:

3

答案 2 :(得分:0)

我认为您想要达到的目标类似于以下示例:

def makeInc(x):
  def inc(y):
     # x is "attached" in the definition of inc
     return y + x

  return inc

incOne = makeInc(1)
incFive = makeInc(5)

incOne (5) # returns 6
incFive(5) # returns 10

您需要创建闭包的pythonic方式。所以上面的例子演示了如何做到这一点。