首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

PyPy/RPython作者谈论如何学习了解python

Carl Friedrich Bolz是伦敦国王学院的研究员,对各种动态语言的实施和优化非常感兴趣。他是PyPy / RPython的核心作者之一,曾从事Prolog,Racket,Smalltalk,PHP和Ruby的实现。他是Twitter上的@cfbolz。

介绍

面向对象编程是当今使用的主要编程范例之一,许多语言提供某种形式的面向对象。表面上,不同的面向对象编程语言为程序员提供的机制非常相似,但细节可能有很大差异。大多数语言的共同点是存在对象和某种继承机制。然而,类不是每种语言都直接支持的功能。例如,在基于原型的语言如Self或JavaScript中,类的概念不存在,而对象则直接相互继承。

了解不同对象模型之间的差异可能很有趣。他们经常揭示不同语言之间的家庭相似之处。将新语言的模型放入其他语言模型的上下文中可能很有用,既能快速理解新模型,又能更好地理解编程语言设计空间。

本章探讨了一系列非常简单的对象模型的实现。它从简单的实例和类开始,并能够在实例上调用方法。这是在早期OO语言(如Simula 67和Smalltalk)中建立的“古典”面向对象方法。然后这个模型逐步扩展,接下来的两个步骤探索不同的语言设计选择,最后一步提高对象模型的效率。最终的模型不是真正的语言,而是Python的对象模型的理想化简化版本。

本章介绍的对象模型将在Python中实现。该代码适用于Python 2.7和3.4。为了更好地理解行为和设计选择,本章还将介绍对象模型的测试。测试可以用py.test或nose运行。

Python作为实现语言的选择是非常不切实际的。一个“真正的”虚拟机通常用C / C ++这样的低级语言来实现,并且需要对工程细节进行大量的关注以使其高效。然而,更简单的实现语言使得更容易关注实际行为差异,而不是陷入实现细节之中。

基于方法的模型

我们将首先介绍的对象模型是Smalltalk的极简版。 Smalltalk是一个面向对象的编程语言,由Alan Kay的小组在20世纪70年代在Xerox PARC设计。它推广了面向对象的编程,并且是当今编程语言中许多功能的来源。 Smalltalk语言设计的核心原则之一是“一切都是对象”。 Smalltalk当前使用的最直接的继任者是Ruby,它使用更类似于C的语法,但保留了大部分Smalltalk的对象模型。

本节中的对象模型将具有它们的类和实例,可以读写属性到对象中,可以在对象上调用方法,也可以将类作为另一个类的子类。从一开始,类将是完全可以拥有属性和方法的普通对象。

关于术语的注释:在本章中,我将使用“实例”一词来表示 - “不是一个类的对象”。

首先开始的一个好方法是编写一个测试来指定要实现的行为应该是什么。本章介绍的所有测试将由两部分组成。首先,定义和使用一些类的一些常规Python代码,并利用Python对象模型的越来越高级的特性。其次,相应的测试使用我们将在本章中实现的对象模型,而不是普通的Python类。

使用普通Python类和使用我们的对象模型之间的映射将在测试中手动完成。例如,不是在Python中编写obj.attribute,而是在对象模型中使用方法obj.read_attr(“attribute”)。这种映射在实际的语言实现中可以由语言的解释器或编译器来完成。

本章中的进一步简化是,我们对实现对象模型的代码和用于编写对象中使用的方法的代码之间没有明确的区别。在一个真正的系统中,这两者通常会以不同的编程语言来实现。

让我们从阅读和书写对象字段的简单测试开始。

测试使用了我们必须实现的三件事情。 类Class和Instance分别表示我们的对象模型的类和实例。 有两个类的特殊实例:OBJECT和TYPE。 OBJECT对应于Python中的对象,并且是继承层次结构的最终基类。 TYPE对应于Python中的类型,并且是所有类的类型。

