Python初始化类中的列表

Tig*_*.ru 2 python class

我不了解python的行为,我有很多相同的错误:

文件“ ./start.py”,第2行,在print2
item.print2()中
RuntimeError:超过最大递归深度

我的代码如下:

#!/usr/bin/python

class Component:
    def print2(self):
        pass

class Composite(Component):
    items = []
    name = 'composite'

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

    def add(self, item):
        self.items.append(item)

    def print2(self):
        if self.items:
            for item in self.items:
                item.print2()
        else:
            print self.name

class Leaf(Component):
    name = 'leaf'

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

    def print2(self):
        print self.name

category = Composite('category1')
leaf = Composite('leaf1')
category.add(leaf)
leaf = Leaf('leaf2')
category.add(leaf)

category.print2()
Run Code Online (Sandbox Code Playgroud)

当我添加self.items = []构造函数(__init__)时,它可以正常工作。您能解释一下这种行为吗?

0x5*_*453 5

items是一个类变量,因此由的每个实例共享Composite。当您append进入时items,所有实例都会获得该项目。因此,在添加leafs之后,您将:

   Type                    Composite.items
------------------------------------------
 Composite    category1    [leaf1, leaf2]
                  |
 Composite      leaf1      [leaf1, leaf2]
                  |
      Leaf      leaf2
Run Code Online (Sandbox Code Playgroud)

如果print2被调用leaf1,它将尝试调用该类中的print2每一个,items该类leaf1一次又一次地无限次递归。

如果改为self.items = []在内部初始化__init__,它将是实例变量而不是类变量,因此每个变量Composite都有其自己的列表items


jua*_*aga 5

因为您的所有对象都共享项目……这是一个类变量,即“静态”

class Foo:
    # <anything assigned here is static>
    static_var1 = 'foo'
    def some_method(self, bar):
        self.x = bar # this is an *instance* variable
Run Code Online (Sandbox Code Playgroud)

所以在:

def print2(self):
    if self.items:
        for item in self.items:
            item.print2()
Run Code Online (Sandbox Code Playgroud)

迭代static items。注意:

In [11]: category.items
Out[11]: [<__main__.Composite at 0x103078978>, <__main__.Leaf at 0x103078ac8>]

In [12]: category.items[0].name
Out[12]: 'leaf1'
Run Code Online (Sandbox Code Playgroud)

当您点击第一个项目时,它将调用item.print2(),然后开始遍历items,然后'leaf1'再次点击,从而再次调用item.print2()...

注意,Python的对象模型非常简单,instance.method()等效于InstanceClass.method(instance),即方法中发生的唯一“魔术”是,如果通过实例访问方法,则实际返回的是bound方法,该方法将实例绑定为该函数的第一个参数。因此请注意:

In [15]: class Foo:
    ...:     def mutate(self, x):
    ...:         self.foo = x
    ...:

In [16]: f = Foo()

In [17]: f.mutate(42)

In [18]: f.foo
Out[18]: 42

In [19]: Foo.mutate(f, 99)

In [20]: f.foo
Out[20]: 99
Run Code Online (Sandbox Code Playgroud)

注意,Foo.mutate只是一个函数。它不在乎self实际上是什么:

In [21]: class Bar:
    ...:     pass
    ...:

In [22]: b = Bar()

In [23]: Foo.mutate(b, 'foooooo')

In [24]: b.foo
Out[24]: 'foooooo'
Run Code Online (Sandbox Code Playgroud)

有点不可思议,每次访问方法时,都会创建一个全新的绑定方法对象:

In [34]: f.mutate
Out[34]: <bound method Foo.mutate of <__main__.Foo object at 0x1030641d0>>

In [35]: methods = f.mutate, f.mutate, f.mutate

In [36]: [hex(id(m)) for m in methods]
Out[36]: ['0x10581de08', '0x10581d208', '0x105abc988']
Run Code Online (Sandbox Code Playgroud)

这导致看似奇怪:

In [37]: f.mutate is f.mutate
Out[37]: False
Run Code Online (Sandbox Code Playgroud)

但是,它包装了相同的基础函数,Foo.mutate该函数不变,并且仅仅是类中的任何其他属性:

In [38]: Foo.mutate is Foo.mutate
Out[38]: True
Run Code Online (Sandbox Code Playgroud)