元类有哪些(具体的)用例?


117

我有一个喜欢使用元类的朋友,并定期提供它们作为解决方案。

我认为您几乎不需要使用元类。为什么?因为我认为如果您正在对一个类进行类似的操作,那么您可能应该对一个对象进行此操作。并进行了少量的重新设计/重构。

能够使用元类已经使很多地方的许多人将类用作某种二流对象,这对我来说似乎是灾难性的。用元编程代替编程吗?不幸的是,添加了类装饰器使它变得更加可以接受。

所以,我很想知道您在Python中对元类的有效(具体)用例。还是要启发一下为什么有时候变异类比变异对象更好。

我将开始:

有时在使用第三方库时,能够以某种方式对类进行更改很有用。

(这是我能想到的唯一情况,并不具体)


3
这是一个很好的问题。从下面的答案来看,它很明显没有元数据类的具体用法。
Marcus Ottosson 2014年

Answers:


25

我有一个处理非交互式绘图的类,作为Matplotlib的前端。但是,有时需要进行交互式绘图。仅使用几个函数,我发现我能够增加图形数量,手动调用绘制等,但是我需要在每次绘制调用之前和之后执行这些操作。因此,要创建交互式绘图包装器和屏幕外绘图包装器,我发现通过元类包装适当的方法来执行此操作比执行以下操作更有效:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

该方法不能跟上API的更改等等,但是__init__在重新设置类属性之前对类属性进行迭代的方法效率更高,并且可以保持最新状态:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

当然,也许有更好的方法可以做到这一点,但是我发现这是有效的。当然,这也可以在__new__或中完成__init__,但这是我发现最直接的解决方案。


103

最近有人问我同样的问题,并提出了几个答案。我希望可以重新启动该线程,因为我想详细说明所提到的一些用例,并添加一些新用例。

