【问题标题】:object oriented programming basics: inheritance & shadowing (Python)面向对象编程基础:继承和阴影(Python)
【发布时间】:2010-09-26 14:22:34
【问题描述】:

级别:初学者

我正在做面向对象编程的第一步。该代码旨在显示方法如何向上传递。因此,当我调用UG.say(person, 'but i like') 时,方法say 被指示调用类MITPerson。鉴于MITPerson 不包含say 方法,它将把它传递给Person 类。我认为代码没有任何问题,因为它是讲座的一部分(参见下面的源代码)。我认为是我在运行代码时忽略了一些定义。不确定是什么。我认为 UG instance 正在寻找错误消息,因为第一个参数是指 self 但原则上不需要提供,对吗?有什么提示吗?

class Person(object):
    def __init__(self, family_name, first_name):
        self.family_name = family_name
        self.first_name = first_name
    def familyName(self):
        return self.family_name
    def firstName(self):
        return self.first_name
    def say(self,toWhom,something):
        return self.first_name + ' ' + self.family_name + ' says to ' +   toWhom.firstName() + ' ' + toWhom.familyName() + ': ' + something


class MITPerson(Person):
    def __init__(self, familyName, firstName):
        Person.__init__(self, familyName, firstName)


class UG(MITPerson):
    def __init__(self, familyName, firstName):
        MITPerson.__init__(self, familyName, firstName)
        self.year = None
    def say(self,toWhom,something):
        return MITPerson.say(self,toWhom,'Excuse me, but ' + something)



>>> person = Person('Jon', 'Doe')
>>> person_mit = MITPerson('Quin', 'Eil')
>>> ug = UG('Dylan', 'Bob')
>>> UG.say(person, 'but i like')


    UG.say(person, 'bla')
**EDIT (for completeness)**: it should say UG.say(person, 'but i like') #the 'bla' creeped in from a previous test
TypeError: unbound method say() must be called with UG instance as first argument (got Person instance instead)

来源:MIT OpenCourseWare http://ocw.mit.edu Introduction to Computer Science and Programming Fall 2008

