订购没有实例化的类方法

时间:2014-12-15 12:46:51

标签: python python-2.7

相关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中提到的解决方案对我不起作用。

1 个答案:

答案 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)