Python 中 "sending messages to objects" 的一个很好的例子是什么?

What would be a good example of "sending messages to objects" within Python?

我最近看了 Nothing is Something by Sandi Metz, and in her talk she uses the idea of sending messages to objects and goes over how that's done in Ruby. The 4:10-7:30 部分将是她开始讨论该主题的一个很好的切入点(它是一个构建块,然后渗透到一半的谈话中)。

现在,对于一些背景知识:我在 Ruby 中编写程序的经验不多,对 smalltalk 的经验为零。我的 OO 经验有些有限,而且 非常 陈旧。我还在 Google 中查找了 send object message python,我所看到的都是关于通过套接字和电子邮件发送消息,这与我的想法不完全相同。

我不确定如何解释 Python 中的这个概念,或者如何实现它。 有什么想法吗? :)


旁注:她提到她的 OO 观点来自 smalltalk 的经验,所以我将其添加为这个问题的标签。

核心思想叫做Message passing

在 python 中,您通常使用标准符号发送消息:

obj.methodname(args)

如果方法名是动态的,可以这样做:

getattr(obj, methodname)(args)

示例:

>>> getattr("  12", "strip")()
'12'

尽管我们通常使用标准表示法,但在幕后有一个属性请求,您可以通过拦截调用来观察:

class Test(object):
    def __getattribute__(self, name):
        print "requesting attr:", name
        return object.__getattribute__(self, name)
    def test(self):
        print "Hello"

>>> Test().test()
requesting attr: test
Hello

Python 使用的术语略有不同。它被称为"calling a method"。但这是一回事。 (C++ 称它为 "calling a virtual function"。同样,差异相同。)

就我个人而言,我不喜欢这个术语,它过于关注实现细节并且失去了 "message sending" 术语的大部分隐喻能力。

与 Python 还有其他差异,其中一些最重要的差异是:

  • 面向对象的数据抽象是通过约定实现的,而不是作为内置语言功能(例如 Smalltalk,Ruby)或设计模式(Scheme、ECMAScript)
  • 并非所有的子例程都是方法

OO 的基本思想是消息传递:您向对象发送消息,对象响应。就像在现实生活中一样,您不知道对象对消息做了什么。您只能观察到答复。该对象可能会自己处理消息,它可能会借助其他人的帮助,它可能会盲目地转发消息而自己实际上没有做任何工作。

由于您无法知道对象对消息做了什么,您只能观察到对象的响应,因此您对对象的了解就是它的 协议(消息它理解并如何回应它们)。你不知道它的实现,你不知道它的表示。 OO就是这样实现数据抽象、信息隐藏、数据隐藏、封装的。

此外,由于每个对象独立决定如何响应消息,因此您获得了多态性。

一个响应消息的典型方式是执行与该消息对应的方法。但这是一种实现机制,这就是我不喜欢该术语的原因。作为比喻,它承载了我上面提到的none的内涵。

Alan Kay has said that OO is about three things, Messaging, Data Abstraction, and Polymorphism:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

他后来澄清说 the Big Thing is Messaging:

Just a gentle reminder that I took some pains at the last OOPSLA to try to remind everyone that Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.

The big idea is "messaging" -- that is what the kernal of Smalltalk/Squeak is all about (and it's something that was never quite completed in our Xerox PARC phase). The Japanese have a small word -- ma -- for "that which is in between" -- perhaps the nearest English equivalent is "interstitial". The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be. Think of the internet -- to live, it (a) has to allow many different kinds of ideas and realizations that are beyond any single standard and (b) to allow varying degrees of safe interoperability between these ideas.

事实上,正如我上面所说,在我看来,其他两个只是消息传递的结果。

当 Alan Kay 提出 "Object Orientation" 这个词时,他深受后来成为 ARPANet 和互联网的东西的启发:独立机器 ("objects") 拥有自己的私人记忆 ( "instance variables") 通过发送消息相互通信。

On Understanding Data Abstraction, Revisited by William R. Cook and also his Proposal for Simplified, Modern Definitions of "Object" and "Object Oriented"也有类似的观点。

Dynamic dispatch of operations is the essential characteristic of objects. It means that the operation to be invoked is a dynamic property of the object itself. Operations cannot be identified statically, and there is no way in general to exactly what operation will executed in response to a given request, except by running it. This is exactly the same as with first-class functions, which are always dynamically dispatched.

Python的对象系统与其他语言有点不同。 Python最初是一种过程语言,后来添加了对象系统,目的是使语言的更改量尽可能少。 Python 中的主要数据结构是 dicts(映射/哈希表),所有行为都在函数中。甚至在 Python 的 OO 特性之前,这种极简主义就表现出来了,例如局部变量和全局变量实际上只是 dict 中的键。因此,很自然地使对象和 class 很像 dict 并重用该概念,一个对象本质上是一个 dict 值,而一个 class 是dict 个函数。没有 "method" 的单独概念,相反,您有将接收者作为第一个参数的函数。 (在大多数其他 OO 语言中,接收者是 "hidden" 第零个参数,可以使用特殊关键字,例如 selfthisme。)

本质上 "sending a message" 在大多数 OO 语言中被称为调用方法。关键区别在于,在动态语言中,您不知道对象是否知道某个方法。所以如果你做 var.jump() 你不知道 var 是什么。也许它是一只兔子并且它可以跳跃,或者也许它是一块石头但它不知道该怎么做(Rock class 没有实现 jump() 方法)。所以从概念的角度来看,发送消息是要求某个对象做某事(也许它不知道你在问什么),而调用方法是让你知道的那个对象做你想做的事。

在 Smalltalk 和 Ruby 中,用另一条消息发送消息真的很容易,所以如果你使用 1.send(:to_s) 就和 1.to_s 一样。而1.send(:to_s)本身就是一条消息send,它以参数:to_s发送给1。所以你也可以把它扩展到 1.send(:send, :to_s).

在 Python 元编程是一个真正的痛苦。所以如果你想做同样的事情 1.send(:to_s) 你必须使用像 getattr(1, "to_s")() 这样的东西。在这里,您使用 getattr 获取 to_s "method" 本身,并使用 () 调用它。 这也是糟糕Python设计的一个很好的例子。你如何在 Python 中做类似 1.send(:send, :to_s) 的事情?在 Ruby send 中是代码对象上的一个方法。所以你也可以通过它自己发送它。在 Python 中,getattr 是一个外部独立函数。

如果你真的想学习 OO 编程的概念,我建议你使用 Smalltalk,因为我发现它的设计最好。 (它也不受 C++ 或其他东西的影响,因为在 70 年代它们不存在)。有一本很好的书 Pharo by Example that teaches you to program in . Also there is a work in progress version 更新了环境的新版本。

在 1970 年代早期的 Smalltalk 中,最初是 "sending a message",现在更普遍地称为“calling a method

发送消息是礼貌地请求对象做出响应。如果它理解它收到的信息并且有回应的方法,它会礼貌地回应。即如果消息在 receiverprotocol 中。

协议是它具有响应方法的消息列表。现在,我们将协议称为接收方的 API.

Alan Kay,Smalltalk 之父,认为消息传递,而不是继承,是所谓的面向对象编程的重要方面。