在Python中查找类的静态属性

时间:2009-03-20 17:33:12

标签: python class-design

这是一个不寻常的问题,但我想基于我碰巧添加到类中的任何属性动态生成类的__slots__属性。

例如,如果我有一个班级:

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

    __slots__ = ['one', 'two']

我想动态地这样做而不是手动指定参数,我该怎么做?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

需要注意的一件非常重要的事情 - 如果这些属性留在课堂上,__slots__代将无用......好吧,也许不是无用的 - 它会使类属性为只读;可能不是你想要的。

简单的方法就是说,“好的,我会将它们初始化为无,然后让它们消失。”优秀!这是一种方法:

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

现在所有乏味都保存在元类中,实际的类很容易阅读,并且(希望!)理解。

如果你需要在这些类中除了属性之外还有其他东西我强烈建议你把它放在mixin类中 - 直接在最后一个类中使它们会使元类更复杂。