面向对象编程基础:继承与扩展阴影(Python)

时间:2010-09-26 14:22:35

标签: python oop inheritance shadowing

等级:初学者

我正在进行面向对象编程的第一步。该代码旨在展示方法如何在链中传递。因此,当我致电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)

来源:麻省理工学院开放式课件http://ocw.mit.edu 2008年秋季计算机科学与编程简介

4 个答案:

答案 0 :(得分:4)

您正在调用类而不是实例。

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


UG.say(person, 'bla')

调用实例

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

答案 1 :(得分:3)

答案很好,但我认为重要的是要注意。获取摘录(在课程MITPerson中):

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

此代码完全无用且冗余。当子类不需要在其超类的方法实现中覆盖任何时,子类看起来像是“覆盖”该方法完全没用......然后只是委托所有的工作,没有任何改变,无论如何都要超级。

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

大多数人似乎在大多数情况下直观地掌握了这一点(所以你没有看到很多代码围绕哪些“假冒 - 覆盖”大多数方法,但在方法体中只是向超类实现添加)除了< / em> for __init__ - 由于某种原因,许多人似乎有一个心理盲点,只是看不出与其他方法完全相同的规则。这个盲点可能来自于熟悉其他完全不同的语言,其中规则不适用于类的构造函数,加上误认为__init__是构造函数,它实际上不是't(它是初始化程序)。

因此,总结一下:子类应该定义__init__ if,并且只有当它需要在超类自己的初始化程序之前,之后或之前和之后执行其他操作时(很少它可能需要)做一些事情而不是,即完全委托给超类初始化程序,但这几乎没有好的做法。如果子类的__init__正文只是对超类__init__的调用,并且具有相同顺序的完全相同的参数,则从代码中清除子类的__init__ (正如你对任何其他类似冗余方法所做的那样)。

答案 2 :(得分:2)

更改

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。 “绑定”意味着要说的第一个参数将是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的规则。

答案 3 :(得分:2)

好的,是时候进行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