面向对象编程基础知识:继承与继承阴影(Python)
级别:初学者
我正在迈出面向对象编程的第一步。该代码旨在展示方法如何在链上传递。因此,当我调用 UG.say(person, 'but i like')
时,方法 say
被指示调用类 MITPerson
。鉴于 MITPerson
不包含 say
方法,它将把它传递给类 Person
。我认为代码没有任何问题,因为它是讲座的一部分(请参阅下面的源代码)。我认为是我在运行代码时省略了定义某些内容。但不确定是什么。我认为错误消息正在寻找的作为第一个参数的 UG 实例
引用了 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 年秋季
Level: Beginner
I'm doing my first steps in Object Oriented programming. The code is aimed at showing how methods are passed up the chain. So when i call UG.say(person, 'but i like')
the method say
is instructed to call class MITPerson
. Given that MITPerson
does not contain a say
method it will pass it up to class Person
. I think there is nothing wrong with the code as it is part of a lecture (see source below). I think it is me who is omitting to define something when i run the code. Not sure what though. I think that the UG instance
the error message is looking for as first argument is refering to self
but that, in principle, doesn't need to be provided, correct? Any hints?
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)
source: MIT OpenCourseWare http://ocw.mit.edu Introduction to Computer Science and Programming Fall 2008
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您正在调用类而不是实例。
改为调用实例
You are calling class instead of instance.
Call instance instead
答案很好,但我认为有一个重要的旁注。拿片段(在
MITPerson
类中):这段代码完全无用且多余。当子类不需要重写其超类的方法实现中的任何内容时,子类看起来像“重写”该方法是完全没有用的......然后只需委托所有工作,无论如何,没有任何改变,到超类。
完全没有目的的代码,永远不会产生任何影响(除了稍微减慢整个系统的速度),因此可以删除而不会造成任何损害,应该被删除:为什么会有它在那里?!程序中存在但根本没有用的任何代码都不可避免地会损害程序的质量:这种无用的“镇流器”会稀释有用的工作代码,使程序更难以阅读、维护、调试等。
大多数人似乎在大多数情况下都直观地掌握了这一点(因此您看不到很多围绕“假覆盖”大多数方法的代码,但在方法主体中只是将其转至超类实现)例外对于
__init__
——由于某种原因,许多人似乎存在心理盲点,并且看不到与其他方法完全相同的规则。这个盲点可能来自于熟悉其他完全不同的语言,其中规则不适用于类的构造函数,再加上将__init__
视为构造函数的误解,其中它实际上不是(它是一个初始化器)。所以,总结一下:当且仅当它需要在超类自己的初始化程序之前、之后或之前和之后执行其他操作时,子类才应该定义 __init__ (很少需要它)做一些事情而不,即不委托给超类初始值设定项,但这几乎不是一个好的做法)。如果子类的
__init__
主体仅调用了超类的__init__
,且参数完全相同且顺序相同,则删除子类的__init__
从您的代码中(就像您对任何其他类似冗余方法所做的那样)。The answers are quite fine, but there's a side note I think is important to make. Take the snippet (in class
MITPerson
):This code is completely useless and redundant. When a subclass does not need to override anything in its superclass's implementation of a method, it's totally useless for the subclass to look like it's "overriding" that method... and then just delegate all the work, without any changes, to the superclass anyway.
Code which has absolutely no purpose, can never make any difference (except marginally slowing down the whole system), and can therefore be removed without any harm, should be removed: why have it there at all?! Any code that's present in your program, but not at all useful, is inevitably hurting the quality of your program: such useless "ballast" dilutes the useful, working code, making your program harder to read, maintain, debug, and so on.
Most people seem to grasp this intuitively in most situations (so you don't see a lot of code around which "fake-overrides" most methods but in the method body just punts to the superclass implementation) except for
__init__
-- where many, for some reason, seem to have a mental blind spot and just can't see that exactly the same rule applies as for other methods. This blind spot may come from being familiar with other, completely different languages, where the rule does not apply to a class's constructor, plus a misconception that sees__init__
as a constructor where it actually isn't (it's an initializer).So, to summarize: a subclass should define
__init__
if, and only if, it needs to do something else before, or after, or both before and after, the superclass's own initializer (very rarely it may want to do something instead, i.e., not delegate to the superclass initializer at all, but that's hardly ever good practice). If the subclass's__init__
body has just a call to the superclass's__init__
, with exactly the same parameters in the same order, expunge the subclass's__init__
from your code (just as you would do for any other similarly-redundant method).好的,现在是关于 Python 方法的简短教程了。
当您在类中定义函数时:
然后使用点式查找检索该函数时,您会得到一个称为“绑定方法”的特殊对象,其中第一个参数自动传递给该函数作为它所在的实例被称为。请参阅:
但是,由于该函数也是在类上定义的,因此您可以通过类而不是通过特定实例来查找它。但是,如果您这样做,您将不会获得绑定方法(显然 - 没有任何东西可以绑定!)。您将获得原始函数,您需要将要调用它的实例传递给该函数:
第一次调用失败,因为函数
say
需要一个UG 作为它的第一个参数,什么也得不到。第二次调用自动绑定第一个参数,因此它可以工作;第三个手动传递您要使用的实例。第二个和第三个是等价的。
还有一件事要提,那就是
say
函数实际上并不需要执行该命令的UG
实例。如果没有,您可以将其注册为“静态方法”,这告诉Python不要绑定第一个属性:OK, time for a short tutorial on Python methods.
When you define a functions inside a class:
and then retrieve that function using a dotted lookup, you get a special object back called a "bound method", in which the first argument is automatically passed through to the function as the instance upon which it is called. See:
But, since that function was also defined on the class, you can look it up through the class instead of through a specific instance. If you do that, though, you won't get a bound method (obviously -- there's nothing to bind!). You'll get the original function, to which you need to pass the instance that you want to call it on:
The first call fails, since the function
say
expects an instance ofUG
as its first argument and gets nothing. The second call automatically binds the first argument, so it works; the third manually passes the instance you want to use. The second and third are equivalent.There's one more thing to mention, which is that it doesn't seem like the
say
function actually needs the instance ofUG
which is doing the saying. If not, you can register it as a "static method", which tells Python not to bind the first attribute:更改
为
UG.say
返回未绑定方法say
。 “Unbound”意味着say
的第一个参数不会自动为您填写。未绑定方法say
需要 3 个参数,第一个参数必须是UG
的实例。反而,UG.say(person, 'but i like')
发送Person
的实例作为第一个参数。这解释了 Python 向您提供的错误消息。相反,
ug.say
返回绑定方法say
。 “Bound”意味着要说的第一个参数将是ug
。绑定方法采用 2 个参数:toWhom
和something
。因此,ug.say(person, 'but i like')
按预期工作。未绑定方法的概念已从Python3。相反,UG.say 只是返回一个(仍然)需要 3 个参数的函数。唯一的区别是不再对第一个参数进行类型检查。然而,你仍然会遇到一个错误,只是一个不同的错误:
PS。当开始学习 Python 时,我想我只是尝试接受 UG.say 返回一个未绑定的方法(需要 3 个参数),而 ug.say 是正常的调用方法的正确方法(需要 2 个参数)。稍后,要真正了解 Python 如何实现这种行为差异(同时保持相同的限定名称语法),您需要研究 描述符和属性查找的规则。
Change
to
UG.say
returns the unbound methodsay
. "Unbound" implies that the first argument tosay
is not automatically filled in for you. The unbound methodsay
takes 3 arguments, and the first one must be an instance ofUG
. Instead,UG.say(person, 'but i like')
sends an instance ofPerson
as the first argument. This explains the error message Python gives to you.In contrast,
ug.say
returns the bound methodsay
. "Bound" implies that the first argument to say will beug
. The bound method takes 2 arguments,toWhom
andsomething
. Thus,ug.say(person, 'but i like')
works as expected.The concept of unbound method has been removed from Python3. Instead,
UG.say
just returns a function which (still) expects 3 arguments. The only difference is that there is no more type checking on the first argument. You'll still end up with an error, however, just a different one:PS. When beginning to learn Python, I think I'd just try to accept that
UG.say
returns an unbound method (expecting 3 arguments), andug.say
is the normal proper way to call a method (expecting 2 arguments). Later, to really learn how Python implements this difference of behavior (while maintaining the same qualified name syntax), you'll want to research descriptors and the rules of attribute lookup.