2009-03-20 2 views
3

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

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

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

    __slots__ = ['one', 'two'] 

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

+0

Это запах преждевременной оптимизации. Можете ли вы обсудить свой вариант использования немного больше? – SingleNegationElimination

+0

Это не имеет большого значения - атрибуты уже определены динамически во время выполнения. Зачем возиться с __slots__? Что вы планируете делать с __slots__? –

+0

Я сделал нагрузку профилирования и есть место для улучшения дизайна, который я думаю. Для 100 000 экземпляров класса я хочу удалить возможность динамически определяемых атрибутов, чтобы классы были более легкими. – Dan

ответ

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__'' memberdescriptor', и в результате мы получаем атрибут 'AttributeError: 'A' атрибут 'one'' –

+0

Argh - выше должен быть атрибут AttributeError:' A '' one 'доступен только для чтения' –

+0

Хотя это правда, что класс еще не создан, вы все равно можете это сделать во время создания класса : используйте 'vars()', и сделайте это * last *. –

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 - их непосредственное включение в конечный класс еще более усложнит метаклассу.

 Смежные вопросы

  • Нет связанных вопросов^_^