【问题讨论】:

    标签: python oop inheritance shadowing


    【解决方案1】:

    你调用的是类而不是实例。

    >>> ug = UG('Dylan', 'Bob')
    >>> UG.say(person, 'but i like')
    
    
    UG.say(person, 'bla')
    

    改为调用实例

    >>> ug = UG('Dylan', 'Bob')
    >>> ug.say(person, 'but i like')
    

    【讨论】:

      【解决方案2】:

      答案很好,但我认为有一点很重要。拿sn-p(上课MITPerson):

      def __init__(self, familyName, firstName):
          Person.__init__(self, familyName, firstName)
      

      此代码完全无用且多余。当子类不需要在其超类的方法实现中重写 anything 时,子类看起来像是“重写”该方法是完全没用的......然后只是委托所有工作,没有任何改变,反正超类。

      完全没有目的的代码,永远不会产生任何影响(除了稍微减慢整个系统的速度),因此可以在没有任何伤害的情况下删除,应该删除:为什么要在全部?!任何存在于程序中但根本没有用的代码都不可避免地会损害程序的质量:这种无用的“镇流器”会稀释有用的工作代码,使您的程序更难阅读、维护、调试等等。

      大多数人似乎在大多数情况下都直观地掌握了这一点(所以你看不到很多“假覆盖”大多数方法的代码,但在方法体中只是平底锅到超类实现)除了 代表__init__——由于某种原因,许多人似乎有一个心理盲点,只是看不到与其他方法完全相同的规则。这个盲点可能来自于熟悉其他完全不同的语言,其中规则不适用于类的构造函数,以及将__init__ 视为构造函数的误解,而实际上并非如此(这是一个初始化器)。

      所以,总结一下:子类应该定义__init__,当且仅当它需要在超类自己的初始化程序之前、之后或之前和之后做其他事情(很少它可能想要做而不是,即委托给超类初始化器,但这几乎不是好的做法)。如果子类的__init__ 主体只是调用了超类的__init__,并以相同的顺序使用完全相同的参数,请从您的代码中删除子类的__init__(就像您会做的那样对于任何其他类似冗余的方法)。

      【讨论】:

        【解决方案3】:

        好的,现在是关于 Python 方法的简短教程的时间了。

        在类中定义函数时:

        >>> class MITPerson:
        ...     def say(self):
        ...             print ("I am an MIT person.")
        ...
        >>> class UG(MITPerson):
        ...     def say(self):
        ...             print ("I am an MIT undergrad.")
        ...
        

        然后使用点分查找检索该函数,您将得到一个称为“绑定方法”的特殊对象,其中第一个参数作为调用它的实例自动传递给函数。见:

        >>> ug = UG()
        >>> ug.say
        <bound method UG.say of <__main__.UG object at 0x022359D0>>
        

        但是,由于该函数也是在类中定义的,因此您可以通过类而不是通过特定实例来查找它。但是,如果您这样做,您将不会获得绑定的方法(显然 - 没有什么可以绑定的!)。您将获得原始函数,您需要将要调用它的实例传递给该函数:

        >>> UG.say()
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: unbound method say() must be called with UG instance as first argument (got nothing instead)
        >>> ug.say()
        I am an MIT undergrad.
        >>> UG.say(ug)
        I am an MIT undergrad.
        

        第一次调用失败,因为函数say 期望UG 的实例作为它的第一个参数,但什么也得不到。第二个调用自动绑定第一个参数,所以它起作用了;第三个手动传递您要使用的实例。第二个和第三个是等价的。


        还有一件事要提一下,那就是say 函数似乎并不需要UG 的实例,这就是所谓的。如果没有,你可以将它注册为“静态方法”,它告诉 Python 不要绑定第一个属性:

        >>> class UG:
        ...     @staticmethod
        ...     def say():
        ...             print("foo")
        ...
        >>> ug = UG()
        >>> UG.say()
        foo
        >>> ug.say()
        foo
        

        【讨论】:

          【解决方案4】:

          改变

          UG.say(person, 'but i like')
          

          ug.say(person, 'but i like')
          

          UG.say 返回未绑定的方法say。 “未绑定”意味着say 的第一个参数不会自动为您填写。未绑定方法say 接受3 个参数,第一个参数必须是UG 的实例。反而, UG.say(person, 'but i like') 发送 Person 的实例作为第一个参数。这解释了 Python 给你的错误信息。

          相比之下,ug.say 返回绑定方法say。 “Bound”意味着要说的第一个参数是ug。绑定方法有 2 个参数,toWhomsomething。因此,ug.say(person, 'but i like') 按预期工作。

          unbound method has been removed from Python3 的概念。相反,UG.say 只返回一个(仍然)需要 3 个参数的函数。唯一的区别是不再对第一个参数进行类型检查。你仍然会得到一个错误,但是,只是一个不同的错误:

          TypeError: say() takes exactly 3 positional arguments (2 given)
          

          PS。在开始学习 Python 时,我想我会尝试接受 UG.say 返回一个未绑定的方法(期望 3 个参数),而 ug.say 是调用方法的正常正确方法(期望 2 个参数)。稍后,要真正了解 Python 如何实现这种行为差异(同时保持相同的限定名称语法),您需要研究 descriptorsattribute lookup 的规则。

          【讨论】:

          • @all:非常感谢您的解释!这也是一个很好的例子,它显示了被名称混淆是多么容易(无论是变量、类等)......特别是如果基础仍然有点不稳定......我会尽量小心/注意命名约定。
          猜你喜欢
          • 1970-01-01
          • 2012-09-23
          • 1970-01-01
          • 1970-01-01
          • 2011-01-06
          • 2012-11-27
          • 1970-01-01
          • 1970-01-01
          • 2011-03-03
          相关资源
          最近更新 更多