2010-09-26 49 views
1

Level:初学者面向对象编程基础:继承与投影(Python)

我正在做面向对象编程的第一步。代码旨在展示如何通过链条传递方法。因此,当我拨打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年秋季

回答

5

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

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


UG.say(person, 'bla') 

呼叫实例,而不是

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

变化

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的规则。

+0

@all:非常感谢你的解释!这也是一个很好的例子,显示了名称(变量,类等)的混淆是多么的容易......特别是如果基础仍然有点不稳定......我会尽量小心/注意命名约定。 – raoulbia 2010-09-26 15:46:24

+0

不客气,巴巴。祝你学习好运。 – unutbu 2010-09-26 16:44:08

3

OK,时间上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

的答案是相当不错,但有一个方面说明,我认为是作出重要。摘录片段(MITPerson):

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

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

代码完全没有目的,永远不会有任何区别(除了边缘放缓整个系统),因此可以删除没有任何伤害,应该被删除:为什么有它呢?!任何代码都存在于你的程序中,但没有任何用处,这不可避免地会损害程序的质量:这种无用的“镇流器”会稀释有用的工作代码,使得你的程序难以阅读,维护和调试等等。大多数人似乎在大多数情况下直观地理解了这一点(所以你没有看到大量的代码,“假 - 覆盖”大多数方法,但在方法体内只是向超类实现),除了__init__ - 出于某种原因,许多人似乎有一个精神盲点,只是看不出与其他方法完全相同的规则。这个盲区可能来自于熟悉其他完全不同的语言,其中规则不适用于类的构造函数,加上一个误解,认为__init__作为一个构造函数,实际上它不是(它是一个初始值设定项) 。

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