Smalltalk(例如Pharo)与Python比较如何?

7 投票
5 回答
3764 浏览
提问于 2025-04-15 14:44

我看到有人比较过Smalltalk和Ruby,还有Ruby和Python,但是Python和Smalltalk之间的比较我还没见过。我特别想知道它们在实现、语法、可扩展性和理念上的根本区别是什么。

比如说,Python似乎没有元类(Metaclasses)。而Smalltalk没有生成器(generators)的概念。虽然说这两种语言都是动态类型的,但我觉得Python并不支持动态方法调度。这说得对吗?

5 个回答

2

我最近在读一本叫做《程序员的工作》的书,这本书里有很多顶尖程序员的访谈,内容非常精彩。其中有一位是Smalltalk语言的发明者,他详细讲了他的编程语言以及它和Python的关系(他对Python也很喜欢)。他唯一对Python的不满就是它的代码运行得比较慢……他其实很希望能把Smalltalk的即时编译器作为Python的后台,但因为这个软件属于他工作的公司,所以这件事没办法实现。

总之……这本书虽然不是逐点比较,但读起来还是很不错的。

7

Python 确实有元类这个概念。

Smalltalk 有一些特别的特点:

  • 它的语法相当简单,只有大约 6 个关键字!其他的功能(包括定义新类)都是通过调用方法来实现的(在 Smalltalk 中称为发送消息)。这让你可以在这个语言里创建一些特定用途的语言(DSL)。
  • 在 Smalltalk 中,你并不是存储源文件,而是有一个大的内存镜像,你可以实时修改它。你还可以修改大部分 Smalltalk 的内容(这可能会导致它出问题哦;)
9

比如说,Python似乎没有元类。

其实是有的,只是它不会为每个类自动生成一个新的元类,而是使用父类的元类,默认情况下是type。Python的设计理念,通常被称为“Python之禅”,可以通过在交互式解释器中输入import this来查看;这里面有一句很重要的,就是“显式优于隐式”。

在Python 2.X中,你可以用以下语法来指定一个自定义的元类:

class sic:
  __metaclass__ = mymeta
  ...

而在Python 3.X中,更优雅的方式是使用命名参数语法:

class sify(metaclass=mymeta):
  ...

Smalltalk没有生成器的概念。

Python的生成器是第一类(通常是独立的)函数,而Smalltalk没有“独立”函数的概念,它只有类中的方法。不过,Smalltalk确实有迭代器,当然是以类的形式存在:

iterator := aCollection iterator.
[iterator hasNext] whileTrue: [iterator next doSomething]. 

由于Smalltalk有第一类的“代码块”(Ruby是从它那里借来的),你可以通过将代码块发送给合适的方法来实现迭代,就像其他的“控制结构”一样,如果你愿意,也可以直接对集合进行操作(想想select:):

aCollection select: [:item | item doSomething].

所以在Smalltalk(和Ruby)中,你是将代码块发送给迭代;而Python则是反过来,迭代将值发送给周围的“调用”代码。看起来很不一样,但最终并没有“深层次”的不同。

if或while这样的“控制结构”语句和关键字:这些可以通过将代码块作为适当方法的参数来实现(例如布尔值的ifTrue:方法)。(Ruby选择在第一类代码块之外,额外添加这些关键字/语句;我认为Python[[显式]]和Smalltalk[[隐式]]都试图像C一样“提供一种执行操作的方式”,而Ruby则更倾向于Perl式的“有很多种方法可以做到这一点”。)

虽然说两者都是动态类型的,但我相信Python并不支持动态方法调度。这正确吗?

不,这绝对不正确——Python是非常支持动态方法调度的,甚至可以说是极端支持。举个例子:

for i in range(10):
  myobject.bah()

根据Python的语义,这会对myobject中的方法bah进行10次查找——以防之前执行该方法时,myobject内部结构发生了变化,以至于它的当前bah方法与之前的完全不同(程序员依赖这种极端的动态性可能有点疯狂,但Python是支持的)。这就是为什么:

themethod = myobject.bah
for i in range(10):
  themethod()

在Python代码中,一个常见的优化手段是——在循环之前进行一次动态查找,而不是在循环内部每次都查找10次,这是一种“常量提升”的情况,因为根据Python对动态查找的严格规则,编译器被禁止自己进行“常量折叠”——除非它能证明这样做是安全的,而实际上这样的证明太难了,所以Python的实现通常不去做。

Python使用统一的命名空间:方法是对象的属性,就像其他属性一样,只不过它们是可以调用的。这就是为什么提取方法而不调用它(称为“绑定方法”),将其引用存储在变量中(或者放入列表或其他容器中,或者从函数返回等)是一个简单的操作,就像上面提到的常量提升的例子一样。

Smalltalk和Ruby有单独的方法和其他属性的命名空间(在Smalltalk中,非方法属性在对象自己的方法外不可见),所以“提取一个方法”和“调用得到的对象”需要更多的反射性操作(但在某些情况下,调度的常见情况可能因此变得稍微简单一些——特别是“只提到”一个没有参数的方法会隐式调用它,而在Python中,像在C中一样,调用是通过添加括号显式执行的,而“只提到”则只是“提到”它,使其可以进行任何显式操作包括调用;-)。

撰写回答