我是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知之甚少,所以这个问题看似简单。提前谢谢。
答案 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
这就是实例仍能正常工作的原因:两个类对象仍然是单独存在的,每个实例都包含对其对象的引用。