Поиск статических атрибутов класса в Python - PullRequest
3 голосов
/ 20 марта 2009

Это необычный вопрос, но я бы хотел динамически сгенерировать атрибут __slots__ класса на основе любых атрибутов, которые я случайно добавил в класс.

Например, если у меня есть класс:

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

    __slots__ = ['one', 'two']

Я бы хотел сделать это динамически, а не указывать аргументы от руки, как бы я это сделал?

Ответы [ 2 ]

3 голосов
/ 20 марта 2009

В момент, когда вы пытаетесь определить slots , класс еще не создан, поэтому вы не можете определить его динамически из класса 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
1 голос
/ 31 октября 2011

Одна очень важная вещь, о которой следует знать - если эти атрибуты останутся в классе, поколение __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 - наличие их непосредственно в конечном классе еще больше усложнит метакласс.

...