静态函数如len()、max()和min()的优势相比于继承方法调用
我刚开始学Python,不太明白为什么Python把len(obj)、max(obj)和min(obj)这些函数设计成静态函数,而不是像Java那样用obj.len()、obj.max()和obj.min()这种方式。
使用len()这些函数有什么优缺点呢?(除了明显的不一致性)
为什么Guido(Python的创始人)选择了这种方式,而不是用方法调用?(如果需要的话,这在Python 3中是可以解决的,但它没有改变,所以一定有好的理由……我希望如此)
谢谢!!
4 个回答
其实,你所说的那些并不是你想的那种“静态”方法。它们是一些内置函数,实际上只是指向某些实现了这些方法的Python对象。
>>> class Foo(object):
... def __len__(self):
... return 42
...
>>> f = Foo()
>>> len(f)
42
这些函数无论对象是否实现了它们,都是可以随时调用的。这样做的目的是为了保持一致性。比如,有些类可能有一个叫做length()的方法,而另一些类则叫size(),但大家约定用len这个函数,这样调用的时候就可以用更简单易懂的方式:len(obj),而不是obj.methodThatDoesSomethingCommon。
这里强调的是一个对象的能力,而不是它的方法或类型。能力是通过一些“辅助”函数来声明的,比如 __iter__
和 __len__
,但这些并不构成接口。接口其实是在内置函数中,另外还有一些内置运算符,比如 + 和 [],用于索引和切片。
有时候,这种关系并不是一一对应的。例如,iter(obj)
可以返回一个对象的迭代器,即使 __iter__
没有被定义。如果没有定义,Python 会继续检查这个对象是否定义了 __getitem__
,如果有的话,它会返回一个可以按索引访问对象的迭代器(就像数组一样)。
这和 Python 的鸭子类型有关,我们只关心能对一个对象做什么,而不在乎它属于哪种特定类型。
一个很大的好处是,内置函数(和运算符)在适当的时候可以应用额外的逻辑,而不仅仅是调用特殊方法。比如,min
函数可以查看多个参数并进行适当的比较,或者它可以接受一个可迭代对象作为参数并以类似的方式处理;而 abs
函数在调用一个没有 __abs__
特殊方法的对象时,可以尝试将该对象与0进行比较,并在需要时使用对象的改变符号的方法(虽然目前它并没有这样做);还有其他类似的情况。
因此,为了保持一致性,所有广泛适用的操作必须始终通过内置函数和/或运算符来进行,而这些内置函数的责任就是查找并应用适当的特殊方法(在一个或多个参数上),在适用的情况下使用替代逻辑,等等。
一个没有正确应用这一原则的例子(但在Python 3中修复了不一致性)是“向前推进一个迭代器”:在2.5及之前的版本中,你需要定义并调用迭代器上那个没有特殊名称的 next
方法。而在2.6及以后的版本中,你可以用正确的方式来做:迭代器对象定义了 __next__
,新的 next
内置函数可以调用它 并且 应用额外的逻辑,比如提供一个默认值(在2.6中你仍然可以用旧的方式来做,以保持向后兼容,但在 3.*
中就不行了)。
另一个例子:考虑表达式 x + y
。在传统的面向对象语言中(只能根据最左边参数的类型进行调度——像Python、Ruby、Java、C++、C#等),如果 x
是某种内置类型,而 y
是你自己新定义的类型,如果语言坚持将所有逻辑委托给实现加法的 type(x)
的方法,你就很倒霉了(假设语言允许运算符重载;-)。
在Python中,+
运算符(当然,内置的 operator.add
也是如此,如果你喜欢的话)会先尝试 x
的类型的 __add__
方法,如果这个方法不知道如何处理 y
,那么就会尝试 y
的类型的 __radd__
方法。因此,你可以定义你的类型,使它们知道如何与整数、浮点数、复数等进行加法,同时也可以定义能够将这些内置数字类型与自己相加的类型(也就是说,你可以编写代码,使得 x + y
和 y + x
都能正常工作,当 y
是你的新类型的实例,而 x
是某种内置数字类型的实例时)。
所谓的“通用函数”(在PEAK中)是一种更优雅的方法(允许根据类型的组合进行任何重写,绝对 不会像面向对象编程那样疯狂地只关注最左边的参数!),但是(a)不幸的是,它们没有被接受为Python 3的一部分,(b) 它们当然需要将通用函数表示为独立的(如果必须考虑函数“属于”某个单一类型,那就太疯狂了,因为其核心目的就是可以根据多个参数的类型的任意组合进行不同的重写/重载!)。任何在Common Lisp、Dylan或PEAK中编程过的人都知道我在说什么;-)。
所以,独立的函数和运算符就是最正确、一致的做法(尽管在简单的Python中缺少通用函数确实减少了一些固有的优雅,但它仍然是优雅与实用的合理结合!-)。