当前位置: 代码迷 >> python >> python2和python3之间的可移植元类
  详细解决方案

python2和python3之间的可移植元类

热度:125   发布时间:2023-06-27 21:57:39.0

我正在尝试在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

您可以将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)

这不需要您手动构建第三个参数,即类主体。

您可以将其变成类装饰器:

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.add_metaclass(MetaDocument)
class Document(object):

还负责您可能定义的所有__slots__ 我上面的简单版本没有。

six也有一个 :

class Document(six.with_metaclass(MetaDocument)):

这为MRO注入了额外的基类。

有一个实用程序。

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实际上会返回带有相应元类的新类。