【问题标题】:What would be a good example of "sending messages to objects" within Python?在 Python 中“向对象发送消息”的一个很好的例子是什么?
【发布时间】:2016-02-05 16:19:14
【问题描述】:

我最近观看了 Sandi Metz 的 Nothing is Something,在她的演讲中,她使用了向对象发送消息的想法,并介绍了在 Ruby 中是如何完成的。 4:10-7:30 部分将是她开始讨论该主题的一个很好的切入点(这是一个构建块,然后渗透到一半以上的谈话中)。

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

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


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

【问题讨论】:

  • hah,在她说的部分:“不要制作 gem,不要把它放在 GitHub 上?”抱歉,在她说之前我已经这样做了 6 年多了 :-D joergwmittag.github.io/b001e
  • 大部分答案归结为:Smalltalk 中的“发送消息”在大多数其他 OO 语言中是“调用方法”。

标签: python ruby oop smalltalk


【解决方案1】:

1970 年代初在 Smalltalk 中以“发送消息”开始,现在更普遍地称为“calling a method”

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

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

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

【讨论】:

    【解决方案2】:

    Python 使用稍微不同的术语。它被称为“调用方法”。但这是同一回事。 (C++ 称之为“调用虚函数”。同样的区别。)

    就我个人而言,我不喜欢这个术语,它过于关注实现细节,失去了“消息发送”术语的很多隐喻能力。

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

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

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

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

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

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

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

    对我来说,OOP 仅意味着消息传递、本地保留、保护和隐藏状态过程,以及所有事物的极端后期绑定。

    他后来澄清the Big Thing is Messaging:

    只是一个温和的提醒,我在上一届 OOPSLA 上努力提醒大家,Smalltalk 不仅不是它的语法或类库,它甚至与类无关。很抱歉,我很久以前就为这个主题创造了“对象”一词,因为它让很多人专注于次要的想法。

    最重要的想法是“消息传递”——这就是 Smalltalk/Squeak 的核心内容(在我们的 Xerox PARC 阶段,它从未完全完成)。日本人有一个小词——ma——代表“介于两者之间的东西”——也许最接近的英语等价物是“interstitial”。制作出色且可扩展的系统的关键在于设计其模块的通信方式,而不是设计其内部属性和行为应该是什么。想想互联网——为了生存,它 (a) 必须允许许多不同类型的想法和实现超出任何单一标准,并且 (b) 允许这些想法之间不同程度的安全互操作性。

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

    当 Alan Kay 提出“面向对象”一词时,他深受后来成为 ARPANet 和互联网的启发:具有自己私有内存(“实例变量”)的独立机器(“对象”)通过发送消息相互通信。

    William R. Cook 和他的Proposal for Simplified, Modern Definitions of "Object" and "Object Oriented" 在On Understanding Data Abstraction, Revisited 中也提出了类似的观点。

    操作的动态分派是对象的基本特征。这意味着要调用的操作是对象本身的动态属性。操作不能被静态识别,并且通常没有办法确切地知道响应给定请求将执行什么操作,除非通过运行它。这与始终动态调度的一流函数完全相同。

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

    【讨论】:

    • 顿悟引言:“对象本质上是值的字典,类是函数的字典”
    • 解决评论,“在 Python 中,面向对象的数据抽象是通过约定实现的,而不是作为内置语言功能(例如 Smalltalk、Ruby)”:它实际上也纯粹是 Smalltalk 中的约定。在 Smalltalk 中对实例变量进行赋值语句是完全可能的。但惯例是为每个实例变量编写访问器(getter 和 setter),然后始终通过发送访问消息来使用这些访问器方法。
    • @wTyeRogers 更正,对象 id 本质上是一个具有潜在继承链的 dict,而 Class 是构造对象及其潜在继承链的函数。
    • 2年后,这一切终于下沉了。
    • @wTyeRogers:这需要 Nygaard、Dahl、Kay 等人。几十年来弄清楚,我认为 2 年相当不错:-D
    【解决方案3】:

    核心思想叫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
    

    【讨论】:

    • 在 Smalltalk 中,此消息应为 ' 12' trim,其答案为 '12'。
    • @LeandroCaniglia,在 Python 中它变成了' 12'.strip()。其他结构适用于更一般的情况。我想你可以用 Ruby 写 ' 12'.send(trim) 或类似的东西。
    【解决方案4】:

    本质上,“发送消息”在大多数 OO 语言中被称为调用方法。关键区别在于,在动态语言中,您不知道对象是否知道方法。所以如果你做var.jump() 你不知道var 是什么。也许它是一只兔子,它可以跳,或者它是一块石头,它不知道怎么做(Rock 类没有实现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“方法”本身并使用() 调用它。 这也是糟糕的 Python 设计的一个很好的例子。你如何在 Python 中做类似1.send(:send, :to_s) 的事情?在 Ruby 中,send 是代码对象的方法。所以你也可以通过它自己发送。在 Python 中 getattr 是一个外部独立函数。

    如果你真的想学习 OO 编程的概念,我建议你玩一下 Smalltalk,因为我发现它有最好的设计。 (它也不受 C++ 或其他东西的影响,因为在 70 年代它们还不存在)。有一本好书Pharo by Example 教你在 中编程。还有一个 work in progress version 为环境的新版本更新。

    【讨论】:

    • Self 和 Newspeak 也非常适合学习 OO。自我,因为它确实使人们相信它是关于对象及其交互的(系统中没有其他东西但对象),而Newspeak因为它绝对狂热地坚持消息传递:没有实例变量,只有getter和setter方法,没有类变量,只有元类的getter和setter方法,甚至没有局部变量变量,只有LexicalScope对象的getter和setter方法。
    • 即使超类查找也是消息发送!
    猜你喜欢
    • 2020-08-29
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 2010-11-07
    • 1970-01-01
    • 2010-12-06
    • 2011-08-28
    相关资源
    最近更新 更多