提高你的Python:具有类型的元类与动态类!

关键字 metaclass 与 type 都是很少用到(因此大多数人没有很好理解)的 Python 构造。在本文里,我们将探索 type() 不同的“类型”,以及 type 少有人知的使用如何与 metaclass 关联。

你是我的类型吗?

Type() 的第一个使用是最广为人知的:确定一个对象的类型。这里, Python 新手通常会打断且说道,“不过我认为 Python 没有类型!”相反, Python 里每样东西都要一个类型(甚至类型本身!)因为一切都是对象。让我们看几个例子:

>>> type ( 1 )

< class ' int '>

>>> type ( 'foo' )

< class ' str '>

>>> type ( 3.0 )

< class ' float '>

>>> type ( float )

< class ' type '>

type的类型

一切如预期,直到我们检查 float 的类型。 <class ’type’> ?这是什么?好吧,很奇怪,但让我们继续:

>>> class Foo ( object ):

... pass

...

>>> type (Foo)

< class ' type '>

啊!又一次 <class ‘type’> 。显然,所有类本身的类型是 type (不管它们是内置的还是用户定义的)。 Type 本身的类型是什么呢?

>>> type ( type )

< class ' type '>

好吧,它在某处停止了。 Type 是所有类型的类型,包括自己。事实上, type 是一个 metaclass ,或者“构建类的一个事物”。类,比如 list() ,就像在 my_list = list() 里,构建了该类的实例。同样, metaclass 构建类型,就像下面的 Foo (译注: Foo 类是一个用户定义类型):

class Foo ( object ):

pass

运转你自己的metaclass

就像普通的类, metaclass 可以是用户定义的。要使用它,你将一个类的 __metaclass__ 属性设置为你构建的 metaclass 。 Metaclass 可以是任意 callable ,只要它返回一个类型。通常,你将一个类的 __metaclass__ 赋给一个函数,在某处,该函数使用我们尚未讨论的 type 的一个变体:用于创建类的三样参数。

Type的黑暗面

如提到的,事实证明,在使用 3 个实参调用时, type 有一个完全独立的用途。 Type(name, bases, dict) 以编程方式创建了一个新类型。如果我有以下代码:

class Foo ( object ):

pass

使用下面的代码我们可以实现完全相同的效果:

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

现在 Foo 引用名为 Foo 的一个类,其基类是 object (使用 type 创建的类,如果没有指定一个基类,会自动做成新形式的类)。

这都很好,但如果我们向向 Foo 添加成员函数会怎么样呢?很容易通过设置 Foo 的属性实现这,就像这样:

def always_false ( self ):

return False

Foo . always_false = always_false

我们可以使用下面的代码一口气完成:

Foo = type ( 'Foo' , (), { 'always_false' : always_false})

当然, bases 参数是 Foo 的一组基类。我们把它留空,但创建一个从 Foo 派生的新类完全是合法的,再次使用 type 来创建它:

FooBar = type ( 'FooBar' , (Foo), {})

这什么时候更有用?

一旦向别人解释, type 与 metaclass 是接下来一个问题是“ OK ,那么我什么时候用它呢?”的其中一个话题。答案是,不经常。不过,有时使用 type 动态创建类是合适的解决方案。让我们看一下一个例子。

Sandman 是我编写的库,用来自动生成一个 REST API 以及用于现有数据库、基于 web 的管理接口的一个接口(无需要求任何样板代码, boilerplate code )。大部分繁重的工作由 SQLAlchemy 完成,一个 ORM 框架。

使用 SQLAlchemy 注册一个数据库表仅有一个方式:创建一个描述该表的一个 Model 类(并非不像 Django 的模型)。为了让 SQLAlchemy 认识一张表,必须以某种方式创建用于该表的一个类。因为 sandman 预先不知道数据库的结构,它不能依赖预制的模型类来注册表。相反,它需要內省( introspect )数据库,同时创建这些类。听起来很熟悉?任何时候你动态地创建新类时, type 是正确的 / 仅有的选择。

下面是 sandman 的相关代码:

if not current_app . endpoint_classes:

db . metadata . reflect(bind = db . engine)

for name in db . metadata . tables():

cls = type ( str (name), (sandman_model, db . Model),

{ '__tablename__' : name})

register(cls)

提高你的Python:具有类型的元类与动态类!

正如你看到的,如果使用者没有为一张表手动创建一个模型类,它被自动创建,带有一个设置为该表名字的 __tablename__ 属性( SQLAlchemy 用来将表匹配到类)。

总结

在本文,我们讨论了 type , metaclass 的两个使用,以及何时要求 type 的另一个使用。虽然 metaclass 是一个有点令人混淆的概念,希望你现在有一个好的基础,可以开展进一步的学习。

进群:960410445 即可获取数十套PDF!

猜你喜欢

转载自blog.csdn.net/qq_42156420/article/details/85987987