4

我正在尝试使用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

有几件事我很困惑。__prepare__首先,有一个原因classmethod吗?该定义不使用metacls- 这只是一个约定吗?

其次,当我尝试这段代码时,'__module__'结果是MyClass.member_namesbefore 'method1'and ,显然与声称是第一个元素'method2'的注释相矛盾。'method1'为什么这个特殊属性最终出现在列表中,而其他属性没有?是否还有其他可能让我感到惊讶(除了__doc__该类是否有文档字符串,以及我明确定义的任何内容)?

最后,此实现不会member_names从基类中检索 。如果我想实现这一点,以下更改是否有任何问题__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
4

1 回答 1

5

首先,类方法是有原因__prepare__的吗?该定义不使用 metacls - 这只是一个约定吗?

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

这是有道理的,因为__prepare__在类体的解析中返回一个类似字典的对象而不是普通的字典 - 所以它根本不依赖于正在创建的类。

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

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

其次,当我尝试这段代码时,__module__最终出现在 MyClass.member_names 之前method1and ,显然与声称是第一个元素method2的评论相矛盾。method1为什么这个特殊属性最终出现在列表中,而其他属性没有?是否还有其他可能让我感到惊讶(除了__doc__该类是否有文档字符串,以及我明确定义的任何内容)?

可能是因为在考虑元类的方法__module__之后才考虑到类的特殊成员。__prepare__(我无法通过谷歌搜索 PEP 定义进行验证,__module__但我敢打赌就是这种情况。)

不,不能保证未来版本的 Python 不会为类添加更多神奇属性,这些属性可能会出现在字典中显式类成员之前。

但是使用元类,您可以完全控制 - 例如,您可以调整您的 dict 对象(member_table在您的代码中)以不计算任何以“__”开头的属性。甚至根本不将 then 添加到最终的类实例中(冒着以这种方式定义的类无法使用某些 Python 功能的风险)。

最后,此实现不会从基类中检索 member_names。如果我想实现这一点,以下更改有什么问题__prepare__吗?

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

Update (2013-06-30): this subject is being actively discussed in the Python developer list, and itlooks like from Python 3.4 all classes will be ordered by default, with no need to use a metaclass or __prepare__ only for ordering

于 2011-12-08T02:50:55.453 回答