3

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

例如,如果我有一堂课:

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

    __slots__ = ['one', 'two']

我想动态地执行此操作,而不是手动指定参数,我该怎么做?

4

2 回答 2

3

在您尝试定义slot时,该类尚未构建,因此您无法从 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
于 2009-03-20T17:41:24.060 回答
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

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

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

于 2011-10-31T07:25:51.647 回答