[英]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
失算或者還需要一些手工的工作。 不幸的是,我對元類很少有經驗。
您的原始代碼使用以下事實:類主體中的名稱 __metaclass__
用作元類,但2to3
修復程序僅查找直接賦值 :
__metaclass__ = MetaClassName
而不是class __metaclass__
語句或其他方式定義名稱( from somemodule import MetaClassName as __metaclass__
將在Python 2類體中工作, 2to3
也會錯過)。
您可以通過將元類移動到單獨的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中定義元類,因為定義元類的機制被改為在運行類體之前而不是在期間確定元類(因此元類也可以影響該元素)。
現在2to3
將正確檢測到類上有__metaclass__
屬性並重寫它們以使用新的Python 3語法:
stackoverflow-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
並且重構的代碼按預期工作:
stackoverflow-2.7 $ bin/python -m lib2to3 -o ../stackoverflow-3.4 -nw --no-diffs fixed.py
lib2to3.main: Output in '../stackoverflow-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 ../stackoverflow-3.4/fixed.py.
RefactoringTool: Files that were modified:
RefactoringTool: fixed.py
stackoverflow-2.7 $ cd ../stackoverflow-3.4
stackoverflow-3.4 $ bin/python -V
Python 3.4.2
stackoverflow-3.4 $ bin/python fixed.py
BaseMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
Heir::define. Called.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.