如何在python 2中替换元类的__prepare__

时间:2016-12-19 19:18:21

标签: python-2.7 python-3.x metaclass

我需要将一些代码从python 3转换为python 2.我有一个元类,其中__prepare__方法在类dict中设置一个函数。我尝试翻译为__new__方法,但我无法设置SET_DEFAULTS功能。这可能吗?

初始化时我有一个NameError: name 'SET_DEFAULTS'

class UazeMessageMeta (type):

    @staticmethod
    def __prepare__(name, bases, **kwargs):
        d = {}
        for b in bases: 
            if 'DEFAULT_VALUES' in dir(b):
                d.update(b.DEFAULT_VALUES)
        return { 
            'SET_DEFAULTS' : lambda **kwargs : d.update(kwargs),
            'DEFAULT_VALUES' : d
        }

class UazeMessage (bytearray):
    """
    A basic message (header only).  This class also provides the
    base behavior for all messages.
    """
    # gp test py27 -----------------
    __metaclass__ = UazeMessageMeta 

    # ------------

    priority    = MessageField(0,  1,  Priority)
    sequence    = MessageField(1,  7,  FieldType.UNSIGNED)
    readWrite   = MessageField(8,  1,  ReadWriteFlag)
    ack         = MessageField(9,  2,  Ack)
    channel     = MessageField(11, 2,  FieldType.UNSIGNED)
    category    = MessageField(13, 3,  Category)
    item        = MessageField(16, 8,  FieldType.UNSIGNED)

    DEFAULT_SIZE = 3

    def __init__(self, init=0, setdefaults=None, **kwargs):
        # If init is still or None, initialize the size of the message
        # using the default size provided in the class.
        if init == None or init == 0: 
            init = type(self).DEFAULT_SIZE
        super(UrmpMessage,self).__init__(init)
        # Set any default or provided fields.
        initval = {}
        if (isinstance(init, int) and setdefaults != False) or \
           (setdefaults == True):
            initval = dict(self.DEFAULT_VALUES)
        initval.update(kwargs)
        for key, value in initval.items():
            setattr(self, key, value)

class ResetBase (UazeMessage):
    """Reset response/request structure."""

    resetType = MessageField(24, 8, ResetType)

    SET_DEFAULTS(
        category  = Category.OPERATION,
        resetType = ResetType.SOFT,
        item      = 0)

    DEFAULT_SIZE = 4

1 个答案:

答案 0 :(得分:1)

通常情况下,你不能这样做。 __prepare__的引入是Python3的一个根本性变化,它允许自定义类本体本身被解析的命名空间。

我认为这样做的主要动机是提供一种方法,用OrderedDict替换类体内的本地命名空间,以便类初始化(在元类__new____init__方法中)可以从类体内的方法和属性的声明顺序中受益。应该考虑到Python 3.6(本周到期的最终版本),默认情况下在类体中使用有序字典,并且不再需要元类。

__prepare__机制比这更灵活,并且在更简单的使用中,允许用简单的预先填充具有预定值的类体字典。这就是你的项目所做的事情。

但是,由于此代码不需要特殊的字典类,只需预先填充普通字典,所以您需要做的就是编写一个普通函数,它接受字典和基类作为参数,并填写该字典根据__prepare__方法中的现有代码。然后,在类主体的开始调用该函数,将locals()调用返回的字典作为参数传入。就是这样:类体命名空间可以预先填充。

def prepare(bases, dct):
    for base in bases:
        dct["special_attribute"] = {}
        if "special_attribute" in base.__dict__:
            dct["special_attribute" ].update(base.__dict__["special_attribute"])
     ...

class MyClass(bytearray):
    prepare((bytearray,), locals())
    ...

所有这一切,我真的建议你尝试尽可能不在这个时候将项目向后端移植到Python2 - 它只会使你的代码库复杂化 - 并以一致的方式放弃使用新功能(例如,这提示而不是__prepare__