代码之家  ›  专栏  ›  技术社区  ›  Exa

python中的\uu slot\uuuu描述符是如何工作的?

  •  4
  • Exa  · 技术社区  · 7 年前

    我知道什么 __slots__ 以及它的用途。

    然而,我还没有找到一个全面的答案来解释 member 使用创建的描述符 __插槽__ 作品

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

    有没有一种方法可以在不直接访问描述符的情况下更改这些值?
    (例如,上课时 C __dict__ 你可以做到 C.__dict__['key'] 而不是 C.key )

    可以“扩展”定义对象的不变性吗 __插槽__ 通过创建类似的类级别描述符?以及进一步的阐述;可以使用元类而不是定义来构建不可变对象吗 __插槽__ 明确地通过手动创建所述描述符?

    1 回复  |  直到 6 年前
        1
  •  4
  •   jsbueno    6 年前

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

    插槽描述符,在Python中以名称显示 member_descriptor 定义如下: https://github.com/python/cpython/blob/master/Objects/descrobject.c

    如果不使用CTypes与本机代码交互,就无法从纯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__]
    
    推荐文章