__slot__描述符如何在python中工作?

时间:2017-12-27 13:39:03

标签: python immutability metaclass slots python-descriptors

我知道__slots__做了什么以及它应该用于什么。

但是,我没有找到关于如何使用member创建的__slots__描述符的底层机制有效的全面答案。

实际存储的对象级值在哪里?

有没有办法在没有直接属性访问描述符的情况下更改这些值? (例如,当课程C__dict__时,您可以C.__dict__['key']代替C.key

可以通过创建类似的类级描述符来“扩展”定义__slots__的对象的不变性吗?并作为进一步阐述;可以使用元类构建不可变对象,但不能通过手动创建所述描述符来显式定义__slots__吗?

1 个答案:

答案 0 :(得分:2)

__slot__属性在对象的本机内存表示中分配,然后与访问的类关联的描述符实际上使用CPython中的本机C方法来设置和检索对每个属性的Python对象的引用作为C结构的类实例上的slot属性。

在Python中使用名称为member_descriptor的插槽描述符在此处定义:https://github.com/python/cpython/blob/master/Objects/descrobject.c

在不使用CType与本机代码交互的情况下,无论如何都无法从纯Python代码执行或增强这些描述符。

可以通过执行类似

的操作来获取其类型
class A:
   __slots__ = "a"

member_descriptor = type(A.a)

然后可以说它可以从它继承,并编写可以做等级的派生__get____set__方法 - 但不幸的是,它不会作为基础工作类。

但是,可以编写其他并行描述符,这些描述符又可以调用本机描述符来实际存储值。 通过使用元类,可以在类创建时重命名传入的__slots__并将其访问权限包装在可执行额外检查的自定义描述符中 - 甚至可以隐藏“dir”。

因此,对于一个天真的类型检查槽变体元类,可以有

class TypedSlot:
    def __init__(self, name, type_):
        self.name = name
        self.type = type_

    def __get__(self, instance, owner):
        if not instance:
            return self
        return getattr(instance, "_" + self.name)

    def __set__(self, instance, value):
        if not isinstance(value, self.type):
            raise TypeError
        setattr(instance, "_" + self.name, value)


class M(type):
    def __new__(metacls, name, bases, namespace):
        new_slots = []
        for key, type_ in namespace.get("__slots__", {}).items():
            namespace[key] = TypedSlot(key, type_)
            new_slots.append("_" + key)
        namespace["__slots__"] = new_slots
        return super().__new__(metacls, name, bases, namespace)

    def __dir__(cls):
        return [name for name in super().__dir__() if  name not in cls.__slots__]