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

作者:Jeff Knupp

原文地址:https://jeffknupp.com/blog/2013/12/28/improve-your-python-metaclasses-and-dynamic-classes-with-type/

关键字metaclasstype都是很少用到(因此大多数人没有很好理解)的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__属性设置为你构建的metaclassMetaclass可以是任意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), {})

这什么时候更有用?

一旦向别人解释,typemetaclass是接下来一个问题是“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)

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

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值