我见过的大多数元类都执行以下两项操作之一:

  1. 注册(将类添加到数据结构中):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass

    每当您子类化时Model,您的班级都会在models字典中注册:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}

    这也可以使用类装饰器来完成:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass

    或具有显式注册功能:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)

    实际上,这几乎是相同的:您不利地提到了类装饰器,但是实际上,对于类的函数调用而言,它只不过是语法糖,所以这没有什么魔术。

    无论如何,在这种情况下,元类的优点是继承,因为它们适用于任何子类,而其他解决方案仅适用于显式修饰或注册的子类。

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
  2. 重构(修改类属性或添加新属性):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass

    每当您子类化Model并定义一些Field属性时,它们就会被注入其名称(例如,用于提供更多有用的错误消息),并分组到_fields字典中(以方便迭代,而不必查看所有类属性及其所有基类的'属性每次):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}

    同样,可以使用类装饰器完成此操作(不继承):

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(

    或明确地:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!

    尽管与您主张的可读性和可维护性的非元编程相反,但这更加麻烦,冗余且容易出错:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}

考虑了最常见和具体的用例之后,您绝对必须使用元类的唯一情况是您想修改类名称或基类列表,因为一旦定义,这些参数就被烘焙到类中,并且没有装饰器或功能可以取消它们。

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

每当定义具有相似名称或不完整继承树的类时,在发出警告的框架中这可能都是有用的,但是除了实际更改这些值之外,我没有想到其他原因。也许大卫·比兹利可以。

无论如何,在Python 3中,元类也具有__prepare__方法,该方法使您可以将类主体评估为除之外的映射dict,从而支持有序属性,重载属性和其他邪恶的东西:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

您可能会争辩说,可以使用创建计数器来实现有序属性,并且可以使用默认参数来模拟重载:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

除了难看之外,它的灵活性也更低:如果您想要有序的文字属性(如整数和字符串)怎么办?如果None有效值是x什么呢?

这是解决第一个问题的创造性方法:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

这是解决第二种问题的一种创造性方法:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

但是,这远比简单的元类(尤其是第一个真正使您的大脑融化的元类)更加巫毒教。我的观点是,您将元类视为陌生且违反直觉的事物,但也可以将它们视为编程语言发展的下一步:您只需要调整心态即可。毕竟,您可能可以在C中完成所有操作,包括使用函数指针定义结构并将其作为函数的第一个参数传递。初次接触C ++的人可能会说:“这是什么魔术?为什么编译器会隐式传递this方法,而不是常规和静态函数?最好是对参数进行露骨和冗长。”但是,一旦获得面向对象的编程,它就会变得功能强大得多;嗯,我想……准面向方面的编程。了解元类,它们实际上非常简单,那么为什么不方便使用它们呢?

最后,元类是rad,编程应该很有趣。始终使用标准的编程构造和设计模式既无聊又无济于事,并且阻碍了您的想象力。坚持一下!这是一个元数据类,仅供您使用。

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

编辑

这是一个很老的问题,但仍在投票中,因此我想添加一个指向更全面答案的链接。如果您想了解有关元类及其使用的更多信息,我刚刚在这里发表了一篇有关元类的文章。


5
这是一个很好的答案,感谢您花时间编写并给出多个示例
Chen A.

“ ...在这种情况下,元类的优点是继承,因为它们适用于任何子类” –我想不是在Python 3中吗?我认为它仅在Python 2中起作用是因为任何子类都继承了该__metaclass__属性,但是此属性在Python 3中不再特殊。是否有任何方法可以使“子类也由父级的元类构造”在Python 3中起作用?
ForceBru

2
对于Python 3也是这样,因为从A继承的类B(其元类为M)也是M的类型。因此,当对B求值时,将调用M来创建它,这实际上使您可以(在A的任何子类上工作)。话虽这么说,Python 3.6引入了更简单的方法init_subclass,所以现在您可以在基类中操作子类,并且不再需要用于此目的的元类。
Dan Gittik

这真是太棒了,我读了很多关于元类的博客文章,只有这一篇才知道元类的优缺点和替代方法。
ospider

36

元类的目的不是用元类/类代替类/对象的区别,而是以某种方式改变类定义(及其实例)的行为。有效地是,以对默认域更有用的方式更改类语句的行为。我使用它们的目的是:

  • 跟踪子类,通常用于注册处理程序。当使用插件样式设置时,这很方便,您希望通过子类化并设置一些类属性来为特定事件注册处理程序。例如。假设您为各种音乐格式编写了一个处理程序,其中每个类针对其类型实现适当的方法(播放/获取标签等)。为新类型添加处理程序将变为:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here

    然后,元类维护{'.mp3' : MP3File, ... }etc 的字典,并在通过工厂函数请求处理程序时构造适当类型的对象。

  • 改变行为。您可能想对某些属性附加特殊含义,从而导致它们出现时行为发生变化。例如,你可能想寻找一个名为方法_get_foo_set_foo和透明地转换为性能。作为一个真实的例子,这是我写的食谱,旨在提供更多类似C的结构定义。元类用于将声明的项转换为结构格式的字符串,处理继承等,并生成能够处理的类。

    对于其他实际示例,请查看各种ORM,例如sqlalchemy ORM或sqlobject。同样,目的是解释具有特定含义的定义(此处为SQL列定义)。


3
好吧,是的,跟踪子类。但是你为什么要那样?您的示例只是对register_music_file(Mp3File,['.mp3'])的隐式,而显式的方式更具可读性和可维护性。这是我正在谈论的坏事的一个例子。
阿里·阿夫沙尔

关于ORM案例,您是在谈论基于表的定义表的方式,还是在映射对象上的元类。因为SQLAlchemy可以(正确地)映射到任何类(我假设它没有对该活动使用元类)。
阿里·阿夫沙尔

10
我更喜欢声明式的样式,而不是为每个子类要求额外的注册方法-如果所有内容都包装在一个位置中,则更好。
布赖恩

对于sqlalchemy,我主要考虑的是声明性层,因此sqlobject可能是一个更好的示例。但是,内部使用的元类也是对特定属性进行类似重新解释以声明含义的示例。
布赖恩

2
抱歉,我的承诺之一在SO超时场景中丢失了。我发现声明性类几乎令人讨厌。我知道人们喜欢它,并且它是公认的行为。但是(根据经验)我知道在您要联合国声明事物的情况下这是不可用的。取消课程注册非常困难
阿里·阿夫沙尔

17

让我们从蒂姆·彼得的经典名言开始:

元类是比99%的用户应该担心的更深的魔力。如果您想知道是否需要它们,则不需要(实际上需要它们的人肯定会知道他们需要它们,并且不需要解释原因)。蒂姆·彼得斯(clp post 2002-12-22)

话虽如此,我(定期地)遇到了元类的真实用法。我想到的是在Django中,所有模型都继承自models.Model。反过来,models.Model确实做了一些严肃的魔术,将您的数据库模型与Django的ORM优势包装在一起。这种魔术通过元类发生。它创建各种形式的异常类,管理器类等。

有关故事的开头,请参见django / db / models / base.py,类ModelBase()。


好吧,是的,我明白了。我不奇怪使用元类的“如何”或“为什么”,我也不知道“谁”和“什么”。我在这里看到ORM是很常见的情况。不幸的是,与SQLAlchemy相比,Django的ORM表现差强人意。魔术是不好的,为此确实不需要元类。
阿里·阿夫沙尔

9
过去读过蒂姆·彼得斯的名言后,时间表明他的说法是无益的。直到在这里在StackOverflow上研究Python元类时,如何实现它们才变得显而易见。在强迫自己学习如何编写和使用元类之后,它们的功能使我惊讶,并使我对Python的真正工作方式有了更好的了解。类可以提供可重用的代码,而元类可以为这些类提供可重用的增强。
Noctis Skytower

6

元类可以很方便地在Python中构造领域特定语言。具体的例子是Django,它是SQLObject的数据库模式声明式语法。

Ian Bicking的“ 保守的元类”中的一个基本示例:

我使用的元类主要是为了支持一种声明性的编程风格。例如,考虑一个验证模式:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

其他一些技术:用Python构建DSL的成分(pdf)。

编辑(由Ali编写):我希望使用一个使用集合和实例进行此操作的示例。重要的事实是实例,它们可以为您提供更多功能,并消除使用元类的原因。进一步值得一提的是,您的示例使用了类和实例的混合体,这肯定表明您不能只对元类进行所有操作。并创建了一种真正不统一的方式。

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

这不是完美的,但是魔术几乎为零,不需要元类,并且一致性更高。


谢谢你 这是一个很好的用例示例,我认为这是不必要的,丑陋的和不可管理的,基于一个简单的集合实例(根据需要带有嵌套的集合),它将变得更加简单。
阿里·阿夫沙尔

1
@Ali A:欢迎您提供通过元类和基于简单集合实例的方法在声明式语法之间进行并行比较的具体示例。
jfs

@Ali A:您可以就地编辑我的答案以添加收藏夹样式示例。
jfs

好了 抱歉,今天有点急,但是稍后/明天将尝试回答所有查询。节日快乐!
阿里·阿夫沙尔

2
第二个例子很丑,因为您必须将验证器实例与其名称绑定在一起。更好的方法是使用字典而不是列表,但是,在python中,类只是字典的语法糖,所以为什么不使用类呢?您还可以获得免费的名称验证,因为python辣妹不能包含字符串可以包含的空格或特殊字符。
Lie Ryan

6

元类使用的合理模式是在定义类时执行一次操作,而不是在实例化相同类时重复执行操作。

当多个类共享相同的特殊行为时,重复__metaclass__=X显然比重复专用代码和/或引入即席共享超类更好。

但是,即使只有一个特殊类且没有可预见的扩展,对于元类而言, __new__与在类定义主体中__init__混合专用代码和normal defclassstatement 相比,初始化类变量或其他全局数据是一种更干净的方法。


5

我唯一在Python中使用元类的时间是在为Flickr API编写包装器时。

我的目标是抓取flickr的api网站并动态生成完整的类层次结构,以允许使用Python对象进行API访问:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

因此,在该示例中,因为我从网站生成了整个Python Flickr API,所以我真的不知道运行时的类定义。能够动态生成类型非常有用。


2
您可以动态生成类型,而无需使用元类。>>>帮助(类型)
Ali Afshar

8
即使你没有意识到这一点,您使用元类即可。类型是元类,实际上是最常见的一种。:-)
Veky 2015年

