fbpx
维基百科

元类

面向对象程序设计中,元类(英語:metaclass)是一种实例是的类。普通的类定义的是特定对象的行为,元类定义的则是特定的类及其实例的行为。不是所有面向对象编程语言都支持元类。在它能做的事情之中,元类可以覆写任何给定方面类行为的程度是不同的。元类可以通过使类成为头等对象来实现,在这种情况下元类简单的就是构造类的一个对象。每个语言都有它自己的元对象协议,给出对象、类和元类如何交互的规则[1]

Smalltalk-80元类 编辑

Smalltalk中,所有东西都是对象。此外,Smalltalk是基于类的系统,这意味着所有对象都有一个类,它定义这个对象的结构(比如说这个类拥有实例变量),和这个对象所理解的消息。二者在一起蕴含了,在Smalltalk中,类是一个对象,因此类也需要是它的元类的实例。[2]

元类在Smalltalk-80系统中的主要角色,是提供协议来初始化类变量,和建立元类的唯一实例(也就是其对应的类)的初始化实例。

实例联系 编辑

为了允许类拥有它们自己的方法,和叫作类实例变量它们自己的实例变量英语Instance variable,Smalltalk-80为每个类C介入了它们自己的元类C class。就像实例方法实际上属于类一样,类方法实际上属于元类。在类中定义实例变量英语Instance variable类变量英语Class variable,而在元类中定义类实例变量。

每个元类在效果上都是单例类。就像连体双胞胎,类和元类是共生的。元类有一个实例变量thisClass,它指向它结合的类。平常的Smalltalk类浏览器英语class browser,不将元类展示为单独的类,转而允许一起同时编辑类和它的元类。

要得到一个实例的类,需要向它发送消息调用class方法。类和元类继承了其超类的name方法,它返回接收者名字的字符串。例如,轿车对象c是类Car的实例,则c class返回Car类对象,而c class name返回'Car';依次类推,Car class返回Car的元类对象,而Car class name返回依赖于实现,有的是nil,即没有名字,有的是'Car class',即用空格分隔的类名字和'class'

在早期的Smalltalk-76中,创建新类的方式是向Class类发送new消息[3]。在Smalltalk-80中,Class是元类的基础类,它是类而不是元类。所有元类都是一个Metaclass类的实例。Metaclass类是Metaclass class的实例,而Metaclass class作为元类,也是Metaclass类的实例。

继承联系 编辑

在Smalltalk-80中,终端对象是一个整数、一个组件、或一台车等,而类是像Integer、或WidgetCar等这样的东西,除了Object之外,所有的都有一个超类。元类所继承的元类,就是元类对应的类所继承的类的元类。

在一个消息被发送到对象的时候,方法的查找开始于它的类。如果没有找到则在上行超类链,停止于Object而不管找到与否。在一个消息被发送到一个类的时候,类方法查找开始于它的元类,并上行超类链至Object class。直至Object class,元类的超类层级并行于类的超类层级。在Smalltalk-80中,Object classClass的子类:

Object class superclass == Class. 

类方法的查找在元类链之后仍可继续下去,所有元类都是Class的在继承层级中的子类,它是所有元类的抽象超类,它描述这些类的一般性质,继而最终可上溯至Object

继承层级 编辑

四个类提供描述新类的设施,下面是它们的继承层级(起自Object),和它们提供的主要设施:

  • Object,对象类是所有类的基础类,它为所有对象提供公共的方法,即公共的缺省行为。至少包括了:测试对象的功能比如class方法,比较对象,对象复制,访问对象的各部份,打印和存储对象,错误处理。
    • Behavior,行为类定义了拥有实例的对象所需要的最小状态英语State (computer science),它提供建立一个类的实例的new方法。特别是,它定义了Smalltalk-80解释器所用到的状态,并为编译方法源代码提供到编译器的基本接口,如compile:等方法。Behavior描述的这个状态,包括了一个类层级连接(superclass:),一个方法字典(methodDictionary:addSelector:withMethod:),和对实例的描述(依据数目和对它们的变量的表示)。尽管一个类的多数设施都规定在Behavior中,但很多消息不能于此实现,对类的完全描述转而在它的子类之中提供。
      • ClassDescription,类描述类为ClassMetactass提供了共同的超类。它表现类命名(name)、类注释(comment:)、和命名实例变量(addlnstVarName:)。特别是,它增加了组织在方法字典中方法(compile:classified:)和类自身(category:)的结构。它还提供了在外部串流(文件)上存储完全的类描述的机制,和记述对类描述的变更的机制。
        • Class,类类是所有元类的基础类,从而为所有类提供公共的方法,它定义了初始化类变量的initialize方法。Class的实例描述对象的表现和行为,它提供比ClassDescription更具描述性的设施,特别是,它增加了对类变量名字(addClassVarName:)和共享的池变量(addSharedPool:)的表示。它还提供比Behavior更综合性的编程支持设施,比如创建一个类的子类的消息:subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:
        • Metaclass,元类类是创建元类的类,它为所有元类提供公共的方法。Metaclass的关键性的消息,是自身初始化消息,这在GNU Smalltalk中依旧保留;一个是发送到Metaclass自身的消息subclassOf: superMeta,用来创建元类superMeta的一个子类;一个是发送到Metaclass的一个实例的消息,用来建立这个元类的唯一实例,对于建立完全初始化的类,它的每个参数都是需要的:name:environment:subclassOf:instanceVariableNames:shape:classVariableNames:poolDictionaries:category:

