Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

理解python的metaclass

$
0
0
理解python的metaclass

2小时前来源:51CTO

前言

这篇博客是我在stack overflow上看了一个提问回复后写的,例子基本用的都是e-satis本人的例子,语言组织也基本按照翻译来。

但我并不是一个翻译者,并不会严格遵守每行每句的翻译;有时候我会将表述换个顺序,省略一些我认为无关紧要的话,以便读者更好理解。

所以,如果你不喜欢我的语言表述,或者想要看英文原文,可以点击此链接去查看原回复。

类也是对象

在理解metaclass之前,我们先要掌握python中的类(class)是什么。

python中类的概念,是借鉴自smalltalk语言。

在大部分语言中,类指的是"描述如何产生一个对象(object)"的一段代码,这对于python也是如此。

但是,在python中,类远不止如此,类同时也是对象。当你遇到关键词class的时候,python就会自动执行产生一个对象。下面的代码段中:

python在内存中产生了一个名叫做"ObjectCreator"的对象。这个对象(类)自身拥有产生对象(实例instance)的能力。 这就是为什么称呼这东西(后面遇到容易混淆的地方,我们称之为:类对象)也是类的原因。同时,它也是一个对象,因此你可以对它做如下操作:

既然类也是对象,那么我们就可以在运行的时候创建它,跟创建对象一样自然。

实际上,type还有一个完全不同的功能,它可以在运行时产生类。type可以传入一些参数,然后返回一个类。(好吧,必须承认,根据不同的传入参数,一个相同的函数type居然会有两个完全不同的作用,这很愚蠢。不过python这样做是为了保持向后兼容性。)

下面举例type创建类的用法。首先,对于类一般是这么定义的:

metaclass 就是创建类的那家伙。(事实上,type就是一个metaclass)

我们知道,我们定义了class就是为了能够创建object的,没错吧?

我们也学习了,python中类也是对象。

那么,metaclass就是用来创造“类对象”的类.它是“类对象”的“类”。

可以这样子来理解:


php?url=0El8HdGHSz" alt="理解python的metaclass" />
MyClass = MetaClass MyObject = MyClass

也可以用我们上面学到的type来表示:

MyClass = type('MyClass', , {})

说白了,函数type就是一个特殊的metaclass.

python在背后使用type创造了所有的类。type是所有类的metaclass.

我们可以使用__class__属性来验证这个说法.

在python中,一切皆为对象:整数、字符串、函数、类.所有这些对象,都是通过类来创造的.

>>> age = 35 >>> age.__class__ 'int'> >>> name = 'bob' >>> name.__class__ 'str'> >>> def foo: pass >>> foo.__class__ 'function'> >>> class Bar(object): pass >>> b = Bar >>> b.__class__ '__main__.Bar'>

那么,__class__的__class__又是什么呢?

>>> age.__class__.__class__ 'type'> >>> name.__class__.__class__ 'type'> >>> foo.__class__.__class__ 'type'> >>> b.__class__.__class__ 'type'>

metaclass就是创造类对象的工具.如果你喜欢,你也可以称之为"类的工厂".

type是pythonQg置的metaclass。不过,你也可以编写自己的metaclass.

__metaclass__ 属性

我们可以在一个类中加入 __metaclass__ 属性.

当你这么做了,python就会使用metaclass来创造类:Foo。

注意啦,这里有些技巧的。

当你写下class Foo(object)的时候,类对象Foo还没有在内存中生成。

python会在类定义中寻找__metaclass__ 。如果找到了,python就会使用这个__metaclass__ 来创造类对象: Foo。如果没找到,python就使用type来创造Foo。

请把下面的几段话重复几遍:

class Foo(Bar): pass

python做了以下事情:

Foo中有__metaclass__这个属性吗?

如果有,python会在内存中通过__metaclass__创建一个名字为Foo的类对象。

如果python没有在Foo中找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__,并尝试做和前面同样的操作。

如果python由下往上遍历父类也都没有找不到__metaclass__,它就会在模块(module)中去寻找__metaclass__,并尝试做同样的操作。

如果还是没有找不到__metaclass__, python才会用内置的type(这也是一个metaclass)来创建这个类对象。

现在问题来了,我们要怎么用代码来实现__metaclass__呢? 写一些可以用来产生类(class)的东西就行。

那什么可以产生类?无疑就是type,或者type的任何子类,或者任何使用到type的东西都行.

自定义metaclass

使用metaclass的主要目的,是为了能够在创建类的时候,自动地修改类。

一个很傻的需求,我们决定要将该模块中的所有类的属性,改为大写。

有几种方法可以做到,这里使用__metaclass__来实现.

在模块的层次定义metaclass,模块中的所有类都会使用它来创造类。我们只需要告诉metaclass,将所有的属性转化为大写。

# type也是一个类,我们可以继承它. class UpperAttrMetaclass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回这个对象 # 而__init__只是将传入的参数初始化给对象 # 实际中,你很少会用到__new__,除非你希望能够控制对象的创建 # 在这里,类是我们要创建的对象,我们希望能够自定义它,所以我们改写了__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__,但这里我们就先不这样. def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): uppercase_attr = {} for name, val in future_class_attr.items: if not name.startswith('__'): uppercase_attr[name.upper] = val else: name] = val return type(future_class_name, future_class_parents, uppercase_attr)

这里的方式其实不是OOP(面向对象编程).因为我们直接调用了type,而不是改写父类的__type__方法.

所以我们也可以这样子处理:

class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): uppercase_attr = {} for name, val in future_class_attr.items: if not name.startswith('__'): uppercase_attr[name.upper] = val else: name] = val return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)

这样子看,我们只是复用了 type.__new__方法,这就是我们熟悉的基本的OOP编程,没什么魔法可言.

你可能注意到,__new__方法相比于

type(future_class_name, future_class_parents, future_class_attr)

多了一个参数: upperattr_metaclass, 请别在意,这没什么特别的: __new__总是将"它要定义的类"作为第一个参数。

这就好比是 self 在类的一般方法(method)中一样,也是被作为第一个参数传入。

当然啦,这里的名字的确是我起的太长了。就像self一样,所有的参数都有它们传统的名称。因此,在实际的代码中,一个metaclass应该是写成下面样子的:

(我们同时使

Viewing all articles
Browse latest Browse all 9596

Latest Images

Trending Articles