3

__slots__これは珍しい質問ですが、クラスに追加した属性に基づいて、クラスの属性を動的に生成したいと思います。

たとえば、クラスがある場合:

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

    __slots__ = ['one', 'two']

引数を手動で指定するのではなく、動的にこれを実行したいのですが、どうすればよいですか?

4

2 に答える 2

3

スロットを定義しようとしている時点では、クラスはまだ構築されていないため、Aクラス内から動的に定義することはできません。

必要な動作を得るには、メタクラスを使用してAの定義をイントロスペクトし、slots属性を追加します。

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

注意すべき非常に重要なことの1つは、これらの属性がクラスに残っている場合、__slots__生成は役に立たないことです...わかりました、おそらく役に立たないことではありません-クラス属性が読み取り専用になります。おそらくあなたが望むものではありません。

簡単な方法は、「わかりました。初期化してから、非表示にします」と言うことです。素晴らしい!これを行う1つの方法は次のとおりです。

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

これらの初期値を維持したい場合は、もう少し作業が必要です...考えられる解決策の1つは次のとおりです。

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

これで、すべての面倒な作業がメタクラスに保持され、実際のクラスは読みやすく(うまくいけば!)理解しやすくなります。

属性以外にこれらのクラスに何かを含める必要がある場合は、それが何であれ、ミックスインクラスに入れることを強くお勧めします。最終クラスに直接入れると、メタクラスがさらに複雑になります。

于 2011-10-31T07:25:51.647 に答える