【问题标题】:Is there a more 'pythonic' way to write this closure?有没有更“pythonic”的方式来写这个闭包?
【发布时间】:2017-01-30 08:39:51
【问题描述】:

在 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”的方式来编写这样的闭包?

【问题讨论】:

  • 在这种情况下,您可能想要使用collections.Counter 对象,或者只是编写一个类。
  • 你首先应该问自己为什么要在 JS 中使用闭包:闭包通常是 classlike 行为的替代品。在 python 的情况下,您确实可以使用 class 来模拟相应的行为。你的两个函数变成了真正的方法。

标签: python closures


【解决方案1】:

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())

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

编辑

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

# 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 保留为字段,而是将其与结果一起返回。

我想说的是,在这种情况下,惯用的 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__ 的目的是允许通过括号调用的方式将对象视为函数。

【讨论】:

  • 谢谢,我希望使用闭包,因为我正在努力学习更多功能技术,但这看起来是迄今为止最好的解决方案。我想做的更大的事情是有一个计数器,我可以用它来跟踪一个函数被调用了多少次。
【解决方案2】:

只需实现__getitem__

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

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


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

输出:

3

【讨论】:

  • 这里的 Foo 的所有实例都使用相同的 class_attributes 吗?
  • 在这个例子中它会。可以通过实现构造函数来定义每个实例的 class_attributes。
【解决方案3】:

我认为您想要实现的类似于以下示例:

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

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

【讨论】:

    猜你喜欢
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多