python2和python3之间的可移植元类

时间:2014-03-14 15:33:58

标签: python python-3.x

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

2 个答案:

答案 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实际上返回了一个具有适当元类的新类。