等级:初学者
我正在进行面向对象编程的第一步。该代码旨在展示方法如何在链中传递。因此,当我致电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年秋季计算机科学与编程简介
答案 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个参数toWhom
和something
。因此,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如何实现这种行为差异(同时保持相同的限定名称语法),您需要研究descriptors和attribute 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