首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在这段代码中,python是如何向class添加属性的?

在这段代码中,python是如何向class添加属性的?
EN

Stack Overflow用户
提问于 2020-04-17 19:09:05
回答 2查看 65关注 0票数 1

python如何将属性val1和val2添加到类中。python内部是否会调用b1.__shared_state'val1‘= 'Jaga Gola!’之类的东西?

代码语言:javascript
复制
# 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)

为什么如果我删除了val1,我不能向类添加属性,就会得到错误: AttributeError:'Borg‘对象没有'val1’属性?

代码语言:javascript
复制
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)
EN

回答 2

Stack Overflow用户

发布于 2020-04-17 19:19:02

在此代码中:

代码语言:javascript
复制
class Borg:
    __shared_state = {}

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

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

这一行:

代码语言:javascript
复制
b1.val1 = 'Jaga Gola!!!'

最终转换为:

代码语言:javascript
复制
b1.__dict__['val1'] = 'Jaga Gola!!!'

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

代码语言:javascript
复制
print(b2.val1, ...

翻译为:

代码语言:javascript
复制
print(b2.__dict__['val1'])

再一次,我们知道b2.__dict__等于 Borg.__shared_state,所以找到了val1

如果去掉开头关于__shared_state的内容,那么b1b2就会得到自己的__dict__对象,所以将val1放入b1的字典中不会对b2产生任何影响,这就是如何得到您提到的错误的。

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

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

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

代码语言:javascript
复制
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
票数 2
EN

Stack Overflow用户

发布于 2020-04-17 19:27:20

这是一件有趣的事情,你正在做什么,它是基于可变性的:

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

它是可变的是什么意思?

这意味着一个字典分配给两个不同的实例,将引用相同的内存地址,即使我们改变独占,内存地址也将保持不变。下面是一个探测器:

代码语言:javascript
复制
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类型是可变的,所以在一个对象中更改字典意味着您要为这两个对象更改字典,因为它们是相同的:

代码语言:javascript
复制
# Executing this
ex1.val1 = 2
# Equals this
ex1.__dict__['val1'] = 2
# Which also equals this
Example.__shared_state['val1'] = 2 

整数不会发生这种情况,因为整数是不可变的

代码语言:javascript
复制
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时,它会引发错误。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61270179

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档