方法查找次序 编辑

下面是方法查找次序的辨析:

  • 每个终端对象,在查找方法时,都首先查找自己的类;然后按类继承链上溯,最后不经过Class(类类)和Metaclass(元类类),最终上至Object(对象类)。
  • 每个类,包括ClassMetaclass,在查找查找方法时,首先查找自己的元类;然后按元类继承链上溯,最终经过Object class(对象元类)而上至Class;接着按类继承链上溯,不经过与其并列的Metaclass,最终上至Object
  • 每个元类,包括Class classMetaclass class,在查找方法时,因为都是Metaclass的实例,所以首先查找Metaclass;然后按类继承链上溯,不经过与其并列的Class,最终上至Object

示意图 编辑

下面是两个示意图,二者都是纵向连线表示实例联系,而横向连线表示继承联系。实例联系以Metaclass(元类类)及其元类为顶端,而继承联系以Object(对象类)及其元类为中心,其中Object class(对象元类)继承Class(类类)是串接元类继承链与类继承链的关键环节。前者图示采用Smalltalk-80蓝皮书的样式(但旋转了180°),将Metaclass及其元类放置在最上方的独立两行,使得实例联系尽量成为树状向上汇聚;后者图示将Metaclass及其元类放置在最左边,使得继承联系尽量都在同一行之上。

例子 编辑

下列例子展示,从Smalltalk-80派生的SqueakPharo的样例代码的结构[4],它们的继承层级的根类实际上是ProtoObjectProtoObject封装了所有对象都必须拥有的极小化的消息集合,它被设计为引发尽可能多的错误,用来支持代理(proxy)定义[5]。例如Smalltalk-80的Object中,错误处理消息doesNotUnderstand:,和系统原始消息become:,就转而在ProtoObject中定义了。

在示意图中,纵向的绿色连接,展示继承联系的“子→父”关系(隐含的自下而上),横向的蓝色连接展示实例联系的“成员→容器”关系,从x出的发蓝色连接,指向x的最小实际容器,它是在调用在x上的方法时查找方法的继承链起点:

 
 r := ProtoObject. c := Class. mc := Metaclass. Object subclass: #A. A subclass: #B. u := B new. v := B new. 
 

这个结构由两个部份构成,用户部份有四个显式的对象和类及其两个隐式的元类:终端对象uv,它们连接到的类AB,它们两个连接到的右侧灰色节点表示的隐式的元类,其他的对象都是内建部份。

Objective-C元类 编辑

Objective-C中的元类,几乎同于Smalltalk-80的元类,这是因为Objective-C从Smalltalk引进了很多东西。就像Smalltalk,在Objective-C中实例变量和方法是对象的类定义的。类也是对象,因此它是元类的一个实例。

就像Smalltalk,在Objective-C中类方法,简单的是在类对象上调用的方法,因此一个类的类方法,必须定义为在它的元类中的实例方法。因为不同的类有不同的类方法集合,每个类都必须有它自己单独的元类。类和元类总是成对创建:运行时系统拥有函数objc_allocateClassPair()objc_registerClassPair()来分别的创建和注册类-元类对。

元类没有名字,但是到任何类对象的指针,可以通过泛化类型Class来提及(类似于用作到任何对象的指针的类型id)。

元类都是相同的类即根类元类的实例,而根类元类是自身的实例。因为类方法是通过继承联系来继承的,就像Smalltalk,除了根类元类之外,元类继承联系必须并行于类继承联系(比如说如果类A的父类是类B,则A的元类的父类是B的元类)。

不同于Smalltalk,根类元类继承自根类自身(通常为使用Cocoa框架的NSObject)。这确保了所有的元类最终都是根类的子类,从而人们可以将根类的实例方法,它们通常是针对对象有用的实用方法,使用于类对象自身上。

