복붙노트

[PYTHON] 선언 된 것과 같은 순서로 클래스 속성을 읽는 방법?

PYTHON

선언 된 것과 같은 순서로 클래스 속성을 읽는 방법?

클래스 속성을 읽고 목록에 저장하는 메타 클래스를 작성하고 있지만 목록 (cls.columns)이 선언 순서 (mycol2, mycol3, zut, cool, menfin, a)를 존중하도록하고 싶습니다. :

import inspect
import pprint

class Column(object):
    pass

class ListingMeta(type):
    def __new__(meta, classname, bases, classDict):
        cls = type.__new__(meta, classname, bases, classDict)
        cls.columns = inspect.getmembers(cls, lambda o: isinstance(o, Column)) 
        cls.nb_columns = len(cls.columns)
        return cls

class Listing(object):
    __metaclass__ = ListingMeta
    mycol2 = Column()
    mycol3 = Column()
    zut = Column()
    cool = Column()
    menfin = Column()
    a = Column()

pprint.pprint(Listing.columns)

결과:

[('a', <__main__.Column object at 0xb7449d2c>),
 ('cool', <__main__.Column object at 0xb7449aac>),
 ('menfin', <__main__.Column object at 0xb7449a8c>),
 ('mycol2', <__main__.Column object at 0xb73a3b4c>),
 ('mycol3', <__main__.Column object at 0xb744914c>),
 ('zut', <__main__.Column object at 0xb74490cc>)]

이것은 Listing 클래스의 Column () 특성 선언 순서를 따르지 않습니다. classDict를 직접 사용하면 도움이되지 않습니다.

어떻게하면됩니까?

