相关:inspect.getmembers in order?
背景:我正在尝试构建一个根据特定规范创建Python文件的工具。一种选择是作为输入提供包含抽象类声明的Python模块,并创建一个继承该抽象类的基类,但也为所有抽象方法添加一个默认实现。
例如:说我有以下文件,名为Abstract.py
,其中包含以下内容:
class Abstract(object):
__metaclass__ = ABCMeta
@abstractmethod
def first(self):
pass
@abstractmethod
def second(self):
pass
然后我的工具的输出将是一个名为BaseClass.py
的文件,其中包含:
class BaseClass(Abstract):
def first(self):
pass
def second(self):
pass
我希望BaseClass
中的方法与Abstract
中的方法相同。
我的问题是:有没有办法根据外观对方法进行排序,而不依赖于内置方法比较(基于内存地址比较)?我也希望尽可能避免任何类型的文件解析。
请注意,我无法创建Abstract
的实例,因此上述related question中提到的解决方案对我不起作用。
答案 0 :(得分:2)
在Python2中创建类时(也就是说,当解释器在运行文件时传递类体时,顺序发生) - 类本身被创建为对象。此时,类体中定义的所有变量和方法都将作为字典传递给“type”(默认元类)的调用。
如你所知,Python中的字典没有排序,所以通常在Python2中是不可能的。在Python 3中是可能的,因为元类可以实现一个__prepare__
方法,该方法返回将用于构建类主体的映射对象 - 因此__prepare__
可以返回OrderedDict,而不是普通的dict。
但是,在您的情况下,所有相关方法都使用@abstractmethod
进行修饰 - 我们可以利用它不仅将方法注释为抽象,而且还标记它们出现的顺序。
您可以包装abstractclass
装饰器,也可以创建另一个装饰器并使用它们。我赞成一个新的装饰器,可以做两件事,以便减少行数。
此外,您必须选择如何保持方法的顺序并使用它。通常迭代类的属性将只是遍历字典(而不是字典代理),这是无序的 - 因此,您必须保持数据结构以保持有序方法可用,以及记录此给定顺序的方法。那里有一些选项 - 但是,最直接的方法是在方法本身中注释方法顺序,并通过调用带有适当sorted
参数的内置key
来检索它们。其他方法需要使用类装饰器或自定义元类来工作。
所以这是我写的一个例子:
from abc import abstractmethod, ABCMeta
class OrderedAbstractMethod(object):
def __init__(self):
self.counter = 0
def __call__(self,func):
func._method_order = self.counter
self.counter += 1
return abstractmethod(func)
ordered_abstract_method = OrderedAbstractMethod()
class Abstract(object):
__metaclass__ = ABCMeta
@ordered_abstract_method
def first(self):
pass
@ordered_abstract_method
def second(self):
pass
@ordered_abstract_method
def third(self):
pass
@ordered_abstract_method
def fourth(self):
pass
print "Unordered methods: ",[method[0] for method in Abstract.__dict__.items() if not method[0].startswith("_") ]
# here it printed out - ['second', 'third', 'fourth', 'first']
print "Ordered methods: ", sorted([method for method in Abstract.__dict__.items() if not method[0].startswith("_") ], key= lambda m: m[1]._method_order)