Python元类 编辑

Python中,内建的类type是元类[6][7]

 
r = object c = type class M(c): pass class A(metaclass=M): pass class B(A): pass b = B() 
 

类的定义,不包括它的实例对象的细节,如它们的字节为单位的大小,它们在内存中的二进制格局,它们是如何分配的,每次建立实例时自动调用的__init__方法,诸如此类。不只是在建立新实例对象的时候,而且在每次访问实例对象的任何特性的时候,这些细节都起到作用。在没有元类的语言中,这些细节是在语言规定中定义的,并且不能被覆写(override)。

在Python中,元类type控制着类行为的这些细节,默认定义出的类自身都是type的实例。新的元类可以很容易定义为type的子类从而覆写它,通过向类定义提供“关键字参数”metaclass就可以使用这个新的元类。

>>> type(b) <class '__main__.B'> >>> print(type(B), B.__bases__, [*B.__dict__]) <class '__main__.M'> (<class '__main__.A'>,) ['__module__', '__doc__'] >>> print(type(A), A.__bases__, [*A.__dict__]) <class '__main__.M'> (<class 'object'>,) ['__module__', '__dict__', '__weakref__', '__doc__'] >>> print(type(M), M.__bases__, [*M.__dict__]) <class 'type'> (<class 'type'>,) ['__module__', '__doc__'] >>> print(type(c), c.__bases__) <class 'type'> (<class 'object'>,) >>> print(type(r), r.__bases__) <class 'type'> () >>> sorted({*r.__dict__} & {*c.__dict__}) ['__delattr__', '__dir__', '__doc__', '__getattribute__', '__init__', '__new__', '__repr__', '__setattr__', '__sizeof__'] >>> sorted({*r.__dict__} - {*c.__dict__}) ['__class__', '__eq__', '__format__', '__ge__', '__gt__', '__hash__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__str__', '__subclasshook__'] >>> sorted({*c.__dict__} - {*r.__dict__}) ['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__dict__', '__dictoffset__', '__flags__', '__instancecheck__', '__itemsize__', '__module__', '__mro__', '__name__', '__prepare__', '__qualname__', '__subclasscheck__', '__subclasses__', '__text_signature__', '__weakrefoffset__', 'mro'] 

例子 编辑

考虑下面这个最简单的Python类:

class Car: def __init__(self, *args, **kwargs): self.__dict__.update(kwargs) def __call__(self, **kwargs): self.__dict__.update(kwargs) @property def description(self):  """返回这辆车的描述.""" return " ".join(str(value) for value in self.__dict__.values()) 
>>> new_car = Car(make='Toyota', model='Prius', year=2005, engine='Hybrid') >>> new_car(color='Green') >>> new_car.description 'Toyota Prius 2005 Hybrid Green' 

上面的例子包含了一些代码来处理初始化特性,也可以使用元类来完成这种任务:

class AttributeInitType(type): def __new__(*args, **kwargs):  """返回创建的实例类.""" cls = type.__new__(*args, **kwargs) def call(self, **kwargs): self.__dict__.update(kwargs) cls.__call__ = call # 为实例类增加__call__方法 return cls def __call__(cls, *args, **kwargs):  """返回为实例类创建的实例对象.""" obj = type.__call__(cls, *args) # 以正常缺省方式建立实例对象。 obj.__dict__.update(**kwargs) # 在这个新对象上设置属性。 return obj 

这个元类只覆写实例类和对象创建部份功能。元类行为的所有其他方面仍由type处理。现在可以重写类Car使用这个新元类:

class Car(object, metaclass=AttributeInitType): def __init__(self, *args): pass # 接收未预期的位置实际参数 @property def description(self):  """返回这辆车的描述.""" return " ".join(str(value) for value in self.__dict__.values()) 

Ruby元类 编辑

Ruby通过介入其自称的特征类(eigenclass),提炼了Smalltalk-80的元类概念,去除了Metaclass类,并重新定义了class-of映射。变更可以图示如下[8]

Smalltalk-80
隐式
元类
终端
对象
Ruby
类的
特征类
特征类的
特征类
终端
对象
终端对象的
特征类

特别要注意在Smalltalk的隐含的元类和Ruby类的特征类之间的对应。Ruby的特征类模型,使得隐式元类概念完全统一:所有对象x,都有它自己的元对象,它叫作x的特征类,它比x高一个元层级。高阶特征类通常是纯粹概念上的存在,在大多数Ruby程序中,它们不包含任何方法也不存储任何(其他)数据[9]