5

就在昨天,我在想同样的事情,完全同意。我认为,尝试使声明式更具声明性而导致的代码复杂性通常会使代码库更难以维护,更难阅读且Python的使用率也更低。它通常也需要大量的copy.copy()ing(以保持继承并从类复制到实例),这意味着您必须在许多地方查看发生了什么(始终从元类向上查看),这与蟒纹也。我一直在浏览formencode和sqlalchemy代码,以查看这种声明式样式是否值得,而显然不值得。这种样式应留给描述符(例如属性和方法)和不可变数据。Ruby对这样的声明式样式提供了更好的支持,我很高兴核心python语言没有走这条路。

我可以看到它们在调试中的用途,可以将一个元类添加到您所有的基类中以获得更丰富的信息。我还看到它们仅在(非常)大型项目中使用,以摆脱一些样板代码(但有一点不清楚)。例如,sqlalchemy 确实在其他地方使用了它们,以基于所有子类的类定义中的属性值向所有子类添加特定的自定义方法,例如玩具示例

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

可能有一个元类,该元类在该类中生成了一个基于“ hello”的特殊属性的方法(例如,将“ hello”添加到字符串末尾的方法)。确保不必在您创建的每个子类中都编写方法,而对于您所要定义的只是method_maker_value,这对维护性有好处。

