首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何强制子类拥有__slots__?

如何强制子类拥有__slots__?
EN

Stack Overflow用户
提问于 2019-06-13 11:22:46
回答 2查看 883关注 0票数 3

我有一个__slots__的类

代码语言:javascript
复制
class A:
    __slots__ = ('foo',)

如果我创建一个子类而不指定__slots__,则子类将具有一个__dict__

代码语言:javascript
复制
class B(A):
    pass

print('__dict__' in dir(B))  # True

有什么方法可以防止B在不需要设置__slots__ = ()的情况下拥有__dict__

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-06-13 16:06:48

@AKX的答案几乎是正确的。我认为__prepare__和元类确实是很容易解决的方法。

我只是简单地回顾一下:

  • 如果类的命名空间在类主体执行后包含一个__slots__键,那么类将使用__slots__而不是__dict__
  • 在使用__prepare__执行类主体之前,可以将名称注入类的命名空间。

因此,如果我们只返回一个字典,其中包含来自'__slots__'的键,那么类将使用__slots__而不是__dict__ (如果'__slots__'键在类主体的计算过程中不再被移除)。因为__prepare__只是提供了初始的命名空间,所以可以轻松地覆盖__slots__,或者在类主体中再次删除它们。

因此,默认情况下提供__slots__的元类如下所示:

代码语言:javascript
复制
class ForceSlots(type):
    @classmethod
    def __prepare__(metaclass, name, bases, **kwds):
        # calling super is not strictly necessary because
        #  type.__prepare() simply returns an empty dict.
        # But if you plan to use metaclass-mixins then this is essential!
        super_prepared = super().__prepare__(metaclass, name, bases, **kwds)
        super_prepared['__slots__'] = ()
        return super_prepared

因此,每个具有这个元类的类和子类(默认情况下)名称空间中都有一个空的__slots__,因此创建了一个“带槽的类”(除非__slots__是有意删除的)。

为了说明它是如何工作的:

代码语言:javascript
复制
class A(metaclass=ForceSlots):
    __slots__ = "a",

class B(A):  # no __dict__ even if slots are not defined explicitly
    pass

class C(A):  # no __dict__, but provides additional __slots__
    __slots__ = "c",

class D(A):  # creates normal __dict__-based class because __slots__ was removed
    del __slots__

class E(A):  # has a __dict__ because we added it to __slots__
    __slots__ = "__dict__",

它通过了AKZ答复中提到的测试:

代码语言:javascript
复制
assert "__dict__" not in dir(A)
assert "__dict__" not in dir(B)
assert "__dict__" not in dir(C)
assert "__dict__" in dir(D)
assert "__dict__" in dir(E)

并核实它是否如预期的那样工作:

代码语言:javascript
复制
# A has slots from A: a
a = A()
a.a = 1
a.b = 1  # AttributeError: 'A' object has no attribute 'b'

# B has slots from A: a
b = B()  
b.a = 1
b.b = 1  # AttributeError: 'B' object has no attribute 'b'

# C has the slots from A and C: a and c
c = C()
c.a = 1
c.b = 1  # AttributeError: 'C' object has no attribute 'b'
c.c = 1

# D has a dict and allows any attribute name
d = D()  
d.a = 1
d.b = 1
d.c = 1

# E has a dict and allows any attribute name
e = E()  
e.a = 1
e.b = 1
e.c = 1

正如在注释( Aran-Fey)中指出的,del __slots__和向__slots__添加__dict__之间有一个区别

这两个选项之间有一个细微的区别:del __slots__不仅会给您的类提供一个__dict__,而且还会给您的类提供一个__weakref__插槽。

票数 6
EN

Stack Overflow用户

发布于 2019-06-13 12:09:48

像这样的元类和 hook怎么样?

代码语言:javascript
复制
import sys


class InheritSlots(type):
    def __prepare__(name, bases, **kwds):
        # this could combine slots from bases, I guess, and walk the base hierarchy, etc
        for base in bases:
            if base.__slots__:
                kwds["__slots__"] = base.__slots__
                break
        return kwds


class A(metaclass=InheritSlots):
    __slots__ = ("foo", "bar", "quux")


class B(A):
    pass


assert A.__slots__
assert B.__slots__ == A.__slots__
assert "__dict__" not in dir(A)
assert "__dict__" not in dir(B)

print(sys.getsizeof(A()))
print(sys.getsizeof(B()))

由于某些原因,这仍然打印64, 88 -也许继承的类的实例总是比基类本身重一点?

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

https://stackoverflow.com/questions/56579348

复制
相关文章

相似问题

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