例如,Python似乎没有元类(Metaclasses)。Smalltalk没有生成器(generators)的概念。虽然它们都被称为动态类型语言,但我认为Python不执行动态方法分派。这是正确的吗?
type
。 Python的设计哲学(即“Python之禅”)可以通过在交互式解释器的提示符下执行import this
来查看;这里适用的点是第二点,“显式优于隐式”。class sic:
__metaclass__ = mymeta
...
class sify(metaclass=mymeta):
...
Smalltalk没有生成器的概念。
Python的生成器是一等公民(通常是独立的)函数,而Smalltalk没有“独立”的函数概念——它有类内的方法。但是它当然有迭代器——作为类存在:
iterator := aCollection iterator.
[iterator hasNext] whileTrue: [iterator next doSomething].
由于Smalltalk拥有一流的"代码块"(Ruby从中借鉴了这一点),因此您可以通过向适当的方法发送代码块来实现迭代,就像其他"控制结构"一样,并且如果您希望,可以直接使用集合进行操作(例如select:
):
aCollection select: [:item | item doSomething].
在Smalltalk(和Ruby)中,您将代码块发送到迭代;而Python则相反,迭代会向周围的“调用”代码发送值。看起来非常不同,但最终并没有“深层次”的区别。
一级代码块意味着Smalltalk不需要也没有“控制结构”语句和关键字,例如if
或while
:可以通过将代码块作为适当方法的参数(例如布尔值的ifTrue:
方法)来完成它们。(Ruby选择在一级代码块之外拥有关键字/语句,我会说,Python [[明确]]和Smalltalk [[隐式]]都像C一样尝试“提供执行操作的单一方法”,而Ruby更多地是Perl-ish学派的“有很多种方法可以做到这一点”)。
虽然两者都被认为是动态类型,但我认为Python不执行动态方法分派。这正确吗?
不,完全不正确- Python 极度执行动态方法分派,到了极致。例如考虑:
for i in range(10):
myobject.bah()
myobject
中进行10次查找以获取方法bah
。这是为了防止先前执行的方法导致myobject
在内部完全重构,以至于其当前的bah
方法与先前的方法完全不同(对于程序员来说,依赖如此激烈的动态性可能是一件相当疯狂的事情,但Python支持它)。这就是使得:themethod = myobject.bah
for i in range(10):
themethod()
Python代码中的一种常见手动优化——在循环之前进行一次动态查找,而不是在循环内部进行10次查找,每个循环一次(这是“常量提升”的情况,因为编译器受到Python极端规则的限制,不能自行执行“常量折叠”操作——除非它能够证明是无害的,在实践中这样的证明过于困难,所以Python实现通常不会费心去做)。
Python使用统一的命名空间:方法是对象的属性,就像任何其他属性一样,只是它们是可调用的。这就是为什么提取方法而不调用它(称为“绑定方法”),将引用设置为变量(或将其存储在列表或其他容器中,从函数返回它,等等)就像上面的常量提升示例一样,是一个简单明了的操作。
Smalltalk和Ruby有独立的方法和其他属性命名空间(在Smalltalk中,非方法属性在对象自己的方法外部不可见),因此“提取方法”和“调用结果对象”需要更多的内省仪式(但在某些情况下,特别是在调度的常见情况下,可能使得调度略微简单——特别是,“仅提及”一个无参数方法隐式调用它,而在Python中,就像在C中一样,调用是通过追加括号显式执行的,而“仅提及”则使其可用于任何类型的显式操作,包括调用;-)。
Python确实有元类。
Smalltalk具有一些不寻常的特点:
总之...也许不是逐点比较,但这本书真的很值得一读。
强调部分已添加,并省略了一个段落。因此,至少在这方面两种语言是相似的。Smalltalk实现
Smalltalk使用基于类型的消息分发器。每个实例都有一个单一的类型,其定义包含方法。当实例接收到消息时,分发器会在类型的消息到方法映射中查找相应的方法,然后调用该方法。[...]
许多其他动态类型的语言,包括Python、Ruby、Objective-C和Groovy使用类似的方法。