使用元类将 Python2 转换为 Python3 会导致错误的流程
Converting Python2 to Python3 with metaclasses resulted in a wrong flow
我有一个非常大的 Python 2.7.6 项目,我需要将其转换为 Python 3.4。我使用了 2to3 脚本,但 'metaclass' 处理似乎被破坏了。
我过滤了代码以缩短并查明问题所在。以下片段适用于 Python 2.7.6:
class Base(object):
class __metaclass__(type):
def __new__(cls, classname, bases, dict):
new = type.__new__(cls, classname, bases, dict)
new.classname = classname
print ("Base::__metaclass__::new. Called.")
return new
class Heir(Base):
class __metaclass__(Base.__metaclass__):
def __new__(self, *args):
new = Base.__metaclass__.__new__(self, *args)
print ("Heir::__metaclass__::new. Called.")
return new
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
class HeirOfHeir(Heir):
pass
Heir.define(HeirOfHeir)
代码按预期打印:
Base::__metaclass__::new. Called.
Base::__metaclass__::new. Called.
Heir::__metaclass__::new. Called.
Base::__metaclass__::new. Called.
Heir::__metaclass__::new. Called.
Heir::define. Called.
但是当 运行 代码与 Python 3.4 我只有最后一个打印:
Heir::define. Called.
要么 2to3
计算错误,要么需要一些人工操作。不幸的是,我对元类没有什么经验。
您的原始代码使用 name class 正文中的 __metaclass__
作为元 class,但是 2to3
修复程序只查找 直接赋值 :
__metaclass__ = MetaClassName
而不是 class __metaclass__
语句或其他定义名称的方式(from somemodule import MetaClassName as __metaclass__
将在 Python 2 class 正文中工作,而 2to3
将也想念那个)。
您可以通过将元 classes 移动到单独的 class
定义来解决此问题:
class BaseMeta(type):
def __new__(cls, classname, bases, dict):
new = type.__new__(cls, classname, bases, dict)
new.classname = classname
print ("BaseMeta::new. Called.")
return new
class Base(object):
__metaclass__ = BaseMeta
class HeirMeta(BaseMeta):
def __new__(self, *args):
new = BaseMeta.__new__(self, *args)
print ("HeirMeta::new. Called.")
return new
class Heir(Base):
__metaclass__ = HeirMeta
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
class HeirOfHeir(Heir):
pass
Heir.define(HeirOfHeir)
您将必须执行此操作以在 Python 3 中定义元classes,由于定义 metaclasses 的机制已更改为确定 metaclass before class body 是 运行 而不是在 运行 期间(这样 metaclass 也可以影响该步骤)。
现在 2to3
将正确检测到您的 class 上有一个 __metaclass__
属性并重写它们以使用新的 Python 3 语法:
Whosebug-2.7 $ bin/python -m lib2to3 fixed.py
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
--- fixed.py (original)
+++ fixed.py (refactored)
@@ -5,8 +5,8 @@
print ("BaseMeta::new. Called.")
return new
-class Base(object):
- __metaclass__ = BaseMeta
+class Base(object, metaclass=BaseMeta):
+ pass
class HeirMeta(BaseMeta):
def __new__(self, *args):
@@ -14,9 +14,7 @@
print ("HeirMeta::new. Called.")
return new
-class Heir(Base):
- __metaclass__ = HeirMeta
-
+class Heir(Base, metaclass=HeirMeta):
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
RefactoringTool: Files that need to be modified:
RefactoringTool: fixed.py
并且重构后的代码按预期工作:
Whosebug-2.7 $ bin/python -m lib2to3 -o ../Whosebug-3.4 -nw --no-diffs fixed.py
lib2to3.main: Output in '../Whosebug-3.4' will mirror the input directory '' layout.
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
RefactoringTool: Writing converted fixed.py to ../Whosebug-3.4/fixed.py.
RefactoringTool: Files that were modified:
RefactoringTool: fixed.py
Whosebug-2.7 $ cd ../Whosebug-3.4
Whosebug-3.4 $ bin/python -V
Python 3.4.2
Whosebug-3.4 $ bin/python fixed.py
BaseMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
Heir::define. Called.
我有一个非常大的 Python 2.7.6 项目,我需要将其转换为 Python 3.4。我使用了 2to3 脚本,但 'metaclass' 处理似乎被破坏了。
我过滤了代码以缩短并查明问题所在。以下片段适用于 Python 2.7.6:
class Base(object):
class __metaclass__(type):
def __new__(cls, classname, bases, dict):
new = type.__new__(cls, classname, bases, dict)
new.classname = classname
print ("Base::__metaclass__::new. Called.")
return new
class Heir(Base):
class __metaclass__(Base.__metaclass__):
def __new__(self, *args):
new = Base.__metaclass__.__new__(self, *args)
print ("Heir::__metaclass__::new. Called.")
return new
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
class HeirOfHeir(Heir):
pass
Heir.define(HeirOfHeir)
代码按预期打印:
Base::__metaclass__::new. Called.
Base::__metaclass__::new. Called.
Heir::__metaclass__::new. Called.
Base::__metaclass__::new. Called.
Heir::__metaclass__::new. Called.
Heir::define. Called.
但是当 运行 代码与 Python 3.4 我只有最后一个打印:
Heir::define. Called.
要么 2to3
计算错误,要么需要一些人工操作。不幸的是,我对元类没有什么经验。
您的原始代码使用 name class 正文中的 __metaclass__
作为元 class,但是 2to3
修复程序只查找 直接赋值 :
__metaclass__ = MetaClassName
而不是 class __metaclass__
语句或其他定义名称的方式(from somemodule import MetaClassName as __metaclass__
将在 Python 2 class 正文中工作,而 2to3
将也想念那个)。
您可以通过将元 classes 移动到单独的 class
定义来解决此问题:
class BaseMeta(type):
def __new__(cls, classname, bases, dict):
new = type.__new__(cls, classname, bases, dict)
new.classname = classname
print ("BaseMeta::new. Called.")
return new
class Base(object):
__metaclass__ = BaseMeta
class HeirMeta(BaseMeta):
def __new__(self, *args):
new = BaseMeta.__new__(self, *args)
print ("HeirMeta::new. Called.")
return new
class Heir(Base):
__metaclass__ = HeirMeta
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
class HeirOfHeir(Heir):
pass
Heir.define(HeirOfHeir)
您将必须执行此操作以在 Python 3 中定义元classes,由于定义 metaclasses 的机制已更改为确定 metaclass before class body 是 运行 而不是在 运行 期间(这样 metaclass 也可以影响该步骤)。
现在 2to3
将正确检测到您的 class 上有一个 __metaclass__
属性并重写它们以使用新的 Python 3 语法:
Whosebug-2.7 $ bin/python -m lib2to3 fixed.py
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
--- fixed.py (original)
+++ fixed.py (refactored)
@@ -5,8 +5,8 @@
print ("BaseMeta::new. Called.")
return new
-class Base(object):
- __metaclass__ = BaseMeta
+class Base(object, metaclass=BaseMeta):
+ pass
class HeirMeta(BaseMeta):
def __new__(self, *args):
@@ -14,9 +14,7 @@
print ("HeirMeta::new. Called.")
return new
-class Heir(Base):
- __metaclass__ = HeirMeta
-
+class Heir(Base, metaclass=HeirMeta):
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
RefactoringTool: Files that need to be modified:
RefactoringTool: fixed.py
并且重构后的代码按预期工作:
Whosebug-2.7 $ bin/python -m lib2to3 -o ../Whosebug-3.4 -nw --no-diffs fixed.py
lib2to3.main: Output in '../Whosebug-3.4' will mirror the input directory '' layout.
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
RefactoringTool: Writing converted fixed.py to ../Whosebug-3.4/fixed.py.
RefactoringTool: Files that were modified:
RefactoringTool: fixed.py
Whosebug-2.7 $ cd ../Whosebug-3.4
Whosebug-3.4 $ bin/python -V
Python 3.4.2
Whosebug-3.4 $ bin/python fixed.py
BaseMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
Heir::define. Called.