为什么许多Python内置/标准库函数实际上是类

2024-09-27 00:23:05 发布

您现在位置:Python中文网/ 问答频道 /正文

许多Python内置的“函数”实际上是类,尽管它们也有一个简单的函数实现。即使是非常简单的,比如itertools.repeat。这样做的动机是什么?对我来说,这似乎太过工程化了。在

编辑:我不是在问itertools.repeat或任何其他特定功能的用途。这只是一个非常简单的函数的一个例子,它有一个非常简单的可能实现:

def repeat(x):
    while True: yield x

但是itertools.repeat实际上不是一个函数,它是作为一个类实现的。我的问题是:为什么?这似乎是不必要的开销。在

我还了解类是可调用函数,以及如何使用类来模拟类似函数的行为。但我不明白为什么它在标准库中被广泛使用。在


Tags: 函数功能true编辑def用途内置例子
3条回答

作为itertools的类实现有一些生成器函数没有的优点。例如:

  1. CPython在C层实现这些内建,在C层,生成器“函数”最好实现为实现__next__的类,该类将状态保留为实例属性;yield的生成器是Python层的精确性,而且实际上,它们只是generator类的一个实例(因此它们实际上仍然是类实例,就像Python中的其他所有实例一样)
  2. 生成器不可pickle或copyable,也没有“story”来让它们支持这两种行为(内部状态太复杂和不透明,无法将其泛化);一个类可以定义__reduce__/__copy__/__deepcopy__(如果它是Python级别的类,则可能根本不需要这样做;它将自动工作)并使实例可pickle/copyable(因此,如果您已经从range迭代器生成了5个元素,那么您可以复制或pickle/unpickle它,并在迭代中获得相同距离的迭代器)

对于非发电机工具,原因通常是相似的。类可以被赋予一个函数所不能的状态和自定义行为,它们可以被继承(如果需要的话,但是C层类可以禁止子类化,如果它们是“逻辑”函数)。在

它对于动态实例创建也很有用;如果您有一个未知类的实例,但有一个已知的原型(比如,接受iterable的序列构造函数,或者chain或其他任何类型),并且您想将其他类型转换为该类,则可以执行type(unknown)(constructorarg);如果它是生成器,type(unknown)是无用的,则不能使用它来创建更多这本身是因为你不能反省来找出它是从哪里来的(不是以合理的方式)。在

除此之外,即使您从未使用过编程逻辑的特性,您更希望在交互解释器中看到什么,或者在type(myiter)<class 'generator'>的打印调试中看到什么,它没有给出关于来源的提示,或者{}确切地告诉您您拥有什么和它来自何处?在

函数和类都是可调用的,因此它们可以在高阶函数中互换使用。在

$ python2
... 
>>> map(dict, [["ab"], ["cd"], ["ef"]])
[{'a': 'b'}, {'c': 'd'}, {'e': 'f'}]
>>> map(lambda x: dict(x), [["ab"], ["cd"], ["ef"]])
[{'a': 'b'}, {'c': 'd'}, {'e': 'f'}]

也就是说,类还可以定义方法,您可以稍后在返回的对象上调用这些方法。例如,^{}类为字典等定义了^{}方法

itertools.repeat(以及大多数迭代器)的情况下,使用一个实现iterator协议的适当类具有实现/维护POV的一些优势,比如可以更好地控制迭代,你可以对类进行专门化等等,我也怀疑在C级可以对不适用于生成器的适当迭代器进行一些优化。在

还要记住,类和函数也是对象,def语句主要是用来创建function实例并用编译代码、本地名称空间、单元格、闭包和其他内容填充它的语法糖(这是一个涉及到某种程度的任务FWIW,出于好奇,我曾经做过一次,这是一个主要的PITA),而且class语句也是创建一个新的type实例的语法糖(手动操作实际上非常简单)。从这个观点来看,yield是一种类似的语法糖,它将您的函数转换成一个工厂,返回泛型generator内建类型的实例—这样可以使您的函数像类一样工作,不需要编写完整类的麻烦,也不需要通过编写完整的类来进行精细的控制和可能的优化。在

在更一般的层面上,有时把你的“函数”写成一个定制的可调用类型反而能带来类似的好处——精细的控制,可能的优化,有时只是更好的可读性(想想两步修饰符、自定义描述符等)。在

最后wrt/builtin types(intstr等等)IIRC(如果我错了,请有人纠正我)它们最初是充当工厂函数的函数(在新型类革命之前,当内置类型和用户定义类型是不同种类的对象时)。当然,现在将它们作为普通类是有意义的,但是为了兼容性,它们必须保留所有较低的命名方案。在

相关问题 更多 >

    热门问题