Python 3中的有序类

Jam*_*mes 4 python metaclass prepare python-3.x

我正在尝试使用PEP 3115中描述的"有序类" (即,可以按声明的顺序访问其成员的类).给出的实现是

# 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
Run Code Online (Sandbox Code Playgroud)

有一些我很困惑的事情.首先,有一个原因__prepare__classmethod什么?该定义不使用metacls- 这只是一个惯例吗?

其次,当我尝试这个代码时,'__module__'最终会在MyClass.member_names之前结束,'method1'并且'method2'显然与声称'method1'是第一个元素的注释相矛盾.为什么这个特殊属性最终会出现在列表中而没有其他属性呢?有没有其他可能让我感到惊讶的事情(__doc__除非该类有文档字符串,我明确定义了什么)?

最后,此实现不检索member_namesfrom基类.如果我想实现这一点,那么以下更改是否有任何问题__prepare__(除了它不检查重复之外)?

@classmethod
def __prepare__(metacls, name, bases):
    prep_dict = member_table()
    for base in bases:
        try:
            prep_dict.member_names.extend(base.member_names)
        except AttributeError:
            pass
    return prep_dict
Run Code Online (Sandbox Code Playgroud)

jsb*_*eno 5

首先,有一个理由__prepare__是为什么是一种类方法?该定义不使用metacls - 这只是一个约定吗?

正如在评论中指出的那样,当__prepare__被调用时,类本身尚未实例化.这意味着,如果它是普通方法(self作为第一个参数) - self在此调用中没有值.

它是有道理的,因为__prepare__在类体的解析中返回类似于对象的dict而不是普通的dict是什么呢 - 因此它根本不依赖于正在创建的类.

如果,如评论中所提到的那样staticmethod(它意味着它不会获得第metacls一个参数)那么__prepare__将无法访问元类的任何其他方法 - 这是不必要的限制.

是的,就像self,metacls在这种情况下,仅仅是一个约定-一个普通的Python标识符.(注意,当在普通类中使用classmethods时,通常表示第一个参数cls而不是metacls.)

其次,当我尝试这段代码时,__module__最终会在MyClass.member_names中结束,method1并且method2显然与声称method1是第一个元素的注释相矛盾.为什么这个特殊属性最终会出现在列表中而没有其他属性呢?有没有其他可能让我感到惊讶的事情(__doc__除非该类有文档字符串,我明确定义了什么)?

可能是因为__module__课程的特殊成员在考虑了__prepare__元类的方法后才想到了.(我无法通过谷歌搜索确定PEP,__module__但我敢打赌这是事实.)

不,不能保证未来的Python版本不会在字典中的显式类成员之前添加更多魔术属性.

但是对于元类,你完全可以控制 - 你可以像对象一样调整你的dict(member_table在你的代码中),不计算任何以"__"开头的属性 - 例如.或者甚至根本不添加到最终的类实例(冒着以这种方式定义类的风险,而不使用某些Python特性).

最后,此实现不从基类检索member_names.如果我想实现这一目标,以下更改是否有任何问题__prepare__

通过阅读它,我认为您提出的实施没有任何问题,但当然,您必须对其进行测试.

更新(2013-06-30):这个主题正在Python开发人员列表中进行讨论,它看起来像Python 3.4所有类都将默认排序,不需要使用元类或__prepare__仅用于排序