Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何按照声明的顺序读取类属性?_Python_Class_Metaclass - Fatal编程技术网

Python 如何按照声明的顺序读取类属性?

Python 如何按照声明的顺序读取类属性?,python,class,metaclass,Python,Class,Metaclass,我正在编写一个元类来读取类属性并将它们存储在列表中,但我希望列表cls.columns遵守声明顺序,即:mycl2、mycl3、zut、cool、menfin、a在我的示例中: import inspect import pprint class Column(object): pass class ListingMeta(type): def __new__(meta, classname, bases, classDict): cls = type.__n

我正在编写一个元类来读取类属性并将它们存储在列表中,但我希望列表cls.columns遵守声明顺序,即:mycl2、mycl3、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', “酷”, “门芬”, “myCL2”, ‘mycol3’, ‘zut’,] 这不符合列表类的列属性的声明顺序。如果我直接使用classDict,它也没有帮助


如何继续?

以下是我刚刚制定的解决方案:

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

我想你应该能够创建一个类,用一个有序的dict替换它的dict,如果你使用的是Python2.x,那么你需要一个类似Lennart建议的hack。如果您使用的是Python3.x,那么请阅读,因为它包含一个您想要的示例。只需修改示例以仅查看列实例:

 # 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

在当前版本的Python中,保留了类顺序。有关详细信息,请参阅

在语言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']

但是,这种方法对现有类没有帮助,因为您需要使用内省。

1因为类定义中的Python 3.6属性在源代码中的显示顺序相同。此顺序现在保留在新类的_dict__;属性中:

您将看到类似于MyClass的输出。\uuuu dict\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

dict_keys(['__module__', 'mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a', '__dict__', '__weakref__', '__doc__'])
注意:python添加的额外_uuxxx_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

2对于以前的Python3.x版本,您可以使用基于@Duncan answer的解决方案,但更简单。 我们使用这个事实,uuuu prepare_uuuuuu方法返回一个OrderDict而不是简单的dict-因此在uu new_uuu调用之前收集的所有属性都将被排序

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()
现在,您可以使用属性_字段_)按所需顺序访问属性:

m = MyClass()
print(m.__fields__)
['__module__', '__qualname__', 'mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a']
请注意,将有属性“\uuuu module\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。要除去这些名称,您可以通过以下方式筛选名称change OrderedClass.\uuu new\uuuuuu:

它将只提供MyClass的属性:

['mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a']

3此anwser仅在python3.x中可用,因为python2.7中没有python3.6的“准备”定义,这已成为默认行为。见PEP520:


排除方法的答案:

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)

我认为如果没有某种源代码级别的分析,就无法将它们整理好。在任何情况下,顺序都应该是无关紧要的。dict按键散列,这就是为什么您根本看不到它的有序性,这是一个非常有建设性的问题。谢谢你可以看看tosca widget 2,首先我想你必须在每个类之后重置创建计数器,然后我意识到你根本不需要,假设你只关心内部顺序。它确实有效它是如何在并行线程中工作的?我认为这段代码不是线程安全的。有关Python3.6+的信息,请参阅@conchliculator answer,它可能基于该语言提供的较新的dict顺序插入保证。您可以使用VARSCL而不是更丑陋的cls。这主意不错。你测试过它吗?我试过了,但结果证明它没有明确的顺序。关于如何使用和学习内省,你有什么想法或建议吗?或者更好,在这种情况下如何应用它。还应该注意的是,即使在P3.6中,当使用外部类来帮助定义Something类项时,顺序也不总是保留的。这也适用于在带有某个_item=字段的类中分配这些项时使用。
['mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a']
class OrderPreserved:
    a = 1
    b = 2
    def meth(self): pass

print(list(OrderPreserved.__dict__.keys()))
# ['__module__', 'a', 'b', 'meth', '__dict__', '__weakref__', '__doc__']
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)