1

级别:初学者

我正在做面向对象编程的第一步。该代码旨在显示方法如何向上传递。因此,当我调用UG.say(person, 'but i like')该方法时say,会指示调用 class MITPerson。鉴于它MITPerson不包含say方法,它将把它传递给 class 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)

来源:MIT OpenCourseWare http://ocw.mit.edu 2008 年秋季计算机科学和编程概论

4

4 回答 4

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')
于 2010-09-26T14:35:30.313 回答
3

答案很好,但我认为有一点很重要。取片段(在课堂上MITPerson):

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

这段代码完全没用而且是多余的。当子类不需要覆盖其超类的方法实现中的任何内容时,子类看起来像是“覆盖”了该方法是完全没用的......然后只是将所有工作委托给超类,而不做任何更改反正。

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

大多数人似乎在大多数情况下都直观地掌握了这一点(所以你看不到很多“假覆盖”大多数方法的代码,但在方法主体中只是平底船到超类实现)除了__init__- 其中很多,对于出于某种原因,似乎有一个心理盲点,只是看不到与其他方法完全相同的规则适用。这个盲点可能来自于熟悉其他完全不同的语言,其中规则不适用于类的构造函数__init__,以及将其视为构造函数而实际上并非如此的误解(它是初始化程序)。

所以,总结一下:一个子类应该定义__init__当且仅当它需要在超类自己的初始化器之前、之后或之前和之后做其他事情(很少它可能想要做一些事情完全委托给超类初始化程序,但这几乎不是好的做法)。如果子类的主体调用了超类的,并以相同的顺序使用完全相同的参数,则从代码中删除子类(就像对任何其他类似冗余的方法所做的那样)。__init____init____init__

于 2010-09-26T15:09:19.393 回答
2

改变

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

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

UG.say返回未绑定的方法say。“未绑定”意味着say不会自动为您填写第一个参数。unbound 方法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 个参数的函数。唯一的区别是不再对第一个参数进行类型检查。你仍然会得到一个错误,但是,只是一个不同的错误:

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

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

于 2010-09-26T14:31:20.090 回答
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
于 2010-09-26T14:42:05.133 回答