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

时间:2016-10-21 09:42:38

标签: python class standard-library

许多Python内置"功能"实际上是类,虽然它们也有一个简单的函数实现。甚至非常简单,例如itertools.repeat。这是什么动机?对我来说似乎过度工程了。

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

def repeat(x):
    while True: yield x

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

我也理解这些类是可调用函数,以及如何使用类模拟类函数行为。但我不明白为什么它通过标准库如此广泛使用。

3 个答案:

答案 0 :(得分:5)

作为itertools的类实现具有生成器功能不具备的一些优点。例如:

  1. CPython在C层实现这些内置函数,在C层实现生成器"函数"最好实现为实现__next__的类,它保留状态作为实例属性;基于yield的生成器是一个精确的Python层,实际上,它们只是generator类的一个实例(所以它们实际上仍然是类实例,就像Python中的其他所有实例一样)
  2. 发电机不可拣选或可复制,并且不会有故事"使它们支持任何一种行为(内部状态太复杂而且不透明以概括它);一个类可以定义__reduce__ / __copy__ / __deepcopy__(如果它是Python级别的,它可能甚至不需要这样做;它会自动工作并使实例可pickleable / copyable(所以如果你已经从range迭代器生成了5个元素,你可以复制或pickle / unpickle它,并在迭代中获得迭代器相同的距离)
  3. 对于非发电机工具,原因通常是相似的。可以为类提供状态和定制的行为,而功能是不能的。它们可以继承自己(如果需要的话,但是C层类可以禁止子类化,如果它们具有逻辑和功能)。

    它对动态实例创建也很有用;如果你有一个未知类的实例但是有一个已知的原型(比如,采用可迭代的序列构造函数,或chain或其他),并且你想将其他类型转换为该类,你可以{ {1}};如果它是一个发电机,type(unknown)(constructorarg)是无用的,你就不能用它来制造更多的东西,因为你无法想象它是从哪里来的(不是以合理的方式)

    除此之外,即使您从未将这些功能用于编程逻辑,您还希望在交互式解释器中看到什么,或者对type(unknown)type(myiter)进行打印调试,但不提供有关原点的提示,或<class 'generator'>能告诉您具体内容及来自何处?

答案 1 :(得分:2)

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

$ 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'}]

也就是说,类也可以定义方法,以后可以调用返回的对象。例如,dict类定义了词典的.get()方法等。

答案 2 :(得分:2)

对于itertools.repeat(以及大多数迭代器),使用实现iterator协议的适当类具有实现/维护POV的一些优点 - 就像您可以更好地控制迭代一样,你可以专门化类等。我也怀疑有一些优化可以在C级完成适用于不适用于生成器的迭代器。

还要记住,类和函数也是对象 - def语句主要是用于创建function实例并用编译代码,本地命名空间,单元格,闭包和诸如此类的东西填充它的语法糖(a某种程度上涉及任务FWIW,我曾做过一次只是为了出于好奇而且它是一个主要的PITA),class语句也是用于创建新type实例的语法糖(手动执行它恰好是真的很琐碎)。从这个POV中,yield是一个类似的语法糖,它将你的函数变成一个工厂,返回泛型generator内置类型的实例 - IOW它使你的函数就像一个类,没有编写一个麻烦的麻烦完整的课程,但也没有精细的控制和可能的优化,你可以通过写一个完整的课程。

在更通用的leval上,有时将“函数”编写为自定义可调用类型而不是提供类似的收益 - 精细控制,可能的优化,以及有时更好的可读性(想想两步装饰器,自定义描述符等)。

最后是wrt / builtin类型(intstr等等)IIRC(如果我错了,请有人纠正我)他们最初是作为工厂职能的职能(在新式班级革命之前)当内置类型和用户定义类型是不同类型的对象时)。当然现在将它们作为普通类是有意义的,但它们必须保持all_lower命名方案以实现兼容性。