如何为 python 中的元类替换 __prepare__ 2
How to replace __prepare__ for metaclass in python 2
我需要将一些代码从 python 3 转换为 python 2。我有一个元 class,其中 __prepare__
方法在 class 字典。我试图转换为 __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
通常情况下,您不能这样做。
__prepare__
的引入是对 Python3 的根本改变,它允许自定义解析 class 主体本身的命名空间。
我相信这样做的主要动机是提供一种方法来用 OrderedDict 替换 class 体内的本地名称空间,以便 class 初始化(在元 class __new__
或 __init__
方法)可以受益于 class 体内方法和属性的声明顺序。应该考虑的是,从 Python 3.6(本周到期的最终版本)开始,class 主体中默认使用有序字典,不再需要元 class那。
__prepare__
机制比那更灵活,并且在更简单的使用中,允许使用预定值简单地预填充 class 正文字典。这就是您的项目所做的。
但是,由于这段代码不需要特殊的字典class,只是预先填充了一个普通的字典,所以你需要做的就是编写一个普通的函数,它接受一个字典和基础classes作为参数,按照__prepare__
方法中已有的代码填充那个字典。然后,在 class 主体的开头调用该函数,将 locals()
调用返回的字典作为参数传入。就是这样:class body 命名空间可以用同样的方式预填充。
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__
)
我需要将一些代码从 python 3 转换为 python 2。我有一个元 class,其中 __prepare__
方法在 class 字典。我试图转换为 __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
通常情况下,您不能这样做。
__prepare__
的引入是对 Python3 的根本改变,它允许自定义解析 class 主体本身的命名空间。
我相信这样做的主要动机是提供一种方法来用 OrderedDict 替换 class 体内的本地名称空间,以便 class 初始化(在元 class __new__
或 __init__
方法)可以受益于 class 体内方法和属性的声明顺序。应该考虑的是,从 Python 3.6(本周到期的最终版本)开始,class 主体中默认使用有序字典,不再需要元 class那。
__prepare__
机制比那更灵活,并且在更简单的使用中,允许使用预定值简单地预填充 class 正文字典。这就是您的项目所做的。
但是,由于这段代码不需要特殊的字典class,只是预先填充了一个普通的字典,所以你需要做的就是编写一个普通的函数,它接受一个字典和基础classes作为参数,按照__prepare__
方法中已有的代码填充那个字典。然后,在 class 主体的开头调用该函数,将 locals()
调用返回的字典作为参数传入。就是这样:class body 命名空间可以用同样的方式预填充。
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__
)