但是,对此的需求如此罕见,并且仅减少了一点打字,除非您有足够大的代码库,否则它根本不值得考虑。


5

您永远不必绝对使用元类,因为您始终可以使用要修改的类的继承或聚合来构造一个可以满足您需要的类。

就是说,在Smalltalk和Ruby中,能够修改现有的类非常方便,但是Python不喜欢直接这样做。

关于Python元类化的一篇很好的DeveloperWorks文章可能会有所帮助。在维基百科的文章也还不错。


1
您也不需要对象进行面向对象的编程-您可以使用一流的函数来进行。因此,您不需要使用对象。但是他们在那里是为了方便。因此,我不确定您在第一段中试图说明什么。
泰勒·克伦普顿

1
回头看看这个问题。
查理·马丁

4

当多个线程尝试与它们进行交互时,某些GUI库会遇到麻烦。tkinter就是这样一个例子;尽管可以通过事件和队列显式处理问题,但以完全忽略该问题的方式使用该库要简单得多。看得出-元类的魔力。

在某些情况下,能够无缝地动态重写整个库,使其在多线程应用程序中按预期正常工作,这可能会非常有用。该safetkinter模块这是否与所提供的元类的帮助threadbox模块-事件和队列没有必要的。

它的一个整洁的方面threadbox是它不在乎它克隆什么类。它提供了一个示例,说明如果需要,元类可以如何触及所有基类。元类附带的另一个好处是它们也可以在继承类上运行。编写自己的程序-为什么不呢?


4

元类的唯一合法用例是防止其他管闲的开发人员接触您的代码。当一个管闲的开发人员掌握了元类并开始与您的类一起玩耍时,请投入另一个或两个级别以将其排除在外。如果这样不起作用,请开始使用type.__new__递归元类或者使用某种方案使用递归元类。

(用舌头写在脸颊上,但我已经看到这种混淆处理了。Django是一个很好的例子)


