在Python中查找类的静态属性

3
这是一个不太寻常的问题,但我想根据我添加到类中的任何属性动态生成类的__slots__属性。
例如,如果我有一个类:
class A(object):
    one = 1
    two = 2

    __slots__ = ['one', 'two']

我希望能够动态地完成这一操作,而不是手动指定参数,我该如何做到这一点?

此外,“属性”比这个例子更复杂,并包含对象,可以为类的每个实例相同(而不是为每个实例实例化一个)。 - Dan
对于具有10万个类实例的高性能,您可以考虑使用享元设计模式--属性不在类实例中,而是简单的元组,类基本上都是静态方法。 - S.Lott
对于同一对象的多个引用,slots 对象是无关紧要的。多个引用只是指向相同底层对象的多个引用 - 不需要做任何操作来使其发生。 - S.Lott
这是很好的建议,slots 似乎并没有太大的区别,而且享元设计模式可能非常有用。我想就此发布一个相关问题... - Dan
@Dan,你接受的答案不正确,除非你想要属性是只读的,并且还包括其他类元数据属性(__module__等)。你能否澄清你的问题,或者接受我的答案,它确实可以做到你所要求的? - Ethan Furman
显示剩余3条评论
2个回答

3

当您尝试定义插槽时,类还没有被构建,因此您不能从A类内部动态定义它。

要获取所需的行为,请使用元类来自省A的定义并添加插槽属性。

class MakeSlots(type):

    def __new__(cls, name, bases, attrs):
        attrs['__slots__'] = attrs.keys()

        return super(MakeSlots, cls).__new__(cls, name, bases, attrs)

class A(object):
    one = 1
    two = 2

    __metaclass__ = MakeSlots

2
这个答案有两个问题:1)它不起作用;2)即使它起作用,它也会将方法、类和类体中定义的所有内容添加到__slots__中。它不起作用是因为名称除了在新创建的__slots__中之外,还在覆盖__slots__的类的成员描述符中,最终我们得到的是AttributeError: 'A' object has no attribute 'one' - Ethan Furman
啊 -- 上面应该是 AttributeError: 'A' 对象属性 'one' 是只读的 - Ethan Furman
иҷҪ然иҝҷдёӘзұ»иҝҳжІЎжңүиў«жһ„е»әпјҢдҪҶдҪ д»Қ然еҸҜд»ҘеңЁзұ»еҲӣе»әжңҹй—ҙдҪҝз”Ёvars()жқҘе®ҢжҲҗе®ғпјҢ并且иҰҒеңЁжңҖеҗҺе®ҢжҲҗгҖӮ - Ethan Furman

1

有一件非常重要的事情需要注意——如果这些属性留在类中,那么__slots__生成将是无用的……好吧,也许不是无用的——它会使类属性变为只读;这可能不是你想要的。

简单的方法是说,“好的,我会将它们初始化为None,然后让它们消失。”太棒了!以下是一种实现方式:

class B(object):
    three = None
    four = None

    temp = vars()                    # get the local namespace as a dict()
    __slots__ = temp.keys()          # put their names into __slots__
    __slots__.remove('temp')         # remove non-__slots__ names
    __slots__.remove('__module__')   # now remove the names from the local
    for name in __slots__:           # namespace so we don't get read-only
        del temp[name]               # class attributes
    del temp                         # and get rid of temp

如果你想保留那些初始值,需要更多的工作...这里有一个可能的解决方案:

class B(object):
    three = 3
    four = 4

    def __init__(self):
        for key, value in self.__init__.defaults.items():
            setattr(self, key, value)

    temp = vars()
    __slots__ = temp.keys()
    __slots__.remove('temp')
    __slots__.remove('__module__')
    __slots__.remove('__init__')
    __init__.defaults = dict()
    for name in __slots__:
        __init__.defaults[name] = temp[name]
        del temp[name]
    del temp

正如您所看到的,即使没有元类,也可以做到这一点--但是谁想要所有那些样板文件呢?元类肯定可以帮助我们整理好这个问题:

class MakeSlots(type):
    def __new__(cls, name, bases, attrs):
        new_attrs = {}
        new_attrs['__slots__'] = slots = attrs.keys()
        slots.remove('__module__')
        slots.remove('__metaclass__')
        new_attrs['__weakref__'] = None
        new_attrs['__init__'] = init = new_init
        init.defaults = dict()
        for name in slots:
            init.defaults[name] = attrs[name]
        return super(MakeSlots, cls).__new__(cls, name, bases, new_attrs)

def new_init(self):
    for key, value in self.__init__.defaults.items():
        setattr(self, key, value)

class A(object):
    __metaclass__ = MakeSlots

    one = 1
    two = 2


class B(object):
    __metaclass__ = MakeSlots

    three = 3
    four = 4

现在所有的繁琐都被保留在元类中,而实际的类易于阅读和(希望!)理解。

如果除了属性之外,您需要在这些类中添加任何其他内容,我强烈建议您将它们放入混合类中--直接将它们放在最终类中会使元类变得更加复杂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接