下面的示意图展示Ruby样例代码的核心结构[10]。这里的灰色节点表示打开Av特征类后扩张出来的特征类。

 
r = BasicObject c = Class class A; end class B < A; end u = B.new v = B.new class << A; end class << v; end 
 

图示还展示了Ruby中特征类的惰性求值v对象可以有它的特征类,作为向v增加“单例方法”的结果而被求值(被分配)。

在语言和工具中的支持 编辑

下面是支持元类的一些最显著的编程语言

一些不甚广泛传播的语言支持元类,包括OpenJava英语OpenJava、OpenC++、OpenAda、CorbaScript英语CorbaScript、ObjVLisp、Object-Z英语Object-Z、MODEL-K、XOTcl英语XOTcl和MELDC。其中几种语言可追溯日期至1990年代早期并具有学术价值[12]

Logtalk英语LogtalkProlog的面向对象扩展,它也支持元类。

资源描述框架(RDF)和统一建模语言(UML)二者都支持元类。

另见 编辑

引用 编辑

  1. ^ Ira R. Forman and Scott Danforth. Putting Metaclasses to Work. 1999. ISBN 0-201-43305-2. 
  2. ^ Alan Kay. . [2022-03-12]. (原始内容存档于2011-04-29). The most puzzling strange idea – at least to me as a new outsider – was the introduction of metaclasses (really just to make instance initialization a little easier – a very minor improvement over what Smalltalk-76 did quite reasonably already).
    Peter’s 1989 comment is typical and true: “metaclasses have proven confusing to many users, and perhaps in the balance more confusing than valuable.” In fact, in their PIE system, Goldstein and Bobrow had already implemented in Smalltalk on “observer language”, somewhat following the view-oriented approach Ihad been advocating and in some ways like the “perspectives” proposed in KRL [Goldstein *].
    Once one can view an instance via multiple perspectives even “sem-metaclasses” like Class Class and Class Object are not really necessary since the object-role and instance-of-a-class-role are just different views and it is easy to deal with life-history issues includeding instantiation. This was there for the taking (along with quite a few other good ideas), but it wsn’t adopted. My guess is that Smalltalk had moved into the final phase I memntioned at the beginning of this story, in which a way of doing things finally gets canonized into an inflexible belief structure.
     
  3. ^ Learning Research Group. (PDF) (报告). Xerox Palo Alto Research Center. October 1979 [2022-03-13]. (原始内容 (PDF)存档于2022-04-12). To define a new class, select a class category in the first pane of the browse window. This selection specifies the category to which the new class will be added, and causes a template to appear in the largest pane of the browse window, the code pane. ……
    The template presented in the code pane looks as follows
        Class new title: ’NameofClass’
        subclassof: Object
        fields: ’names of fields’
        declare: ’names of class variables’
     
  4. ^ The core structure of Smalltalk-80. . [2021-03-29]. (原始内容存档于2021-05-06). 
  5. ^ . [2022-01-16]. (原始内容存档于2022-03-12). 
  6. ^ IBM Metaclass programming in Python, parts 1 (页面存档备份,存于互联网档案馆), 2 (页面存档备份,存于互联网档案馆) and 3 (页面存档备份,存于互联网档案馆
  7. ^ Artima Forum: Metaclasses in Python 3.0 (part 1 of 2) (页面存档备份,存于互联网档案馆) (part 2 of 2) (页面存档备份,存于互联网档案馆
  8. ^ Introduction - Introductory sample. . [2021-03-29]. (原始内容存档于2021-05-06). 
  9. ^ Paolo Perrotta. (PDF). Pragmatic Bookshelf. 2014 [2022-03-30]. ISBN 978-1-94122-212-6. (原始内容 (PDF)存档于2022-05-15). 
  10. ^ The core structure of Ruby - Eigenclass actuality. . [2021-03-29]. (原始内容存档于2021-05-06). 
  11. ^ Herb Sutter. Metaclasses (PDF). [2020-09-25]. (原始内容 (PDF)于2020-11-11). 
  12. ^ (PDF). [2007-11-27]. (原始内容 (PDF)存档于2007-10-16). 


元类, 在面向对象程序设计中, 英語, metaclass, 是一种实例是类的类, 普通的类定义的是特定对象的行为, 定义的则是特定的类及其实例的行为, 不是所有面向对象编程语言都支持, 在它能做的事情之中, 可以覆写任何给定方面类行为的程度是不同的, 可以通过使类成为头等对象来实现, 在这种情况下简单的就是构造类的一个对象, 每个语言都有它自己的元对象协议, 给出对象, 类和如何交互的规则, 目录, smalltalk, 实例联系, 继承联系, 继承层级, 方法查找次序, 示意图, 例子, objective, . 在面向对象程序设计中 元类 英語 metaclass 是一种实例是类的类 普通的类定义的是特定对象的行为 元类定义的则是特定的类及其实例的行为 不是所有面向对象编程语言都支持元类 在它能做的事情之中 元类可以覆写任何给定方面类行为的程度是不同的 元类可以通过使类成为头等对象来实现 在这种情况下元类简单的就是构造类的一个对象 每个语言都有它自己的元对象协议 给出对象 类和元类如何交互的规则 1 目录 1 Smalltalk 80元类 1 1 实例联系 1 2 继承联系 1 3 继承层级 1 4 方法查找次序 1 5 示意图 1 6 例子 2 Objective C元类 3 Python元类 3 1 例子 4 Ruby元类 5 在语言和工具中的支持 6 另见 7 引用Smalltalk 80元类 编辑在Smalltalk中 所有东西都是对象 此外 Smalltalk是基于类的系统 这意味着所有对象都有一个类 它定义这个对象的结构 比如说这个类拥有实例变量 和这个对象所理解的消息 二者在一起蕴含了 在Smalltalk中 类是一个对象 因此类也需要是它的元类的实例 2 元类在Smalltalk 80系统中的主要角色 是提供协议来初始化类变量 和建立元类的唯一实例 也就是其对应的类 的初始化实例 实例联系 编辑 为了允许类拥有它们自己的方法 和叫作类实例变量它们自己的实例变量 英语 Instance variable Smalltalk 80为每个类C介入了它们自己的元类C class 就像实例方法实际上属于类一样 类方法实际上属于元类 在类中定义实例变量 英语 Instance variable 和类变量 英语 Class variable 而在元类中定义类实例变量 每个元类在效果上都是单例类 就像连体双胞胎 类和元类是共生的 元类有一个实例变量thisClass 它指向它结合的类 平常的Smalltalk类浏览器 英语 class browser 不将元类展示为单独的类 转而允许一起同时编辑类和它的元类 要得到一个实例的类 需要向它发送消息调用class方法 类和元类继承了其超类的name方法 它返回接收者名字的字符串 例如 轿车对象c是类Car的实例 则c class返回Car类对象 而c class name返回 Car 依次类推 Car class返回Car的元类对象 而Car class name返回依赖于实现 有的是nil 即没有名字 有的是 Car class 即用空格分隔的类名字和 class 在早期的Smalltalk 76中 创建新类的方式是向Class类发送new消息 3 在Smalltalk 80中 Class是元类的基础类 它是类而不是元类 所有元类都是一个Metaclass类的实例 Metaclass类是Metaclass class的实例 而Metaclass class作为元类 也是Metaclass类的实例 继承联系 编辑 在Smalltalk 80中 终端对象是一个整数 一个组件 或一台车等 而类是像Integer 或Widget或Car等这样的东西 除了Object之外 所有的类都有一个超类 元类所继承的元类 就是元类对应的类所继承的类的元类 在一个消息被发送到对象的时候 方法的查找开始于它的类 如果没有找到则在上行超类链 停止于Object而不管找到与否 在一个消息被发送到一个类的时候 类方法查找开始于它的元类 并上行超类链至Object class 直至Object class 元类的超类层级并行于类的超类层级 在Smalltalk 80中 Object class是Class的子类 Object class superclass Class 类方法的查找在元类链之后仍可继续下去 所有元类都是Class的在继承层级中的子类 它是所有元类的抽象超类 它描述这些类的一般性质 继而最终可上溯至Object 继承层级 编辑 四个类提供描述新类的设施 下面是它们的继承层级 起自Object 和它们提供的主要设施 Object 对象类是所有类的基础类 它为所有对象提供公共的方法 即公共的缺省行为 至少包括了 测试对象的功能比如class方法 比较对象 对象复制 访问对象的各部份 打印和存储对象 错误处理 Behavior 行为类定义了拥有实例的对象所需要的最小状态 英语 State computer science 它提供建立一个类的实例的new方法 特别是 它定义了Smalltalk 80解释器所用到的状态 并为编译方法源代码提供到编译器的基本接口 如compile 等方法 Behavior描述的这个状态 包括了一个类层级连接 superclass 一个方法字典 methodDictionary addSelector withMethod 和对实例的描述 依据数目和对它们的变量的表示 尽管一个类的多数设施都规定在Behavior中 但很多消息不能于此实现 对类的完全描述转而在它的子类之中提供 ClassDescription 类描述类为Class和Metactass提供了共同的超类 它表现类命名 name 类注释 comment 和命名实例变量 addlnstVarName 特别是 它增加了组织在方法字典中方法 compile classified 和类自身 category 的结构 它还提供了在外部串流 文件 上存储完全的类描述的机制 和记述对类描述的变更的机制 Class 类类是所有元类的基础类 从而为所有类提供公共的方法 它定义了初始化类变量的initialize方法 Class的实例描述对象的表现和行为 它提供比ClassDescription更具描述性的设施 特别是 它增加了对类变量名字 addClassVarName 和共享的池变量 addSharedPool 的表示 它还提供比Behavior更综合性的编程支持设施 比如创建一个类的子类的消息 subclass instanceVariableNames classVariableNames poolDictionaries category Metaclass 元类类是创建元类的类 它为所有元类提供公共的方法 Metaclass的关键性的消息 是自身初始化消息 这在GNU Smalltalk中依旧保留 一个是发送到Metaclass自身的消息subclassOf superMeta 用来创建元类superMeta的一个子类 一个是发送到Metaclass的一个实例的消息 用来建立这个元类的唯一实例 对于建立完全初始化的类 它的每个参数都是需要的 name environment subclassOf instanceVariableNames shape classVariableNames poolDictionaries category 方法查找次序 编辑 下面是方法查找次序的辨析 每个终端对象 在查找方法时 都首先查找自己的类 然后按类继承链上溯 最后不经过Class 类类 和Metaclass 元类类 最终上至Object 对象类 每个类 包括Class和Metaclass 在查找查找方法时 首先查找自己的元类 然后按元类继承链上溯 最终经过Object class 对象元类 而上至Class 接着按类继承链上溯 不经过与其并列的Metaclass 最终上至Object 每个元类 包括Class class和Metaclass class 在查找方法时 因为都是Metaclass的实例 所以首先查找Metaclass 然后按类继承链上溯 不经过与其并列的Class 最终上至Object 示意图 编辑 下面是两个示意图 二者都是纵向连线表示实例联系 而横向连线表示继承联系 实例联系以Metaclass 元类类 及其元类为顶端 而继承联系以Object 对象类 及其元类为中心 其中Object class 对象元类 继承Class 类类 是串接元类继承链与类继承链的关键环节 前者图示采用Smalltalk 80蓝皮书的样式 但旋转了180 将Metaclass及其元类放置在最上方的独立两行 使得实例联系尽量成为树状向上汇聚 后者图示将Metaclass及其元类放置在最左边 使得继承联系尽量都在同一行之上 nbsp Smalltalk 80的类和元类层级 使用UML类图表示 这里从左至右 第一列是Class class 类元类 和Class 类类 第二列是ClassDescription class 类描述元类 和ClassDescription 类描述类 第三列是Behavior class 行为元类 和Behavior 行为类 第四列是Object class 对象元类 和Object 对象类 第五列是Metaclass class 元类元类 Metaclass 元类类 Car class Car元类 Car Car类 和c Car Car实例 nbsp Smalltalk中在类和元类之间的继承和实例联系的示意图 这里从左至右 第一列是Metaclass元类和Metaclass 元类类 第二列是Class元类和Class 类类 第三列是ClassDescription元类与Behavior元类 和ClassDescription 类描述类 与Behavior 行为类 第四列是Object元类 Object 对象类 和Object实例 第五列是Foo元类 Foo类和Foo实例 第六列是Bar元类 Bar类和Bar实例 例子 编辑 下列例子展示 从Smalltalk 80派生的Squeak和Pharo的样例代码的结构 4 它们的继承层级的根类实际上是ProtoObject ProtoObject封装了所有对象都必须拥有的极小化的消息集合 它被设计为引发尽可能多的错误 用来支持代理 proxy 定义 5 例如Smalltalk 80的Object中 错误处理消息doesNotUnderstand 和系统原始消息become 就转而在ProtoObject中定义了 在示意图中 纵向的绿色连接 展示继承联系的 子 父 关系 隐含的自下而上 横向的蓝色连接展示实例联系的 成员 容器 关系 从x出的发蓝色连接 指向x的最小实际容器 它是在调用在x上的方法时查找方法的继承链起点 r ProtoObject c Class mc Metaclass Object subclass A A subclass B u B new v B new nbsp 这个结构由两个部份构成 用户部份有四个显式的对象和类及其两个隐式的元类 终端对象u和v 它们连接到的类A和B 它们两个连接到的右侧灰色节点表示的隐式的元类 其他的对象都是内建部份 Objective C元类 编辑在Objective C中的元类 几乎同于Smalltalk 80的元类 这是因为Objective C从Smalltalk引进了很多东西 就像Smalltalk 在Objective C中实例变量和方法是对象的类定义的 类也是对象 因此它是元类的一个实例 nbsp 在Objective C中在类和元类之间的继承和实例联系的示意图 注意Objective C有多个根类 每个根类都有独立的层级 这个示意图只展示了例子根类NSObject的层级 每个其他根类都有类似的层级 就像Smalltalk 在Objective C中类方法 简单的是在类对象上调用的方法 因此一个类的类方法 必须定义为在它的元类中的实例方法 因为不同的类有不同的类方法集合 每个类都必须有它自己单独的元类 类和元类总是成对创建 运行时系统拥有函数objc allocateClassPair 和objc registerClassPair 来分别的创建和注册类 元类对 元类没有名字 但是到任何类对象的指针 可以通过泛化类型Class来提及 类似于用作到任何对象的指针的类型id 元类都是相同的类即根类元类的实例 而根类元类是自身的实例 因为类方法是通过继承联系来继承的 就像Smalltalk 除了根类元类之外 元类继承联系必须并行于类继承联系 比如说如果类A的父类是类B 则A的元类的父类是B的元类 不同于Smalltalk 根类元类继承自根类自身 通常为使用Cocoa框架的NSObject 这确保了所有的元类最终都是根类的子类 从而人们可以将根类的实例方法 它们通常是针对对象有用的实用方法 使用于类对象自身上 Python元类 编辑在Python中 内建的类type是元类 6 7 r object c type class M c pass class A metaclass M pass class B A pass b B nbsp 类的定义 不包括它的实例对象的细节 如它们的字节为单位的大小 它们在内存中的二进制格局 它们是如何分配的 每次建立实例时自动调用的 init 方法 诸如此类 不只是在建立新实例对象的时候 而且在每次访问实例对象的任何特性的时候 这些细节都起到作用 在没有元类的语言中 这些细节是在语言规定中定义的 并且不能被覆写 override 在Python中 元类type控制着类行为的这些细节 默认定义出的类自身都是type的实例 新的元类可以很容易定义为type的子类从而覆写它 通过向类定义提供 关键字参数 metaclass就可以使用这个新的元类 gt gt gt type b lt class main B gt gt gt gt print type B B bases B dict lt class main M gt lt class main A gt module doc gt gt gt print type A A bases A dict lt class main M gt lt class object gt module dict weakref doc gt gt gt print type M M bases M dict lt class type gt lt class type gt module doc gt gt gt print type c c bases lt class type gt lt class object gt gt gt gt print type r r bases lt class type gt gt gt gt sorted r dict amp c dict delattr dir doc getattribute init new repr setattr sizeof gt gt gt sorted r dict c dict class eq format ge gt hash init subclass le lt ne reduce reduce ex str subclasshook gt gt gt sorted c dict r dict abstractmethods base bases basicsize call dict dictoffset flags instancecheck itemsize module mro name prepare qualname subclasscheck subclasses text signature weakrefoffset mro 例子 编辑 考虑下面这个最简单的Python类 class Car def init self args kwargs self dict update kwargs def call self kwargs self dict update kwargs property def description self 返回这辆车的描述 return join str value for value in self dict values gt gt gt new car Car make Toyota model Prius year 2005 engine Hybrid gt gt gt new car color Green gt gt gt new car description Toyota Prius 2005 Hybrid Green 上面的例子包含了一些代码来处理初始化特性 也可以使用元类来完成这种任务 class AttributeInitType type def new args kwargs 返回创建的实例类 cls type new args kwargs def call self kwargs self dict update kwargs cls call call 为实例类增加 call 方法 return cls def call cls args kwargs 返回为实例类创建的实例对象 obj type call cls args 以正常缺省方式建立实例对象 obj dict update kwargs 在这个新对象上设置属性 return obj 这个元类只覆写实例类和对象创建部份功能 元类行为的所有其他方面仍由type处理 现在可以重写类Car使用这个新元类 class Car object metaclass AttributeInitType def init self args pass 接收未预期的位置实际参数 property def description self 返回这辆车的描述 return join str value for value in self dict values Ruby元类 编辑Ruby通过介入其自称的特征类 eigenclass 提炼了Smalltalk 80的元类概念 去除了Metaclass类 并重新定义了class of映射 变更可以图示如下 8 Smalltalk 80 类 隐式元类终端对象 Ruby 类 类的特征类 特征类的特征类终端对象 终端对象的特征类特别要注意在Smalltalk的隐含的元类和Ruby类的特征类之间的对应 Ruby的特征类模型 使得隐式元类概念完全统一 所有对象x 都有它自己的元对象 它叫作x的特征类 它比x高一个元层级 高阶特征类通常是纯粹概念上的存在 在大多数Ruby程序中 它们不包含任何方法也不存储任何 其他 数据 9 下面的示意图展示Ruby样例代码的核心结构 10 这里的灰色节点表示打开A和v特征类后扩张出来的特征类 r BasicObject c Class class A end class B lt A end u B new v B new class lt lt A end class lt lt v end nbsp 图示还展示了Ruby中特征类的惰性求值 v对象可以有它的特征类 作为向v增加 单例方法 的结果而被求值 被分配 在语言和工具中的支持 编辑下面是支持元类的一些最显著的编程语言 Common Lisp 通过CLOS Delphi和受它影响的其他Object Pascal版本 Groovy Objective C Python Perl 通过元类pragma 还有Moose 英语 Moose Perl Ruby Smalltalk C 规划用于C 23 11 一些不甚广泛传播的语言支持元类 包括OpenJava 英语 OpenJava OpenC OpenAda CorbaScript 英语 CorbaScript ObjVLisp Object Z 英语 Object Z MODEL K XOTcl 英语 XOTcl 和MELDC 其中几种语言可追溯日期至1990年代早期并具有学术价值 12 Logtalk 英语 Logtalk 是Prolog的面向对象扩展 它也支持元类 资源描述框架 RDF 和统一建模语言 UML 二者都支持元类 另见 编辑元编程 元对象 反射式编程引用 编辑 Ira R Forman and Scott Danforth Putting Metaclasses to Work 1999 ISBN 0 201 43305 2 Alan Kay The Early History of Smalltalk 2022 03 12 原始内容存档于2011 04 29 The most puzzling strange idea at least to me as a new outsider was the introduction of metaclasses really just to make instance initialization a little easier a very minor improvement over what Smalltalk 76 did quite reasonably already Peter s 1989 comment is typical and true metaclasses have proven confusing to many users and perhaps in the balance more confusing than valuable In fact in their PIE system Goldstein and Bobrow had already implemented in Smalltalk on observer language somewhat following the view oriented approach Ihad been advocating and in some ways like the perspectives proposed in KRL Goldstein Once one can view an instance via multiple perspectives even sem metaclasses like Class Class and Class Object are not really necessary since the object role and instance of a class role are just different views and it is easy to deal with life history issues includeding instantiation This was there for the taking along with quite a few other good ideas but it wsn t adopted My guess is that Smalltalk had moved into the final phase I memntioned at the beginning of this story in which a way of doing things finally gets canonized into an inflexible belief structure Learning Research Group How To Use the Smalltalk 76 System PDF 报告 Xerox Palo Alto Research Center October 1979 2022 03 13 原始内容 PDF 存档于2022 04 12 To define a new class select a class category in the first pane of the browse window This selection specifies the category to which the new class will be added and causes a template to appear in the largest pane of the browse window the code pane The template presented in the code pane looks as follows br Class new title NameofClass br subclassof Object br fields names of fields br declare names of class variables The core structure of Smalltalk 80 Object Membership The Core Structure of Object Technology 2021 03 29 原始内容存档于2021 05 06 ProtoObject 2022 01 16 原始内容存档于2022 03 12 IBM Metaclass programming in Python parts 1 页面存档备份 存于互联网档案馆 2 页面存档备份 存于互联网档案馆 and 3 页面存档备份 存于互联网档案馆 Artima Forum Metaclasses in Python 3 0 part 1 of 2 页面存档备份 存于互联网档案馆 part 2 of 2 页面存档备份 存于互联网档案馆 Introduction Introductory sample Object Membership The Core Structure of Object Technology 2021 03 29 原始内容存档于2021 05 06 Paolo Perrotta Metaprogramming Ruby 2 PDF Pragmatic Bookshelf 2014 2022 03 30 ISBN 978 1 94122 212 6 原始内容 PDF 存档于2022 05 15 The core structure of Ruby Eigenclass actuality Object Membership The Core Structure of Object Technology 2021 03 29 原始内容存档于2021 05 06 Herb Sutter Metaclasses PDF 2020 09 25 原始内容存档 PDF 于2020 11 11 An implementation of mixins in Java using metaclasses PDF 2007 11 27 原始内容 PDF 存档于2007 10 16 取自 https zh wikipedia org w index php title 元类 amp oldid 80877498, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。