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

发布于 2024-09-24 23:22:40 字数 1770 浏览 4 评论 0原文

级别:初学者

我正在迈出面向对象编程的第一步。该代码旨在展示方法如何在链上传递。因此,当我调用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

救赎№ 2024-10-01 23:22:40

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

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


UG.say(person, 'bla')

改为调用实例

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

You are calling class instead of instance.

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


UG.say(person, 'bla')

Call instance instead

>>> ug = UG('Dylan', 'Bob')
>>> ug.say(person, 'but i like')
甜中书 2024-10-01 23:22:40

答案很好,但我认为有一个重要的旁注。拿片段(在MITPerson类中):

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

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

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

大多数人似乎在大多数情况下都直观地掌握了这一点(因此您看不到很多围绕“假覆盖”大多数方法的代码,但在方法主体中只是将其转至超类实现)例外对于 __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):

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

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).

胡大本事 2024-10-01 23:22:40

好的,现在是关于 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

OK, time for a short tutorial on Python methods.

When you define a functions inside a class:

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

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:

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

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:

>>> 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.

The first call fails, since the function say expects an instance of UG 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 of UG which is doing the saying. If not, you can register it as a "static method", which tells Python not to bind the first attribute:

>>> class UG:
...     @staticmethod
...     def say():
...             print("foo")
...
>>> ug = UG()
>>> UG.say()
foo
>>> ug.say()
foo
傲影 2024-10-01 23:22:40

更改

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

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

UG.say 返回未绑定方法 say。 “Unbound”意味着 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') 按预期工作。

未绑定方法的概念已从Python3。相反,UG.say 只是返回一个(仍然)需要 3 个参数的函数。唯一的区别是不再对第一个参数进行类型检查。然而,你仍然会遇到一个错误,只是一个不同的错误:

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

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

Change

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

to

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

UG.say returns the unbound method say. "Unbound" implies that the first argument to say is not automatically filled in for you. The unbound method say takes 3 arguments, and the first one must be an instance of UG. Instead,
UG.say(person, 'but i like') sends an instance of Person as the first argument. This explains the error message Python gives to you.

In contrast, ug.say returns the bound method say. "Bound" implies that the first argument to say will be ug. The bound method takes 2 arguments, toWhom and something. 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:

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

PS. When beginning to learn Python, I think I'd just try to accept that UG.say returns an unbound method (expecting 3 arguments), and ug.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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文