【问题标题】:How does this use of Python's __metaclass__ work?使用 Python 的 __metaclass__ 是如何工作的?
【发布时间】:2014-07-27 12:55:25
【问题描述】:

Here in this IPython Notebook 我找到了一个关于生成器和函数式编程的演示文稿。我偶然发现了__metaclass__

  1. 为什么RealfloatAccounting 的参数?现在一般在bases里面是什么,但是需要在里面写Realfloat吗?

  2. __abstractmethods__ 有什么作用?我在通常的特殊方法列表中找不到它。

  3. 作者是否通过使用这两个 for 循环来丰富他的 Accounting 类与其他类的方法?

  4. 为什么Accounting(getattr(float,meth)(*args, **kwargs))不会导致无限循环?与第二个版本相比,这里发生了什么?

首先,这是与我的问题相关的摘录:

class Accounting(Real, float):
    def __metaclass__(name, bases, body):
        for method in Real.__abstractmethods__:
            if method not in body:
                body[method] = (lambda meth: lambda *args, **kwargs: Accounting(getattr(float,meth)(*args, **kwargs)))(method)
        for method in ('__ne__','__nonzero__',):
            if method not in body:
                body[method] = (lambda meth: lambda *args, **kwargs: getattr(float,meth)(*args, **kwargs))(method)
        return Real.__metaclass__(name, bases, body)
    __new__ = float.__new__

这是整个代码:

from numbers import Real
class Accounting(Real, float):
    def __metaclass__(name, bases, body):
        for method in Real.__abstractmethods__:
            if method not in body:
                body[method] = (lambda meth: lambda *args, **kwargs: Accounting(getattr(float,meth)(*args, **kwargs)))(method)
        for method in ('__ne__','__nonzero__',):
            if method not in body:
                body[method] = (lambda meth: lambda *args, **kwargs: getattr(float,meth)(*args, **kwargs))(method)
        return Real.__metaclass__(name, bases, body)
    __new__ = float.__new__
    def __format__(self, fmt):        
        return { 1: ' %s'%float.__format__(self, fmt),
                 0: ' %s'%float.__format__(self, fmt).replace('0', '-'),
                -1: '(%s)'%float.__format__(self, fmt) }[cmp(self,0)]

然后在这里使用代码:

template = '{region:<{align}}   {profit:>14,.0f}'.format
def output(markets, write=print, template=template):
    align = max(map(len,markets))
    for region, profit in markets.items():
        line = template(region=region, profit=profit, align=align)
        write(line)

output({region:Accounting(profit) for region, profit in markets.items()})

输出:

Central America   (      -675,140)
US                (    -2,724,620)
Antarctica                      -
Asia              (    -3,614,396)
UK                (    -2,380,001)
EU                        771,665
CEMEA                   3,480,172

【问题讨论】:

标签: python class metaprogramming


【解决方案1】:

部分答案:

for method in Real.__abstractmethods__:
           if method not in body:
               body[method] = (lambda meth: lambda *args, **kwargs: Accounting(getattr(float,meth)(*args, **kwargs)))(method)

numbers.Real 是一个抽象类(即你不能证实它的实例,但你可以将它用作子类。Real 类有一个特殊的 freezeset,称为 __abstractmethods__,它似乎列出了所有方法Real 类期望被定义。:

在交互式解释器中:

from numbers import Real
Real.__abstractmethods__
frozenset(['__rtruediv__', '__radd__', '__truediv__', '__pow__', '__add__', '__rdiv__', '__rmul__', '__rmod__', '__eq__', '__lt__', '__float__', '__rpow__', '__mod__', '__trunc__', '__abs__', '__pos__', '__div__', '__le__', '__rfloordiv__', '__neg__', '__floordiv__', '__mul__'])

因此,此循环确保主体(实际上是由元类创建的类)对这些方法中的每一个都有一个具体的定义。

我承认我并没有完全理解嵌套的 lambda 定义。

【讨论】:

  • 是的,lambda 定义很难理解——尤其是没有看到Real 类的源代码。它似乎做的是实例化一个单独的Accounting 对象(同时创建Accounting 类!),然后将使用提供的参数调用相应float 方法的结果传递给 then 方法,最后将该结果传递给那个临时的Accounting 对象的__call__() 方法。
猜你喜欢
  • 1970-01-01
  • 2016-12-25
  • 2016-06-10
  • 2016-10-03
  • 2012-04-01
  • 2013-09-04
相关资源
最近更新 更多