python类实例变量和类变量

python class instance variables and class variables

我在理解类/实例变量如何在Python中工作时遇到了一个问题。我不明白为什么当我尝试这个代码时,列表变量似乎是一个类变量

1
2
3
4
5
6
7
8
9
10
class testClass():
    list = []
    def __init__(self):
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

输出:

1
2
['thing']
['thing', 'thing']

当我这样做的时候,它似乎是一个实例变量

1
2
3
4
5
6
7
8
9
10
class testClass():
    def __init__(self):
        self.list = []
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

输出:

1
2
['thing']
['thing']


这是因为python使用.解析名称的方式。在编写self.list时,python运行时首先尝试通过在实例对象中查找来解析list名称,如果在实例对象中找不到该名称,则在类实例中查找。

让我们一步一步地研究一下

1
self.list.append(1)
  • 对象self中是否有list名称?
    • 是的,用它!完成。
    • 不:2点。
  • 对象self的类实例中是否有list名称?
    • 是的,用它!完成
    • 不:错误!
  • 但是当你绑定一个名字时,事情就不同了:

    1
    self.list = []
  • 对象self中是否有list名称?
    • 是:覆盖它!
    • 不,把它绑起来!
  • 所以,这总是一个实例变量。

    第一个示例在类实例中创建一个list,因为这是当时的活动范围(任何地方都没有self)。但第二个示例在self的范围内显式创建了一个list

    更有趣的例子是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class testClass():
        list = ['foo']
        def __init__(self):
            self.list = []
            self.list.append('thing')

    x = testClass()
    print x.list
    print testClass.list
    del x.list
    print x.list

    将打印:

    1
    2
    3
    ['thing']
    ['foo']
    ['foo']

    删除实例名时,类名通过self引用可见。


    关于查找名称,python有一些有趣的规则。如果您真的想改变主意,请尝试以下代码:

    1
    2
    3
    4
    class testClass():
        l = []
        def __init__(self):
            self.l = ['fred']

    这将为每个实例提供一个名为l的变量,该变量将屏蔽类变量l。如果您执行self.__class__.l,您仍然可以获得类变量。

    我是这样想的…每当您执行instance.variable时(即使对于方法名,它们只是变量,值恰好是函数),它都会在实例的字典中查找它。如果在那里找不到它,它会试图在实例的类字典中查找它。只有当变量被"读取"时,才会出现这种情况。如果它被分配给,它总是在实例字典中创建一个新条目。


    在第一个示例中,list是类的一个属性,由类的所有实例共享。这意味着您甚至可以在没有testClass类型的对象的情况下访问它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> class testClass():
    ...     list = []
    ...     def __init__(self):
    ...             self.list.append("thing")
    ...
    >>> testClass.list
    []
    >>> testClass.list.append(1)
    >>> testClass.list
    [1]

    但所有对象都与类和其他对象共享list属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> testObject = testClass()
    >>> testObject.list
    [1, 'thing']
    >>> testClass.list
    [1, 'thing']
    >>>
    >>> testObject2 = testClass()
    >>> testClass.list
    [1, 'thing', 'thing']
    >>> testObject2.list
    [1, 'thing', 'thing']

    实例化类时,将自动执行__init__方法。

    在第一种情况下,您的列表是一个类属性,由它的所有实例共享。你得到了两个"东西",因为你在实例化EDOCX1时附加了一个(15),在实例化EDOCX1时附加了另一个(第一个已经在第一个调用中附加了)。