python如何在这段代码中向类添加atributes?

2024-09-27 00:19:01 发布

您现在位置:Python中文网/ 问答频道 /正文

python如何向类添加属性val1和val2。python是否在内部调用类似b1的东西。u共享_状态['val1']='Jaga Gola!!!'

# Borg (monostate pattern) lets a class have as many instances as one likes,
# but ensures that they all share the same state


class Borg:
    __shared_state = {}

    def __init__(self):
        self.__dict__ = self.__shared_state



b1 = Borg()
b2 = Borg()

print(b1 == b2)
b1.val1 = 'Jaga Gola!!!'
b1.val2 = 'BOOOM!!!'
print(b2.val1, b1.val2)

为什么如果我删除了_shared_state and self.__dict__ = self.__shared_state,我就不能向类添加属性并得到错误:AttributeError:'Borg'对象没有属性'val1'

class Borg:

    def __init__(self):
        pass



b1 = Borg()
b2 = Borg()

print(b1 == b2)
b1.val1 = 'Jaga Gola!!!'
b1.val2 = 'BOOOM!!!'
print(b2.val1, b1.val2)

Tags: self属性defasb2borgclassb1
2条回答

你在那里做的事情很有趣,它基于可变性:

声明的初始__shared_state是在执行任何代码之前创建的。该字典称为Class属性,因为它链接到类,而不是实例(不使用self进行声明)。这意味着__shared_stateb1b2之间共享,因为它是在它们之前创建的,并且因为它是一个dict,所以它是可变的

什么是可变的

这意味着分配给两个不同实例的一个字典将引用相同的内存地址,即使我们更改了听写,内存地址也将保持不变。这是一个探针:

class Example:

    __shared_state = {1: 1}

    def __init__(self):
        self.__dict__ = self.__shared_state
        print(self.__shared_state)

ex1 = Example()
ex2 = Example()

print(id(ex1.__dict__), id(ex2.__dict__))

# Prints
# {1: 1}
# {1: 1}
# 140704387518944 140704387518944

注意到他们是如何拥有相同的id的吗?这是因为它们引用的是同一个对象,而且dictionary类型是可变的,在一个对象中更改字典意味着您正在更改这两个对象的字典,因为它们是相同的:

# Executing this
ex1.val1 = 2
# Equals this
ex1.__dict__['val1'] = 2
# Which also equals this
Example.__shared_state['val1'] = 2 

这不会发生在不可变的整数上:

class Example:

    __shared_state = 2

    def __init__(self):
        self.a = self.__shared_state
        print(self.__shared_state)

ex1 = Example()
ex2 = Example()

ex2.a = 3

print(id(ex1.a), id(ex2.a))
# Prints
# 2
# 2
# 9302176 9302208
# Notice that once we change ex2.a, its ID changes!

当你删除你的__shared_state时,当你分配b1.val1 = 'Jaga Gola!!!'b1.val2 = 'BOOOM!!!'时,它只是从b1分配给字典,这就是为什么当你试图打印b2.val1b2.val2时会出现错误

在此代码中:

class Borg:
    __shared_state = {}

    def __init__(self):
        self.__dict__ = self.__shared_state
  • __shared_state = {}行出现在类级别,因此它只添加一次到类Borg,而不是添加到类型为Borg的每个单独对象。这与之后写Borg.__shared_state = {}是一样的
  • self.__dict__ = self.__shared_state令人困惑,因为它使用了self.两次,但效果非常不同:
    • 赋值给self.something时,在对象self中设置该something。这并不奇怪
    • 但是当self.something读取时,首先在self对象中查找something,如果在那里找不到,则在对象的类中查找。这种想法听起来很奇怪,但实际上你一直在使用它:这就是方法通常的工作方式。例如,在s = "foo"; b = s.startswith("f")中,对象s没有属性startswith,但是它的类str有属性,这就是调用该方法时使用的

这一行:

b1.val1 = 'Jaga Gola!!!'

最终转化为:

b1.__dict__['val1'] = 'Jaga Gola!!!'

但是我们知道b1.__dict__等于Borg.__shared_state,所以它被分配给它。然后:

print(b2.val1, ...

翻译为:

print(b2.__dict__['val1'])

我们再次知道b2.__dict__等于相同的{},因此val1被发现

如果您在开始时删除了关于__shared_state的内容,那么b1b2将获得它们自己的__dict__对象,因此将val1放入b1的dict中对b2没有影响,这就是您所提到的错误的原因


这对于理解正在发生的事情来说是很好的,但是您应该意识到这段代码并不能保证工作,并且可能会在Python的未来版本或其他实现(如PyPy)中崩溃。Python documentation for ^{}将其描述为“只读属性”,因此根本不应该为其赋值。不要在其他人可能运行的代码中执行此操作

事实上,认为a.foo就是a.__dict__['foo']的想法是一个巨大的简化。首先,我们已经遇到了这样的情况:有时在阅读时,它后面跟着a.__class__.__dict__['foo']。另一个例子是a.__dict__显然不是a.__dict__['__dict__'],否则它将如何结束!?这个过程有些复杂,在the Data Model docs中有记录

获得此行为的支持方式是使用特殊的__setattr____getattr__方法(也在这些数据模型文档中描述),如下所示:

class Borg:
    __shared_state = {}

    def __getattr__(self, name):
        try:
            return Borg.__shared_state[name]
        except KeyError:
            raise AttributeError

    def __setattr__(self, name, value):
        Borg.__shared_state[name] = value

相关问题 更多 >

    热门问题