15

我最近在 python 中发现了元类。

基本上,python中的元类是一个创建类的类。您想要这样做的原因有很多——例如任何类型的类初始化。在工厂上注册类、复杂的属性验证、改变继承的工作方式等等。所有这些不仅变得可能而且变得简单。

但在 python 中,元类也是普通类。所以,我开始想知道抽象是否可以有用地更高,在我看来它可以而且:

  • 元类对应或实现模式中的角色(如在 GOF 模式语言中)。
  • 元元类就是模式本身(如果我们允许它创建代表抽象角色的类元组,而不仅仅是一个类)
  • meta-meta-metaclass 是一个模式工厂,它对应于 GOF 模式分组,例如 Creational、Structural、Behavioural。一个工厂,您可以在其中描述某种类型的问题的案例,它会为您提供一组解决它的类。
  • 一个 meta-meta-meta-metaclass (据我所知),是一个模式工厂 factory,一个你可以描述问题类型的工厂,它会给你一个模式工厂来询问。

我在网上找到了一些关于这个的东西,但大多不是很有用。一个问题是不同的语言对元类的定义略有不同。

有没有其他人在 python/其他地方使用过这样的元类,或者在野外看到过这个,或者考虑过它?其他语言的类似物是什么?例如,在 C++ 中,模板递归能走多远?

我非常想进一步研究它。

4

5 回答 5

18

这让我想起了一些人似乎对“模式的通用实现”的永恒追求。就像一个可以创建任何对象(包括另一个工厂)的工厂,或者一个通用的依赖注入框架,它比简单地编写实际执行某些操作的代码要复杂得多。

当我管理 Zend Framework 项目时,我不得不与那些想抽象的人打交道。我拒绝了一堆创建不做任何事情的组件的提议,它们只是 GoF 模式的神奇实现,就好像模式本身就是一个目标,而不是实现目标的手段。

抽象有一个收益递减点。一些抽象很棒,但最终您需要编写一些有用的代码。

否则它只是一路下来的乌龟

于 2009-01-24T21:00:00.587 回答
8

回答你的问题:没有。

随意进一步研究它。

但是请注意,您将设计模式(只是想法)与代码(这是一种实现)混为一谈。

好的代码通常反映了许多相互关联的设计模式。没有简单的方法可以将其正式化。您能做的最好的事情是一张漂亮的图片、编写良好的文档字符串和反映各种设计模式的方法名称。

另请注意,元类是一个类。那是一个循环。没有更高级别的抽象。在这一点上,它只是意图。meta-meta-class 的想法没有多大意义——它是元类的元类,这很愚蠢,但在技术上是可行的。然而,这一切都只是一堂课。


编辑

“创建元类的类真的那么傻吗?它们的效用怎么突然用完了?”

创建类的类很好。差不多就是这样。目标类是元类或抽象超类或具体类这一事实并不重要。元类产生类。他们可能会创建其他元类,这很奇怪,但它们仍然只是创建类的元类。

该实用程序“突然”耗尽,因为在创建另一个元类的元类中没有您需要(甚至可以编写)的实际东西。并不是它“突然”变得愚蠢。就是那里没有什么用处。

当我播种时,请随意研究它。例如,实际编写一个构建另一个元类的元类。玩得开心。那里可能有一些有用的东西。

OO 的重点是编写模拟现实世界实体的类定义。因此,元类有时可以方便地定义几个相关类的横切方面。(这是一种进行面向方面编程的方法。)这就是元类真正可以做的事情。它是一个存放一些函数的地方,比如__new__(),它们不是类本身的适当部分。

于 2009-01-24T20:49:28.050 回答
8

在 2007 年的编程语言历史会议上,Simon Peyton Jones 评论说 Haskell 允许使用类型类进行元编程,但它真的是一路下跌。您可以在 Haskell 中进行 meta-meta-meta-meta 等程序,但他从未听说过有人使用超过 3 级间接。

Guy Steele 指出这在 Lisp 和 Scheme 中是一样的。您可以使用反引号和 eval 进行元编程(您可以将反引号视为 Python lambda,有点),但他从未见过使用超过 3 个反引号。

大概他们看到的代码比你或我看到的更多,所以说没有人超过 3 个元级别只是有点夸张。

如果您考虑一下,大多数人从不使用元编程,而且两个级别很难让您理解。我猜想三个几乎是不可能的,最后一个尝试四个的人最终进入了庇护所。

于 2010-01-19T00:39:14.637 回答
6

自从我第一次了解 Python 中的元类时,我一直想知道“元元类可以做什么?”。这至少是 10 年前的事了——而现在,就在几个月前,我很清楚 Python 类创建中有一种机制实际上涉及“元-元”类。因此,可以尝试想象它的一些用途。

回顾 Python 中的对象实例化:每当在 Python 中通过使用与调用普通函数相同的语法“调用”其类来实例化对象时,类的__new____init__. 在类上“协调”调用这些方法的正是类'元类'__call__方法。通常在 Python 中编写元类时,元类的__new__or__init__方法是自定义的。

因此,事实证明,通过编写“元元”类,可以自定义其__call__方法,从而控制传递哪些参数以及元类__new____init__方法,以及是否要在这些之前或之后调用其他代码。最后的结果是,metcalsses 本身通常是硬编码的,即使在非常大的项目中,也只需要几个,如果有的话。因此,可能在“元元”调用中完成的任何定制通常直接在元类本身上完成。

还有它们,Python 元类还有其他不太常见的用途——可以自定义元类中的__add__方法,以便它们定义的类是“可添加的”,并创建一个派生类,将两个添加的类作为超类。该机制对元类也完全有效 - 因此,我们“有一些实际代码”,遵循“元元”类的示例,该类允许仅通过在类声明中添加它们来为类组成“元类” :

class MM(type):
    def __add__(cls, other):
        metacls = cls.__class__
        return metacls(cls.__name__ + other.__name__, (cls, other), {})

class M1(type, metaclass=MM):
    def __new__(metacls, name, bases, namespace):
        namespace["M1"] = "here"
        print("At M1 creation")
        return super().__new__(metacls, name, bases, namespace)

class M2(type, metaclass=MM):
    def __new__(metacls, name, bases, namespace):
        namespace["M2"] = "there"
        print("At M2 creation")
        return super().__new__(metacls, name, bases, namespace)

我们可以看到在交互式控制台上工作:

In [22]: class Base(metaclass = M1 + M2): 
    ...:     pass
    ...: 
At M1 creation
At M2 creation

请注意,由于 Python 中的不同元类通常难以组合,因此通过允许将用户制作的元类与库或 stdlib 的元类组合起来,这实际上很有用,而不必将这个元类显式声明为前者的父类:

In [23]: import abc

In [24]: class Combined(metaclass=M1 + abc.ABCMeta):
    ...:     pass
    ...: 
At M1 creation
于 2017-08-25T01:40:18.060 回答
4

Smalltalk 中的班级系统是一个有趣的学习系统。在 Smalltalk 中,一切都是对象,每个对象都有一个类。这并不意味着层次结构趋于无穷。如果我没记错的话,它是这样的:

5 -> 整数 -> 整数类 -> 元类 -> 元类类 -> 元类 -> ...(它循环)

其中 '->' 表示“是一个实例”。

于 2009-01-24T22:08:59.513 回答