Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/343.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 函数闭包与可调用类_Python_Performance_Python 3.x_Closures - Fatal编程技术网

Python 函数闭包与可调用类

Python 函数闭包与可调用类,python,performance,python-3.x,closures,Python,Performance,Python 3.x,Closures,在许多情况下,有两种实现选择:闭包和可调用类。比如说, class F: def __init__(self, op): self.op = op def __call__(self, arg1, arg2): if (self.op == 'mult'): return arg1 * arg2 if (self.op == 'add'): return arg1 + arg2 raise InvalidOp(op) f = F(

在许多情况下,有两种实现选择:闭包和可调用类。比如说,

class F:
  def __init__(self, op):
    self.op = op
  def __call__(self, arg1, arg2):
    if (self.op == 'mult'):
      return arg1 * arg2
    if (self.op == 'add'):
      return arg1 + arg2
    raise InvalidOp(op)

f = F('add')

在选择哪个方向时,应该考虑什么因素?< /P> 我可以想到两个:

  • 似乎闭包总是有更好的性能(不能 想一个反例)

  • 我认为有些情况下,关闭无法完成工作(例如,如果 其状态随时间而变化)


我说的对吗?还有什么可以补充的?

我认为类方法更容易理解一眼,因此,更可维护。由于这是优秀Python代码的前提之一,我认为在所有条件相同的情况下,最好使用类而不是嵌套函数。在这种情况下,Python的灵活性使得该语言违反了Python编码的“应该有一种,最好只有一种明显的方式”谓词

任何一方的性能差异都应该可以忽略不计——如果您的代码在这个级别上的性能很重要,那么您当然应该对其进行分析并优化相关部分,可能会将一些代码重写为本机代码


但是,如果存在使用状态变量的紧密循环,那么评估闭包变量应该比评估类属性稍微快一些。当然,在进入循环之前,只需在类方法中插入一行,如
op=self.op
,就可以克服这一问题,使循环中的变量访问成为局部变量-这将避免对每次访问进行属性查找和获取。同样,性能差异应该是可以忽略的,如果您需要这一点点的额外性能,并且使用Python编码,那么您会遇到更严重的问题。

闭包更快。类更灵活(也就是说,比仅仅调用更多的方法可用)。

我将用以下内容重新编写
示例:

class F(object):
    __slots__ = ('__call__')
    def __init__(self, op):
        if op == 'mult':
            self.__call__ = lambda a, b: a * b
        elif op == 'add':
            self.__call__ = lambda a, b: a + b
        else:
            raise InvalidOp(op)
在我使用Python3.2.2的机器上,这将提供0.40usec/pass(函数0.31,因此速度降低29%)。在不使用
对象
作为基类的情况下,它提供0.65 usec/次(即比基于
对象
的速度慢55%)。由于某种原因,在
\uuuuu call\uuuuu
中检查
op
的代码给出的结果几乎与在
\uuuuu init\uuuuu
中执行的结果相同。以
对象
为基础,在
内检查调用
给出0.61 usec/次

使用类的原因可能是多态性

class UserFunctions(object):
    __slots__ = ('__call__')
    def __init__(self, name):
        f = getattr(self, '_func_' + name, None)
        if f is None: raise InvalidOp(name)
        else: self.__call__ = f

class MyOps(UserFunctions):
    @classmethod
    def _func_mult(cls, a, b): return a * b
    @classmethod
    def _func_add(cls, a, b): return a + b

我意识到这是一篇较旧的文章,但我没有看到列出的一个因素是,在Python(pre-nonlocal)中,您不能修改引用环境中包含的局部变量。(在您的示例中,这样的修改并不重要,但从技术上讲,无法修改这样的变量意味着它不是真正的闭包。)

例如,以下代码不起作用:

def counter():
    i = 0
    def f():
        i += 1
        return i
    return f

c = counter()
c()
调用上面的c将引发未绑定的LocalError异常

这很容易通过使用可变表(如字典)来实现:

def counter():
    d = {'i': 0}
    def f():
        d['i'] += 1
        return d['i']
    return f

c = counter()
c()     # 1
c()     # 2

当然,这只是一个解决办法。

请注意,由于之前在测试代码中发现的错误,我的原始答案不正确。修订版如下。

我制作了一个小程序来测量运行时间和内存消耗。我创建了以下可调用类和闭包:

class CallMe:
    def __init__(self, context):
        self.context = context

    def __call__(self, *args, **kwargs):
        return self.context(*args, **kwargs)

def call_me(func):
    return lambda *args, **kwargs: func(*args, **kwargs)
我对接受不同数量参数的简单函数的调用进行计时(
math.sqrt()
带有1个参数,
math.pow()
带有2个参数,
max()
带有12个参数)

我在Linuxx64上使用了CPython2.7.10和3.4.3+。我只能在Python2上进行内存分析。我使用的源代码是可用的

我的结论是:

  • 闭包的运行速度比等价的可调用类快:在Python2上大约快3倍,但在Python3上只快1.5倍。缩小的原因既有闭包变慢,也有可调用类变慢
  • 闭包比等价的可调用类占用更少的内存:大约占内存的2/3(仅在Python2上测试)
  • 虽然不是原始问题的一部分,但值得注意的是,通过闭包进行调用的运行时开销与调用
    math.pow()
    大致相同,而通过可调用类进行调用的运行时开销大约是前者的两倍
这些都是非常粗略的估计,它们可能会因硬件、操作系统和您正在比较的功能而有所不同。但是,它让您了解了使用每种可调用函数的影响


因此,这支持了@RaymondHettinger给出的公认答案是正确的(与我之前所写的相反),并且对于间接调用,闭包应该是首选的,至少只要它不影响可读性。另外,感谢@AXO指出了我原始代码中的错误。

“使用什么更好”?请定义您试图优化的标准。擅长什么?更小更快?更多使用Oracle许可产品?你说“更好”是什么意思?@max,实际上闭包可以有状态(它们可以捕获周围的任何东西,包括局部变量)。@S.Lott,据我所知,这个问题类似于:“为什么你更喜欢可调用类而不是闭包?”另请参见:@S.Lott:我实际上是想知道我将来应该怎么想,每当这个问题出现时。所以我不想把讨论局限于一种特定的情况。我想,我想考虑的方面是性能、清晰度、灵活性和可靠性。至少我曾经说过,代码的优化可能会被使用的时间提升。但是,在大多数情况下,高级算法有更大的潜力来获得更高的性能。这些函数确实通过使用cpu时间显示在分析器的顶部,因为它们重复了很多次。我在想
class CallMe:
    def __init__(self, context):
        self.context = context

    def __call__(self, *args, **kwargs):
        return self.context(*args, **kwargs)

def call_me(func):
    return lambda *args, **kwargs: func(*args, **kwargs)