许多Python内置"功能"实际上是类,虽然它们也有一个简单的函数实现。甚至非常简单,例如itertools.repeat
。这是什么动机?对我来说似乎过度工程了。
编辑:我不是在询问itertools.repeat
或任何其他特定功能的用途。这只是一个非常简单的函数的例子,它具有非常简单的可能的实现:
def repeat(x):
while True: yield x
但itertools.repeat
实际上并不是一个函数,它是作为一个类实现的。我的问题是:为什么?这似乎是不必要的开销。
我也理解这些类是可调用函数,以及如何使用类模拟类函数行为。但我不明白为什么它通过标准库如此广泛使用。
答案 0 :(得分:5)
作为itertools
的类实现具有生成器功能不具备的一些优点。例如:
__next__
的类,它保留状态作为实例属性;基于yield
的生成器是一个精确的Python层,实际上,它们只是generator
类的一个实例(所以它们实际上仍然是类实例,就像Python中的其他所有实例一样) __reduce__
/ __copy__
/ __deepcopy__
(如果它是Python级别的,它可能甚至不需要这样做;它会自动工作并使实例可pickleable / copyable(所以如果你已经从range
迭代器生成了5个元素,你可以复制或pickle / unpickle它,并在迭代中获得迭代器相同的距离)对于非发电机工具,原因通常是相似的。可以为类提供状态和定制的行为,而功能是不能的。它们可以继承自己(如果需要的话,但是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'}]
答案 2 :(得分:2)
对于itertools.repeat
(以及大多数迭代器),使用实现iterator
协议的适当类具有实现/维护POV的一些优点 - 就像您可以更好地控制迭代一样,你可以专门化类等。我也怀疑有一些优化可以在C级完成适用于不适用于生成器的迭代器。
还要记住,类和函数也是对象 - def
语句主要是用于创建function
实例并用编译代码,本地命名空间,单元格,闭包和诸如此类的东西填充它的语法糖(a某种程度上涉及任务FWIW,我曾做过一次只是为了出于好奇而且它是一个主要的PITA),class
语句也是用于创建新type
实例的语法糖(手动执行它恰好是真的很琐碎)。从这个POV中,yield
是一个类似的语法糖,它将你的函数变成一个工厂,返回泛型generator
内置类型的实例 - IOW它使你的函数就像一个类,没有编写一个麻烦的麻烦完整的课程,但也没有精细的控制和可能的优化,你可以通过写一个完整的课程。
在更通用的leval上,有时将“函数”编写为自定义可调用类型而不是提供类似的收益 - 精细控制,可能的优化,以及有时更好的可读性(想想两步装饰器,自定义描述符等)。
最后是wrt / builtin类型(int
,str
等等)IIRC(如果我错了,请有人纠正我)他们最初是作为工厂职能的职能(在新式班级革命之前)当内置类型和用户定义类型是不同类型的对象时)。当然现在将它们作为普通类是有意义的,但它们必须保持all_lower命名方案以实现兼容性。