7

我是 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 知之甚少,所以这个问题可能看起来很简单。提前致谢。

4

3 回答 3

9

Python 是一种垃圾收集语言。当您这样做时del MyClass,您实际上并没有删除“类对象”(类也是对象),而只是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 来说有点特殊,但是一旦你得到它就不是那么难了。这里给出了一个很好的解释。

于 2013-09-18T10:08:16.020 回答
3

使用 删除变量时del,删除的是变量名和您自己对变量中对象的引用,而不是对象本身。

您创建的对象仍然包含它自己对类的引用。一般来说,只要有人仍然持有对任何对象(包括类定义)的引用,垃圾收集器就不会删除它。

于 2013-09-18T10:09:31.060 回答
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

这就是实例仍然可以正常工作的原因:两个类对象仍然单独存在,并且每个实例都持有对其对象的引用。

于 2013-09-18T12:36:18.153 回答