Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何动态更改_插槽_属性?_Python_Python 3.x_Slots - Fatal编程技术网

Python 如何动态更改_插槽_属性?

Python 如何动态更改_插槽_属性?,python,python-3.x,slots,Python,Python 3.x,Slots,假设我有一个带有\uuuuu插槽的类 class A: __slots__ = ['x'] a = A() a.x = 1 # works fine a.y = 1 # AttributeError (as expected) 现在我要更改A的插槽 A.__slots__.append('y') print(A.__slots__) # ['x', 'y'] b = A() b.x = 1 # OK b.y = 1 # AttributeError (why?)

假设我有一个带有
\uuuuu插槽的类

class A:
    __slots__ = ['x']

a = A()
a.x = 1   # works fine
a.y = 1   # AttributeError (as expected)
现在我要更改
A
插槽

A.__slots__.append('y')
print(A.__slots__)   # ['x', 'y']
b = A()
b.x = 1   # OK
b.y = 1   # AttributeError (why?)
b
是在
A
\uuuuuuuuuuuuuuuuuuu
更改后创建的,因此原则上Python可以为
b.y
分配内存。为什么没有

如何正确地修改一个类的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,以使新实例具有修改后的属性?

我觉得。那好吧。因为在这一切之下,python正在寻找一个
元组
,所以没有办法对它进行变异。事实上,我甚至不确定您是否可以访问它,除非您首先向实例传递一个元组

您设置的原始对象仍然作为类型的一个属性保留(也许)只是一种方便的内省

你不能修改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

当然,您始终可以使用子类来扩展插槽:

>>> class C(A):
...   __slots__ = ['z']
... 
>>> c = C()
>>> c.x = 1
>>> c.z = 1

在创建类之后,不能动态更改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。从:

\uu\u插槽
通过为每个变量名创建描述符(实现描述符)在类级别实现。因此,类属性不能用于设置由
定义的实例变量的默认值;否则,class属性将覆盖描述符赋值

您可以在类
中看到描述符

>>> class A:
...     __slots__ = ['x']
... 
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ['x']})
>>> A.__dict__['x']
<member 'x' of 'A' objects>
>>> a = A()
>>> A.__dict__['x'].__get__(a, A)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: x
>>> A.__dict__['x'].__set__(a, 'foobar')
>>> A.__dict__['x'].__get__(a, A)
'foobar'
>>> a.x
'foobar'
Python开发人员在这里所做的只是扩展系统,使用任意名称添加更多的此类插槽,这些名称取自所创建类的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>属性,以便节省内存;字典比插槽中的值的简单引用占用更多内存。通过指定
\uuuu插槽
可以禁用
\uuuuu指令
\uuuuuuuukref\uuuuu
插槽,除非明确将这些插槽包括在
\uuuuu插槽
序列中

扩展槽的唯一方法是子类;您可以使用
type()
函数或使用工厂函数动态创建子类:

def extra_slots_subclass(base, *slots):
    class ExtraSlots(base):
        __slots__ = slots
    ExtraSlots.__name__ = base.__name__
    return ExtraSlots

创建类后,您不能修改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu
属性。这是因为它会导致奇怪的行为

想象一下下面的情景

class A:
    __slots__ = ["x"]

a = A()
A.__slots__.append("y")
a.y = None 
在这种情况下会发生什么?最初没有为第二个插槽分配空间,但根据插槽属性,
a
应该能够为
y
分配空间

\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
并不是为了保护可以访问和不能访问的名称。相反,
\uuuuuuu插槽
是关于减少对象的内存占用。通过尝试修改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

插槽如何减少内存占用
通常,对象的属性存储在
dict
中,这本身就需要相当多的内存。如果要创建数百万个对象,则这些命令所需的空间将变得不可接受
\uuuu slots\uuuu
通知生成类对象的python机制,该类的实例引用的属性将非常多,属性的名称将是什么。因此,该类可以通过将属性直接存储在实例上而不是存储在
dict
中来进行优化。它将属性(指向属性的指针)的内存直接放在对象上,而不是为对象创建一个新的
dict

将这些问题的答案放在一起,我想强调一下这个问题的解决方案:

您可以通过创建具有相同名称的子类,然后用其子类替换父类来修改
\uuuuuuuuuuu slots\uuuuuuu
。请注意,您可以对在任何模块中声明和使用的类执行此操作,而不仅仅是您的类


考虑以下模块,该模块声明了一些类:

>>> import module
>>> from module import A
>>>
>>> # for classes imported into your module:
>>> A = type('A', (A,), {'__slots__': ('foo',)})
>>> # for classes which will be instantiated by the `module` itself:
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> a = A()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
模块.py:
class A(object):
    # some class a user should import
    __slots__ = ('x', 'b')

    def __init__(self):
        self.b = B()

class B(object):
    # let's suppose we can't use it directly,
    # it's returned as a part of another class
    __slots__ = ('z',)
from module import A

def get_instance():
    return A()
以下是如何向这些类添加属性:

>>> import module
>>> from module import A
>>>
>>> # for classes imported into your module:
>>> A = type('A', (A,), {'__slots__': ('foo',)})
>>> # for classes which will be instantiated by the `module` itself:
>>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
>>>
>>> a = A()
>>> a.x = 1
>>> a.foo = 2
>>>
>>> b = a.b
>>> b.z = 3
>>> b.bar = 4
>>>
但是,如果您使用
模块
从某个第三方模块接收类实例,该怎么办

模块第三方。py:
class A(object):
    # some class a user should import
    __slots__ = ('x', 'b')

    def __init__(self):
        self.b = B()

class B(object):
    # let's suppose we can't use it directly,
    # it's returned as a part of another class
    __slots__ = ('z',)
from module import A

def get_instance():
    return A()
没问题,它也会工作的!唯一的区别是,您可能需要在导入第三方模块之前对它们进行修补(如果它从
模块导入类
):


这是因为Python只导入一次模块,然后在所有其他模块之间共享它们,所以您对模块所做的更改会影响您的模块中运行的所有代码。

当然,请在这里闲逛,践踏我的答案;-)+1但是对于有描述符的有趣演示。@mgilson::-P您的在这张图片中同样有效,所以我已经投票支持您了。谢谢您的投票:-)。我开始怀疑我是否在杂草丛中,是否应该删除我的答案。。。不管怎样,最近我一直在寻找机会去挖掘C代码,因为,为什么不呢?在玩了几年描述符之后,我开始把一个描述符看作是它自己的dict,例如:
def\uu get\uuuu(dsc,obj,cls=None):如果cls不是其他的,则返回dsc。Map[obj]
其中
Map
将是描述符实例上的dict,其中包含与obj实例关联的值。。。这让我怀疑描述符是否真的能节省内存。@Tcll你把插槽和描述符搞混了。插槽是描述符的一种特殊形式。带有插槽的对象有点像一个可变的命名元组,其中属性名只是获取给定索引处元素的方便方法。插槽描述符是在C级实现的,基本上只是
*(self+att)