为什么在__init__期间实例化的对象不能看到它们的创建者?

时间:2012-01-28 13:03:58

标签: python oop instantiation init

我也是SO,OOP和python的新手,所以请保持温和;)

我在其他地方寻找与此范围问题相关的线索和解释,但没有发现任何问题。如果有任何帮助,我将不胜感激。

示例代码:

class Zeus(object):
    def __init__(self):
        self.name='zeus'
        self.maze=Maze()
        self.maze.get_zeus_name_1()
        self.maze.get_zeus_name_2(self)
        self.get_name_1()
        self.get_name_2(self)

    def get_name_1(self):
        try:
            print zeus.name
        except:
            print "impossible!?"

    def get_name_2(self,scope):
        print scope.name

class Maze(object):
    def get_zeus_name_1(self):
        try:
            print zeus.name
        except:
            print "can't be done!?"

    def get_zeus_name_2(self,scope):
        print scope.name

zeus=Zeus()
print 'now external calls:'
zeus.maze.get_zeus_name_1()
zeus.maze.get_zeus_name_2(zeus)
zeus.get_name_1()
zeus.get_name_2(zeus)

输出:

can't be done!?
zeus
impossible!?
zeus
now external calls:
zeus
zeus
zeus
zeus

zeus实例化期间,如果__init__方法创建另一个类的实例maze,则此新实例无法访问其创建者对象zeus (除非self传递给它)。 (另外,如果__init__方法在其自己的类get_name_1中调用方法,则该方法也无法访问其对象属性(除非self传递给它)。)

但是,在对象实例化后,第二个对象maze现在可以识别并访问其创建者对象zeus

这种行为给我带来了一些困惑和困难,因为我正在编写一些代码,其中所有内容都已初始化并从__init__序列运行 - 现在我想最好避免这种情况......

我的问题:

  1. 为什么会这样?
  2. 通过__init__电话中的实例化,可以最大程度地避免出现问题吗?
  3. 还有进一步的设计含义吗?
  4. self传递给新实例,似乎可能因为自我引用而产生问题,是否也应避免这种情况?
  5. 其他有趣的含义/我错过了什么?
  6. 感谢您的帮助。

3 个答案:

答案 0 :(得分:5)

get_zeus_name_1()正在尝试访问全局变量zeus,该变量在调用get_zeus_name_1()时尚未初始化。

get_zeus_name_2()接受一个参数(scope)并访问它,这是有效的。它不会尝试访问全局变量zeus

get_name_1()get_name_2()相同的故事。

我认为关键是要了解python如何执行这一行:

zeus=Zeus()

这行告诉python:执行方法Zeus()(这是__init__(self)类中Zeus方法的另一个名称),然后将该方法返回的对象分配给全局变量zeus

Python首先执行方法Zeus()然后,之后,init方法完成执行并返回Zeus类的对象实例,python将该对象分配给全局变量zeus 。因此,在init方法完成之前,尚未定义全局变量zeus,因此get_name_1()和get_zeus_name_1()无法访问它。

get_name_2()和get_zeus_name_2()访问Zeus类的同一对象实例,因为get_name_1()和get_zeus_name_1()尝试访问,但是它们通过传递给它们的参数访问它,而不是通过全局变量访问它,所以他们没有遇到问题。

以下是一个更简单的示例,演示了完全相同的问题:

>>> class Foo:
...     def __init__(self):
...         self.name = 'foobar'
...         print self.name
...         print foo.name
... 
>>> foo = Foo()
foobar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __init__
NameError: global name 'foo' is not defined
>>> 

print self.name行工作正常(相当于get_name_2()和get_zeus_name_2()),但print foo.name行崩溃(相当于get_name_2()和get_zeus_name_2())。

答案 1 :(得分:4)

我认为这是因为Python本质 - 它“逐行”执行源代码。在这一行中:zeus=Zeus()您创建了一个对象,并调用了__init__方法。执行时,还没有全局zeus变量。我想你抓到了NameError:名字'zeus'没有定义(顺便说一句,抓住每个异常都是不好的做法except: ...;相反,你应该只捕获你期望的异常; except NameError: ...)。

你需要记住,在Python中,一切都在运行时发生。这里

x = 1 # x is just created, y 2 isn't created yet, you can't access it
y = 2 # y is just created, you can use it

当模块A导入模块B时,这也可能导致循环导入,而模块B导入模块A中的某些东西,但模块A尚未创建 - 错误。

在你的情况下,最好将原始Zeus对象的链接传递给Maze对象,就像在__init__中一样,并且只有在完全创建Zeus对象后才能从Maze访问它。

答案 2 :(得分:1)

让我看看我是否可以举一个例子来帮助他。

如果我们想与一群希腊神一起工作,我们需要从一个非常简单的GreekGod类开始:

class GreekGod(object):

    def __init__(self, name):
        self.name = name

zeus = GreekGod('Zeus')
athena = GreekGod('Athena')
print zeus.name

如果现在我们想要做更多的事情,例如在每个希腊神给他自己的epithet,我们需要在我们班上添加加词属性:

class GreekGod(object):

    def __init__(self, name, epithet):
        self.name = name
        self.epithet = epithet

    def get_catch_frase(self):
        return self.name + ' ' + self.epithet

zeus = GreekGod('Zeus', 'Father of the gods')
print zeus.get_catch_frase()  # -> Zeus Father of the gods

正如您所看到的,方法get_catch_frase返回一个字符串,不打印该字符串,当您调用方法“get_something”时会出现这种行为,因为人们会期望返回的内容,而不是其他内容。

我也希望你看到self的角色,它代表了类中的对象实例。