我正在尝试在python3中运行python2程序,它具有以下Meta类定义。哪个在Py2上运行得很好。什么是让它与py2和py3兼容的“最佳”方式?
它在单元测试中失败了:
try:
raise Actor.DoesNotExist
except Actor.DoesNotExist:
pass
失败是:
AttributeError: type object 'Actor' has no attribute 'DoesNotExist'
基本元类定义是:
class MetaDocument(type):
def __new__(meta,name,bases,dct):
class DoesNotExist(BaseException):
pass
class MultipleDocumentsReturned(BaseException):
pass
dct['DoesNotExist'] = DoesNotExist
dct['MultipleDocumentsReturned'] = MultipleDocumentsReturned
class_type = type.__new__(meta, name, bases, dct)
if not class_type in document_classes:
if name == 'Document' and bases == (object,):
pass
else:
document_classes.append(class_type)
return class_type
class Document(object):
__metaclass__ = MetaDocument
答案 0 :(得分:8)
您可以使用MetaDocument()
元类作为工厂来生成类替换您的Document
类,重新使用类属性:
class Document(object):
# various and sundry methods and attributes
body = vars(Document).copy()
body.pop('__dict__', None)
body.pop('__weakref__', None)
Document = MetaDocument(Document.__name__, Document.__bases__, body)
这不需要您手动构建第3个参数,即类体。
你可以把它变成一个类装饰器:
def with_metaclass(mcls):
def decorator(cls):
body = vars(cls).copy()
# clean out class body
body.pop('__dict__', None)
body.pop('__weakref__', None)
return mcls(cls.__name__, cls.__bases__, body)
return decorator
然后用作:
@with_metaclass(MetaDocument)
class Document(object):
# various and sundry methods and attributes
或者,请使用six
library:
@six.add_metaclass(MetaDocument)
class Document(object):
其中@six.add_metaclass()
decorator也会处理您可能已定义的任何__slots__
;我上面的简单版本没有。
six
也有six.with_metaclass()
base-class factory:
class Document(six.with_metaclass(MetaDocument)):
将一个额外的基类注入MRO。
答案 1 :(得分:1)
six
有一个实用程序。
class Document(six.with_metaclass(MetaDocument, object)):
# class definition, without the __metaclass__
唯一的副作用是类层次结构从
更改>>> Document.__mro__
(<class 'test.Document'>, <type 'object'>)
到
>>> Document.__mro__
(<class 'test.Document'>, <class 'test.NewBase'>, <type 'object'>)
因为with_metaclass
实际上返回了一个具有适当元类的新类。