Python 是否可以在已经定义了uuu插槽的类的装饰器中添加uuu插槽uuuuu?

Python 是否可以在已经定义了uuu插槽的类的装饰器中添加uuu插槽uuuuu?,python,metaclass,slots,Python,Metaclass,Slots,首先,我要说的是,我理解槽和元类在Python中是如何工作的。和这两个人一起玩,我遇到了一个有趣的问题。下面是一个简单的例子: def decorator(cls): dct = dict(cls.__dict__) dct['__slots__'] = ('y',) return type('NewClass', cls.__bases__, dct) @decorator class A(object): __slots__= ('x',) def

首先,我要说的是,我理解槽和元类在Python中是如何工作的。和这两个人一起玩,我遇到了一个有趣的问题。下面是一个简单的例子:

def decorator(cls):
    dct = dict(cls.__dict__)
    dct['__slots__'] = ('y',)
    return type('NewClass', cls.__bases__, dct)

@decorator
class A(object):
    __slots__= ('x',)
    def __init__(self):
        self.x = 'xx'

A()
这将产生以下异常:

Traceback (most recent call last):
  File "p.py", line 12, in <module>
    A()
  File "p.py", line 10, in __init__
    self.x = 'xx'
TypeError: descriptor 'x' for 'A' objects doesn't apply to 'NewClass' object
但是这些解决方案与原来的不完全相同,因为它们都添加了一个基类。他们可能在更复杂的设置中失败。例如,如果继承树更复杂,您可能会遇到以下异常:
TypeError:multiple base具有实例布局冲突

因此,我的具体问题是:

有没有办法通过调用
type
来创建一个新类,该类修改现有类的
\uuuu\uuuuu
属性,但不将现有类添加为新类的基类

编辑:

我知道严格元类是我上面例子的另一个解决方法。有很多方法可以使最少的示例工作,但我的问题是通过
new
创建一个基于现有类的类,而不是如何使示例工作。很抱歉给你带来了困惑

编辑2:

评论中的讨论让我提出了一个比我最初提出的问题更精确的问题:

是否可以通过调用
type
,创建一个使用现有类的插槽和描述符而不是该类后代的类?


如果答案是“否”,我希望能得到一个关于为什么不这样做的消息来源。

否,不幸的是,在类创建之后(也就是在类上调用装饰器的时候),无法对
\uuuuuuuuuuuuuuu>进行任何处理。唯一的方法是使用元类,并在调用
类型之前修改/添加
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

此类元类的一个示例:

class MetaA(type):
    def __new__(mcls, name, bases, dct):
        slots = set(dct.get('__slots__', ()))
        slots.add('y')
        dct['__slots__'] = tuple(slots)
        return super().__new__(mcls, name, bases, dct)

class BaseA(metaclass=MetaA):
    pass

class A(BaseA):
    __slots__ = ('x',)

    def __init__(self):
        self.x = 1
        self.y = 2

print(A().x, A().y)
如果没有元类,您可以执行一些魔术,从定义的类中复制所有内容,并动态创建一个新类,但代码有异味;)

这种代码的主要缺点是,如果有人在你的装饰器之前应用了另一个装饰器,并且,比如说,在某个地方创建了对装饰类的引用,那么他们最终将存储对另一个类的引用。元类也是如此——它们将执行两次。因此,元类方法更好,因为没有副作用


创建类后,为什么不能真正更改
\uuuuu\uuuu\uuuu
,最终答案取决于您正在使用的python解释器的实现细节。例如,在CPython中,对于您定义的每个插槽,类都有一个描述符(请参见CPython源代码中的
PyMemberDescr\u Type
&
PyMemberDef
struct),该描述符具有插槽值在内部对象存储中对齐位置的偏移参数。在公共Python API中,您根本没有处理这些事情的工具。您可以用灵活性换取更少的内存使用(同样,在CPython中,就像在PyPy中一样,您可以为所有类自动获得相同的内存效果)


如果绝对需要修改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu唯一的方法是使用元类,在调用
类型之前修改/添加
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

此类元类的一个示例:

class MetaA(type):
    def __new__(mcls, name, bases, dct):
        slots = set(dct.get('__slots__', ()))
        slots.add('y')
        dct['__slots__'] = tuple(slots)
        return super().__new__(mcls, name, bases, dct)

class BaseA(metaclass=MetaA):
    pass

class A(BaseA):
    __slots__ = ('x',)

    def __init__(self):
        self.x = 1
        self.y = 2

print(A().x, A().y)
如果没有元类,您可以执行一些魔术,从定义的类中复制所有内容,并动态创建一个新类,但代码有异味;)

这种代码的主要缺点是,如果有人在你的装饰器之前应用了另一个装饰器,并且,比如说,在某个地方创建了对装饰类的引用,那么他们最终将存储对另一个类的引用。元类也是如此——它们将执行两次。因此,元类方法更好,因为没有副作用


创建类后,为什么不能真正更改
\uuuuu\uuuu\uuuu
,最终答案取决于您正在使用的python解释器的实现细节。例如,在CPython中,对于您定义的每个插槽,类都有一个描述符(请参见CPython源代码中的
PyMemberDescr\u Type
&
PyMemberDef
struct),该描述符具有插槽值在内部对象存储中对齐位置的偏移参数。在公共Python API中,您根本没有处理这些事情的工具。您可以用灵活性换取更少的内存使用(同样,在CPython中,就像在PyPy中一样,您可以为所有类自动获得相同的内存效果)


如果绝对需要修改
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

class MetaSlot(type):
    def __new__(mcs, name, bases, dic):
        dic['__slots__'] += ('y',)
        return type.__new__(mcs, name, bases, dic)


class C(metaclass=MetaSlot):  # Python 3 syntax
    __slots__ = ('x',)
现在可以使用
x
y

>>> c = C()
>>> c.y = 10
>>> c.x = 10

您可以使用元类实现这一点:

class MetaSlot(type):
    def __new__(mcs, name, bases, dic):
        dic['__slots__'] += ('y',)
        return type.__new__(mcs, name, bases, dic)


class C(metaclass=MetaSlot):  # Python 3 syntax
    __slots__ = ('x',)
现在可以使用
x
y

>>> c = C()
>>> c.y = 10
>>> c.x = 10

当decorator执行时,类已经被创建。你应该定义一个元类来实现这一点。你希望
x
槽仍然有效吗?是的,我希望
x
y
都能在生成的类中工作。你的问题标题仍然是“是否可以在类的装饰器中添加
\uuuuuuuuuuuu槽”,那么它必须通过装饰器来完成吗?我已经更新了我的答案,并更具体地回答了您的问题,请看一看。当装饰器执行时,类已经创建。你应该定义一个元类来实现这一点。你希望
x
槽仍然有效吗?是的,我希望
x
y
都能在生成的类中工作。你问标题仍然从“是吗?”