해결법

  1. ==============================

    1.현재 버전의 Python에서는 클래스 순서가 유지됩니다. 자세한 내용은 PEP520을 참조하십시오.

    현재 버전의 Python에서는 클래스 순서가 유지됩니다. 자세한 내용은 PEP520을 참조하십시오.

    이전 버전의 언어 (3.5 이하는 2.x는 아님)에서는 클래스 네임 스페이스에 대해 OrderedDict를 사용하는 메타 클래스를 제공 할 수 있습니다.

    import collections 
    
    class OrderedClassMembers(type):
        @classmethod
        def __prepare__(self, name, bases):
            return collections.OrderedDict()
    
        def __new__(self, name, bases, classdict):
            classdict['__ordered__'] = [key for key in classdict.keys()
                    if key not in ('__module__', '__qualname__')]
            return type.__new__(self, name, bases, classdict)
    
    class Something(metaclass=OrderedClassMembers):
        A_CONSTANT = 1
    
        def first(self):
            ...
    
        def second(self):
            ...
    
    print(Something.__ordered__)
    # ['A_CONSTANT', 'first', 'second']
    

    그러나이 방법은 기존 클래스에서는 도움이되지 않지만 인트로 스펙 션을 사용해야하는 곳에서는 유용합니다.

  2. ==============================

    2.다음은 방금 개발 한 해결 방법입니다.

    다음은 방금 개발 한 해결 방법입니다.

    import inspect
    
    class Column(object):
        creation_counter = 0
        def __init__(self):
            self.creation_order = Column.creation_counter
            Column.creation_counter+=1
    
    class ListingMeta(type):
        def __new__(meta, classname, bases, classDict):
            cls = type.__new__(meta, classname, bases, classDict)
            cls.columns = sorted(inspect.getmembers(cls,lambda o:isinstance(o,Column)),key=lambda i:i[1].creation_order) 
            cls.nb_columns = len(cls.columns)
            return cls
    
    class Listing(object):
        __metaclass__ = ListingMeta
        mycol2 = Column()
        mycol3 = Column()
        zut = Column()
        cool = Column()
        menfin = Column()
        a = Column()
    
    
    for colname,col in Listing.columns:
        print colname,'=>',col.creation_order
    
  3. ==============================

    3.파이썬 3.6에서는 이것이 기본 동작이되었습니다. PEP520 : https://www.python.org/dev/peps/pep-0520/을 참조하십시오.

    파이썬 3.6에서는 이것이 기본 동작이되었습니다. PEP520 : https://www.python.org/dev/peps/pep-0520/을 참조하십시오.

    class OrderPreserved:
        a = 1
        b = 2
        def meth(self): pass
    
    print(list(OrderPreserved.__dict__.keys()))
    # ['__module__', 'a', 'b', 'meth', '__dict__', '__weakref__', '__doc__']
    
  4. ==============================

    4.Python 2.x를 사용한다면 Lennart가 제안한 해킹과 같은 해킹이 필요합니다. Python 3.x를 사용하고 있다면 PEP 3115를 읽으십시오. 원하는 것을 수행하는 예제가 들어 있습니다. Column () 인스턴스 만 보도록 예제를 수정하십시오.

    Python 2.x를 사용한다면 Lennart가 제안한 해킹과 같은 해킹이 필요합니다. Python 3.x를 사용하고 있다면 PEP 3115를 읽으십시오. 원하는 것을 수행하는 예제가 들어 있습니다. Column () 인스턴스 만 보도록 예제를 수정하십시오.

     # The custom dictionary
     class member_table(dict):
        def __init__(self):
           self.member_names = []
    
        def __setitem__(self, key, value):
           # if the key is not already defined, add to the
           # list of keys.
           if key not in self:
              self.member_names.append(key)
    
           # Call superclass
           dict.__setitem__(self, key, value)
    
     # The metaclass
     class OrderedClass(type):
    
         # The prepare function
         @classmethod
         def __prepare__(metacls, name, bases): # No keywords in this case
            return member_table()
    
         # The metaclass invocation
         def __new__(cls, name, bases, classdict):
            # Note that we replace the classdict with a regular
            # dict before passing it to the superclass, so that we
            # don't continue to record member names after the class
            # has been created.
            result = type.__new__(cls, name, bases, dict(classdict))
            result.member_names = classdict.member_names
            return result
    
     class MyClass(metaclass=OrderedClass):
        # method1 goes in array element 0
        def method1(self):
           pass
    
        # method2 goes in array element 1
        def method2(self):
           pass
    
  5. ==============================

    5.@Duncan에 의한 솔루션을 기반으로하지만 더 간단합니다 (python3에만 해당). 우리는 __prepare__ 메서드가 간단한 dict 대신 OrderDict를 반환한다는 사실을 사용하므로 __new__ 호출 전에 수집 된 모든 특성이 정렬됩니다.

    @Duncan에 의한 솔루션을 기반으로하지만 더 간단합니다 (python3에만 해당). 우리는 __prepare__ 메서드가 간단한 dict 대신 OrderDict를 반환한다는 사실을 사용하므로 __new__ 호출 전에 수집 된 모든 특성이 정렬됩니다.

    from collections import OrderedDict
    
    class OrderedClass(type):
        @classmethod
        def __prepare__(mcs, name, bases): 
             return OrderedDict()
    
        def __new__(cls, name, bases, classdict):
            result = type.__new__(cls, name, bases, dict(classdict))
            result.__fields__ = list(classdict.keys())
            return result
    
    class Column:
        pass
    
    class MyClass(metaclass=OrderedClass):
        mycol2 = Column()
        mycol3 = Column()
        zut = Column()
        cool = Column()
        menfin = Column()
        a = Column()
    

    이제 __fields__ 특성을 사용하여 필요한 순서로 특성에 액세스 할 수 있습니다.

    m = MyClass()
    print(m.__fields__)
    ['__module__', '__qualname__', 'mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a']
    

    타입 클래스에서 태어난 attrs '__module__', '__qualname__'이있을 것입니다. 그들을 제거하려면 다음과 같이 이름을 필터링 할 수 있습니다 (OrderedClass .__ new__ 변경).

    def __new__(cls, name, bases, classdict):
        result = type.__new__(cls, name, bases, dict(classdict))
        exclude = set(dir(type))
        result.__fields__ = list(f for f in classdict.keys() if f not in exclude)
        return result    
    

    MyClass에서만 attrs를 제공합니다.

    ['mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a']
    
  6. ==============================

    6.방법을 배제한 답변 :

    방법을 배제한 답변 :

    from collections import OrderedDict
    from types import FunctionType
    
    
    class StaticOrderHelper(type):
        # Requires python3.
        def __prepare__(name, bases, **kwargs):
            return OrderedDict()
    
        def __new__(mcls, name, bases, namespace, **kwargs):
            namespace['_field_order'] = [
                    k
                    for k, v in namespace.items()
                    if not k.startswith('__') and not k.endswith('__')
                        and not isinstance(v, (FunctionType, classmethod, staticmethod))
            ]
            return type.__new__(mcls, name, bases, namespace, **kwargs)
    
    
    class Person(metaclass=StaticOrderHelper):
        first_name = 'First Name'
        last_name = 'Last Name'
        phone_number = '000-000'
    
        @classmethod
        def classmethods_not_included(self):
            pass
    
        @staticmethod
        def staticmethods_not_included(self):
            pass
    
        def methods_not_included(self):
            pass
    
    
    print(Person._field_order)
    
  7. ==============================

    7.나는 __dict__를 order-dict로 대체하는 클래스를 만들 수 있어야한다고 생각합니다.

    나는 __dict__를 order-dict로 대체하는 클래스를 만들 수 있어야한다고 생각합니다.

  8. from https://stackoverflow.com/questions/4459531/how-to-read-class-attributes-in-the-same-order-as-declared by cc-by-sa and MIT license