为什么没有这个类,对象仍能正常工作

时间:2013-09-18 10:06:24

标签: python

我是Python的新手。在阅读了 Python Tutorial Release 2.7.5 的一些章节之后,我对Python范围和命名空间感到困惑。这个问题可能会重复,因为我不知道要搜索什么。

我创建了一个类和一个实例。然后我使用del删除了该课程。但实例仍然正常。为什么?

>>>class MyClass:    # define a class
...    def greet(self):
...        print 'hello'
...
>>>instan = MyClass()    # create an instantiation
>>>instan
<__main__.MyClass instance at 0x00BBCDC8>
>>>instan.greet()
hello
>>>dir()
['instan', 'MyClass', '__builtins__', '__doc__', '__name__', '__package__']
>>>
>>>
>>>del MyClass
>>>dir()
['instan', '__builtins__', '__doc__', '__name__', '__package__']
>>>instan
<__main__.MyClass instance at 0x00BBCDC8>    # Myclass doesn't exist!
>>>instan.greet()
hello  

我对OOP知之甚少,所以这个问题看似简单。提前谢谢。

3 个答案:

答案 0 :(得分:9)

Python是一种garbage collected语言。当你执行del MyClass时,实际上并没有删除'类对象'(类也是对象),但是你只从当前命名空间中删除'name'MyClass,这是某种参考到类对象。任何对象只要被某些东西引用就会保持活着状态。由于实例引用了自己的类,只要至少有一个实例存活,该类就会保持活动状态。

要注意的一件事是当你重新定义一个类时(例如在命令行上):

In [1]: class C(object):
   ...:     def hello(self):
   ...:         print 'I am an instance of the old class'
In [2]: c = C()
In [3]: c.hello()
I am an instance of the old class
In [4]: class C(object):  # define new class and point C to it
   ...:     def hello(self):
   ...:         print 'I am an instance of the new class'
In [5]: c.hello()  # the old object does not magically become a new one
I am an instance of the old class
In [6]: c = C()  # point c to new object, old class and object are now garbage
In [7]: c.hello()
I am an instance of the new class

旧类的任何现有实例都将继续具有旧行为,考虑到我提到的内容,这种行为是有意义的。名称空间和对象之间的关系对于python来说有点特殊,但是一旦得到它就不那么难了。给出了一个很好的解释here

答案 1 :(得分:3)

使用del删除变量时,删除变量名称以及您对变量中对象的引用,而不是对象本身。

您创建的对象仍包含自己对该类的引用。通常,只要有人仍然拥有对任何对象的引用(包括类定义),它就不会被垃圾收集器删除。

答案 2 :(得分:1)

Python不会在变量中存储值,而是为对象分配名称。 locals()函数将返回当前命名空间中的所有名称(或更具体地说,当前范围)。让我们开始一个新的翻译会话,看看locals()会给我们什么。

>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}

命名空间中当前唯一的名称是以Python在启动时放置的名称构建的。这里有一个快速的单行代码,只向我们展示我们分配的名称:

>>> {k:v for k,v, in locals().iteritems() if k[0] != '_'}
{}

那更好。不要担心单线程如何工作,让我们继续创建一个类。

>>> class C(object):
        greeting = "I'm the first class"

当我们定义一个类时,它的名称在当前范围的位置:

>>> {k:v for k,v, in locals().iteritems() if k[0] != '_'}
{'C': <class '__main__.C'>}

这部分是Python的方式,说有一个太大而无法打印出来的对象,但它是我们定义的类对象。让我们看一下我们的类对象存储在的内存地址。我们可以使用id()函数来查找。

>>> id(C)
18968856

id()返回的数字是参数的内存位置。如果您自己运行这些命令,您将看到不同的数字,但在单个会话期间该数字不会更改。

>>> id(C)
18968856

现在让我们创建一个实例。

>>> c = C()
>>> c.greeting
"I'm the first class"

现在,当我们查看locals()时,我们可以看到我们的类对象和实例对象。

>>> {k:v for k,v, in locals().iteritems() if k[0] != '_'}
{'C': <class '__main__.C'>, 'c': <__main__.C object at 0x011BDED0>}

每个实例对象都有一个特殊成员__class__,它是对该实例为实例的类对象的引用。

>>> c.__class__
<class '__main__.C'>

如果我们在该变量上调用id(),我们可以看到它是对我们刚刚定义的类C的引用:

>>> id(c.__class__)
18968856
>>> id(c.__class__) == id(C)
True

现在让我们从本地命名空间中删除名称C

>>> del C
>>> {k:v for k,v, in locals().iteritems() if k[0] != '_'}
{'c': <__main__.C object at 0x011BDED0>}
>>> C
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    C
NameError: name 'C' is not defined

这正是我们所期望的。名称C不再分配给任何内容。但是,我们的实例仍然引用了类对象。

>>> c.__class__
<class '__main__.C'>
>>> id(c.__class__)
18968856

如您所见,该类仍然存在,您无法通过本地名称空间中的名称C来引用它。

让我们创建一个名为C的第二个类。

>>> class C(object):
    greeting = "I'm the second class"
>>> {k:v for k,v, in locals().iteritems() if k[0] != '_'}
{'C': <class '__main__.C'>, 'c': <__main__.C object at 0x011BDED0>}

如果我们创建第二个类的实例,它的行为就像你注意到的那样:

>>> c2 = C()
>>> c2.greeting
"I'm the second class"
>>> c.greeting
"I'm the first class"

要了解原因,让我们看一下这个新课程的id。我们可以看到新的类对象存储在与第一个对象不同的位置。

>>> id(C)
19011568
>>> id(C) == id(C.__class__)
False

这就是实例仍能正常工作的原因:两个类对象仍然是单独存在的,每个实例都包含对其对象的引用。