Как получить порядок определения атрибутов класса в Python? - PullRequest
6 голосов
/ 19 ноября 2010

Я хочу определить легковесные классы, которые должны представлять структуры данных.Как и в случае со многими структурами данных, порядок данных важен.Поэтому, если я пойду дальше и определю это:

class User(DataStructure):
    username = StringValue()
    password = StringValue()
    age = IntegerValue()

Я подразумеваю, что это структура данных, в которой строка с именем пользователя идет первой, затем следует строка с паролем и, наконец, возрастuser как целое число.

Если вы знакомы с Python, вы будете знать, что вышеприведенный класс User является объектом, наследуемым от type.Он, как и большинство других объектов в Python, будет иметь __dict__.И тут-то и кроется моя проблема.Это __dict__ является хеш-картой, поэтому порядок атрибутов класса в __dict__ никак не связан с порядком их определения.

Есть ли способ, которым я могу выяснить фактический порядок определения?Я спрашиваю здесь, прежде чем перейти к одному из менее здравомыслящих методов, о которых я могу подумать ...

О, и просто для ясности, что я хочу, это способ получить это из приведенного выше определения:['username', 'password', 'age']

Ответы [ 5 ]

5 голосов
/ 19 ноября 2010

Python 2.7 и 3.x определяют OrderedDict в модуле коллекций.Я считаю, что он использует связанный список для поддержания порядка вставки своих элементов.Он добавляет итерируемые методы к стандартным изменяемым методам отображения.

Вы можете определить метакласс, который использует OrderedDict вместо стандартного неупорядоченного dict в качестве пространства имен __dict__ для классов структур данных.Если вы дадите метаклассу специальный метод __prepare__(), вы можете сделать это.Я не пробовал это, но в соответствии с документами это возможно:

Из Python 3.1 Язык Ссылка Раздел 3.3.3 Модель данных - Настройка создания класса:

If the metaclass has a __prepare__() attribute (usually implemented as a class 
or static method), it is called before the class body is evaluated with the 
name of the class and a tuple of its bases for arguments. It should return an 
object that supports the mapping interface that will be used to store the 
namespace of the class. The default is a plain dictionary. This could be used, 
for example, to keep track of the order that class attributes are declared in 
by returning an ordered dictionary.

К сожалению эквивалентный раздел 3.4.3 в Python 2.7 Язык Ref не упоминает о возможности замены dict пространства имен класса и не упоминает метод __prepare__().Так что это возможно только в Python версии 3.

3 голосов
/ 19 ноября 2010

Это то, что не очень хорошо поддерживается в Python. Джанго использовал метаклассы, чтобы справиться с этим. См. Этот вопрос: Как Django узнает приказ об обработке полей формы?

(Резюме: посмотрите на django.forms.forms.DeclarativeFieldsMetaclass, django.forms.forms.get_declared_fields и как creation_counter используется в django.forms.fields.Field.)

2 голосов
/ 19 ноября 2010

Один из способов, который был бы более общим, чем подход Django, __slots__ и , доступные в Python 2, - это метакласс:

class OrderedTypeMeta(type):
    def __new__(mcls, clsname, bases, clsdict):
        attrs = clsdict.get('_attrs_', [])
        attrnames = []
        for name, value in attrs:
            clsdict[name] = value
            attrnames.append(name) 
        clsdict['_attrs_'] = attrnames
        return super(OrderedTypeMeta, mcls).__new__(mcls, clsname, bases, clsdict)


class User(DataStructure):
    __metaclass__ = OrderedTypeMeta
    _attrs_ = (('name', StringValue()),
               ('password', StringValue()),
               ('age', IntegerValue()))

Я говорю, что он более общий, чем способ Djangoпоскольку вам не нужно, чтобы атрибуты были экземплярами определенного класса, подойдет любое значение.Он также более общий, чем __slots__, потому что вы все равно сможете назначать атрибуты экземплярам классов (хотя это может и не понадобиться: в этом случае я бы предпочел __slots__).В python3 я бы предпочел __prepare__.

Основным недостатком этого, помимо того, что это некрасиво, является то, что он не будет работать с наследованием.Было бы не слишком сложно извлечь __attrs__ из базовых классов и расширить его, вместо того, чтобы устанавливать его в пустой список.

1 голос
/ 19 ноября 2010

Вы можете использовать __slots__, чтобы явно упорядочить атрибуты. Если каждый класс, от которого вы наследуете, определяет его, это дает дополнительный выигрыш в производительности памяти и доступа к атрибутам.

Редактировать: Самый надежный способ - определить __prepare__ в метаклассе. Справочные документы имеют его, в комплекте с примером хранения атрибутов в OrderedDictionary .

Вы не можете иметь порядок неявно в Python 2 без техники полевых классов Django, потому что Python просто не отслеживает эту информацию; он теряется к тому времени, когда вызывается метакласс.

0 голосов
/ 15 января 2014

Ниже приведено описание порядка атрибутов, которые являются методами или имеют тип struct.Я не пробовал это со встроенными типами.Затем вы можете построить ходунки вокруг детей

class ordered_type(type):
    __ordernate__ = 0
    def __new__(meta, name, baseclasses, dct):
        new = super(Type, meta).__new__(meta, name, baseclasses, dct)
        new.__ordernate__ = Type.__ordernate__
        Type.__ordernate__ += 1        
        def key(x):
            from inspect import ismethod
            if ismethod(x):
                return x.im_func.__code__.co_firstlineno
            elif isinstance(x, type):
                return x.__ordernate__ + 1000000
        new.__children__ = sorted(
            [getattr(new, x) for x in dir(new) if not x.startswith('__')],
            key=key)

class struct(object):
    __metaclass__ = ordered_type

#use case
class A(struct):
    '''Description of class A
    '''
    def name(self):
        '''The name of instance of A
        '''
    class B(struct):
        '''Description of class B
        '''
        def name(self):
            '''The name of instance of B
            '''
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...