要对Class和Instance的实例做任何事情,他们通过继承共享基类Base来实现一个共享接口,Base可以公开许多方法:

Base类实现存储对象的类和包含对象的字段值的字典。 现在我们需要实现Class和Instance。 Instance的构造函数将该类实例化并将字段dict初始化为空字典。 否则Instance只是Base的一个非常简单的子类,不会增加任何额外的功能。

Class的构造函数接受类的名称,基类,类的字典和元类。 对于类,这些字段由对象模型的用户传递给构造函数。 类构造函数也接受一个基类,目前测试并不需要该基类,但我们将在下一节中使用它。

由于类也是一种对象,它们(间接)从Base继承。 因此,该类需要成为另一个类的实例:它的元类。

现在我们的第一次测试几乎通过 唯一缺少的是基类TYPE和OBJECT的定义,它们都是Class的实例。 对于这些,我们将从Smalltalk模型出发,该模型具有相当复杂的元类系统。 相反,我们将使用Python采用的ObjVlisp1中引入的模型。

在ObjVlisp模型中,OBJECT和TYPE是交织在一起的。 OBJECT是所有类的基类,这意味着它没有基类。 TYPE是OBJECT的一个子类。 默认情况下,每个类都是TYPE的一个实例。 特别是,TYPE和OBJECT都是TYPE的实例。 但是,程序员也可以通过子类来创建一个新的元类:

为了定义新的元类,对TYPE进行子类化就足够了。 但是,在本章的其余部分我们不会那样做; 我们将总是使用TYPE作为每个类的元类。

现在第一次测试通过。 第二个测试检查阅读和写作属性也适用于类。 写起来很容易,并且立即通过。

isinstance检查

到目前为止,我们还没有利用对象具有类的事实。 下一个测试实现了isinstance机器:

要检查对象obj是否是某个类cls的实例,检查cls是否是obj类的超类或该类本身就足够了。 为了检查一个类是否是另一个类的超类,这个类的超类链是走的。 当且仅当在该链中找到另一个类时,它才是超类。 包括类本身在内的类的超类链称为该类的“方法解析顺序”。 它可以很容易地递归计算:

通过该代码,测试通过。

调用方法

该对象模型的第一个版本的其余缺失功能是能够调用对象上的方法。 在本章中,我们将实现一个简单的单一继承模型。

为了找到发送给对象的方法的正确实现,我们走对象类的方法解析顺序。 在方法解析顺序中的一个类的字典中找到的第一个方法称为:

与Base实现中的callmethod代码一起,这通过了测试。

为了确保具有参数的方法也能正常工作,并且正确实现了重写方法,我们可以使用以下稍微复杂的测试,该测试已经通过:

Attribute-Based Model

现在我们的对象模型的最简单版本正在工作,我们可以想办法改变它。 本节将介绍基于方法的模型和基于属性的模型之间的区别。 这是Smalltalk,Ruby和JavaScript之间的核心区别之一,另一方面是Python和Lua之间的核心区别之一。

基于方法的模型将方法调用作为程序执行的原语:

基于属性的模型将方法调用分为两步:查找属性并调用结果:

以下测试可以显示这种差异:

虽然设置与方法调用的相应测试相同,但调用方法的方式不同。 首先,在对象上查找带有方法名称的属性。 该查找操作的结果是绑定方法,该对象封装了对象以及类中找到的函数。 接下来,通过调用操作2调用该绑定方法。

为了实现这个行为,我们需要改变Base.read_attr的实现。 如果在字典中找不到该属性,则在该类中查找该属性。 如果它在类中找到,并且该属性是可调用的,则需要将其转换为绑定方法。 要模拟绑定方法,我们只需使用闭包。 除了更改Base.read_attr之外,我们还可以更改Base.callmethod以使用新方法调用方法,以确保所有测试仍然通过。

其余的代码根本不需要改变。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180317A0NQSX00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券