7
我不确定Django的动机是否相同。
阿里·阿夫沙尔

3

元类不能代替编程!它们只是一个技巧,可以使某些任务自动化或变得更加优雅。一个很好的例子是Pygments语法高亮库。它具有一个称为的类,该类RegexLexer使用户可以将一组词法化规则定义为类上的正则表达式。元类用于将定义变成有用的解析器。

他们就像盐一样。过多使用很容易。


好吧,我认为,Pygments案是没有必要的。为什么不仅仅拥有像dict这样的简单集合,又为什么要强迫一个班级这样做呢?
阿里·阿夫沙尔

4
由于一类不错的词法分析封装了的想法,但有其他有用的方法,如guess_filename()等
本杰明·彼得森

3

我使用元类的方式是为类提供一些属性。举个例子:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

将在每个将元类设置为指向NameClass的类上放置name属性。


5
是的,这可行。您也可以使用超类,该超类至少是显式的,并且可以在代码中遵循。出于兴趣,您将其用于什么目的?
阿里·阿夫沙尔

2

这是次要用途,但是...我发现元类有用的一件事是每当创建子类时都调用一个函数。我将其编码为一个寻找__initsubclass__属性的元类:每当创建一个子类时,所有使用该方法定义该方法的父类都将被调用__initsubclass__(cls, subcls)。这允许创建一个父类,该父类随后在某个全局注册表中注册所有子类,在定义子类时对它们进行不变检查,执行后期绑定操作等...所有这些都无需手动调用函数创建自定义元类,履行这些单独的职责。

提醒您,我逐渐意识到这种行为的隐含魔力在某种程度上是不可取的,因为如果从上下文中查看类定义是意外的……因此,我不再使用该解决方案来处理其他严重问题__super为每个类和实例初始化一个属性。


1

最近,我不得不使用元类来帮助围绕填充有来自美国的人口普查数据的数据库表来声明性定义SQLAlchemy模型 http://census.ire.org/data/bulkdata.html的

IRE提供数据库外壳了人口普查数据表的,这些根据p012015,p012016,p012017等人口普查局的命名约定创建整数列。

我想要a)能够使用model_instance.p012017语法访问这些列,b)对我正在做的事情相当明确,c)不必在模型上明确定义数十个字段,因此我将SQLAlchemy的子类化为DeclarativeMeta一个迭代范围,列并自动创建与列对应的模型字段:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

然后,我可以将此元类用于模型定义,并访问模型上自动枚举的字段:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...


0

我不得不将它们一次用于二进制解析器,以使其更易于使用。您可以定义消息类,其中包含电线上存在的字段的属性。需要按照声明的顺序来订购它们,以从中构造最终的电汇格式。如果使用有序命名空间dict,则可以使用元类来实现。实际上,在元类的示例中:

https://docs.python.org/3/reference/datamodel.html#metaclass-example

但总的来说:如果您确实确实需要增加元类的复杂性,请仔细评估。


0

@Dan Gittik的回答很酷

最后的示例可以澄清很多事情,我将其更改为python 3并给出了一些解释:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan
  • 一切都是对象,所以类是对象
  • 类对象是由元类创建的
  • 从类型继承的所有类都是元类
  • 元类可以控制类的创建
  • 元类也可以控制元类的创建(因此它可以永远循环)
  • 这是元编程...您可以在运行时控制类型系统
  • 再次,一切都是对象,这是一个统一的系统,类型创建类型,类型创建实例

0

另一个用例是,当您希望能够修改类级别的属性并确保它仅影响手头的对象时。实际上,这意味着“合并”元类和类实例化的阶段,从而使您仅处理它们自己(唯一)种类的类实例。

当(出于对可读性多态性的考虑)我们要动态定义 property s时,该返回值(可能)是基于(经常更改的)实例级属性的计算结果的,而这只能在类级别完成,因此我还必须这样做在元类实例化之后和类实例化之前。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.