整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

python 动态创建字典_一、Python 中类的定义、创建、使用

Python 界的领袖 Tim Peters 说的:

元类就是深度的魔法,99% 的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。

所以,这篇文章,认真阅读一遍就好了。

目录

3d80cf2207a61df9dce14481201363b3.png

一、Python 中类也是对象

在了解元类之前,我们先进一步理解 Python 中的类,在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在 Python 中这一点也是一样的。

这点在学习类的章节也强调过了,下面可以通过例子回忆一下:

25d38469f8e7bcf784a80e0e9b81fd8a.png

但是,Python 中的类有一点跟大多数的编程语言不同,在 Python 中,可以把类理解成也是一种对象。对的,这里没有写错,就是对象。

为什么呢?

因为只要使用关键字 class ,Python 解释器在执行的时候就会创建一个对象。

如:

class (object): pass

当程序运行这段代码的时候,就会在内存中创建一个对象,名字就是。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。

但是,它的本质仍然是一个对象,于是我们可以对它做如下的操作:

014e7ed5961d9ae18d7b719b7c3081a7.png

# 可以直接打印一个类,因为它其实也是一个对象print()# 可以直接把一个类作为参数传给函数(注意这里是类,是没有实例化的)echo()# 也可以直接把类赋值给一个变量 = ()

输出的结果如下:

dcc3452d1f163d3efaf5ce6ca66366e2.png

二、使用 type() 动态创建类

因为类也是对象,所以我们可以在程序运行的时候创建类。

Python 是动态语言。

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

在之前,我们先了了解下 type() 函数。

首先我们新建一个 hello.py 的模块,然后定义一个 Hello 的 class ,

c99de33a6b7c7ec06ebdcf59a85d500c.png

然后在另一个模块中引用 hello 模块,并输出相应的信息。

其中 type() 函数的作用是可以查看一个类型和变量的类型。

a93aa42e1bbe8448995cbcfe1f459d90.png

上面也提到过,type() 函数可以查看一个类型或变量的类型,Hello 是一个 class ,它的类型就是 type ,而 h 是一个实例,它的类型就是 com..hello.Hello。

前面的 com. 是我的包名,hello 模块在该包名下。

在这里还要细想一下,上面的例子中,我们使用 type() 函数查看一个类型或者变量的类型。

其中查看了一个 Hello class 的类型,打印的结果是: 。

其实 type() 函数不仅可以返回一个对象的类型,也可以创建出新的类型。

class 的定义是运行时动态创建的,而创建 class 的方法就是使用 type() 函数。

比如我们可以通过 type() 函数创建出上面例子中的 Hello 类,具体看下面的代码:

e4156c0fbe83dfc03e396fda9bfdb312.png

在这里,需先了解下通过 type() 函数创建 class 对象的参数说明:

创建类时首先要定义类的_如何定义类及创建对象_类创建的对象称为什么

1、class 的名称,比如例子中的起名为 Hello

2、继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,tuple 要使用单元素写法;例子中继承 object 类,因为是单元素的 tuple ,所以写成 (object,)

3、class 的方法名称与函数绑定;例子中将函数 绑定在方法名 hello 中

具体的模式如下:

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

好了,了解完具体的参数使用之外,我们看看输出的结果,可以看到,通过 type() 函数创建的类和直接写 class 是完全一样的。

这是因为Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用 type() 函数创建出 class 的。

不过一般的情况下,我们都是使用 class ***... 的方法来定义类的,不过 type() 函数也可以让我们创建出类来。

也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

可以看到,在 Python 中,类也是对象,你可以动态的创建类。

其实这也就是当你使用关键字 class 时 Python 在幕后做的事情,而这就是通过元类来实现的。

三、什么是元类

通过上面的介绍,终于模模糊糊的带到元类这里来了。可是我们到现在还不知道元类是什么鬼东西。

我们创建类的时候,大多数是为了创建类的实例对象。

那么元类呢?

元类就是用来创建类的。也可以换个理解方式就是:元类就是类的类。

通过上面 type() 函数的介绍,我们知道可以通过 type() 函数创建类:

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

实际上 type() 函数是一个元类。

type() 就是 Python 在背后用来创建所有类的元类。

那么现在我们也可以猜到一下为什么 type() 函数是 type 而不是 Type呢?

这可能是为了和 str 保持一致性,str 是用来创建字符串对象的类,而 int 是用来创建整数对象的类。

type 就是创建类对象的类。

你可以通过检查 属性来看到这一点。

Python 中所有的东西,注意喔,这里是说所有的东西,他们都是对象。

这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来。

d6762c96f05cdf735e62d6728420e496.png

输出的结果如下:

可以看到,上面的所有东西,也就是所有对象都是通过类来创建的,那么我们可能会好奇, 的 会是什么呢?

换个说法就是,创建这些类的类是什么呢?

我们可以继续在上面的代码基础上新增下面的代码:

d499165102675bea39d53442321262ac.png

认真观察,再理清一下,上面输出的结果是我们把整形 age ,字符创 name ,函数 fu 和对象实例 mEat 里 的 打印出来的结果。

也可以说是他们类的类打印结果。发现打印出来的 class 都是 type 。

一开始也提到了,元类就是类的类。

也就是元类就是负责创建类的一种东西。

你也可以理解为,元类就是负责生成类的。

而 type 就是内建的元类。也就是 Python 自带的元类。

四、自定义元类

类创建的对象称为什么_如何定义类及创建对象_创建类时首先要定义类的

到现在,我们已经知道元类是什么鬼东西了。

那么,从始至终我们还不知道元类到底有啥用。

只是了解了一下元类。

在了解它有啥用的时候,我们先来了解下怎么自定义元类。

因为只有了解了怎么自定义才能更好的理解它的作用。

首先我们来了解下 属性

,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?

那就必须根据创建出类,所以:先定义,然后创建类。

连接起来就是:先定义,就可以创建类,最后创建实例。

所以, 允许你创建类或者修改类。

换句话说,你可以把类看成是 创建出来的“实例”。

class (object): = …[…]

如果是这样写的话,Python 就会用元类来创建类 。

当你写下 class (object),但是类对象 还没有在内存中创建。P

ython 会在类的定义中寻找 属性,如果找到了,Python 就会用它来创建类 ,如果没有找到,就会用内建的 type 函数来创建这个类。如果还不怎么理解,看下下面的流程图:

72e67a5c00c71f923d7db9cfd82c0d90.png

再举个实例:

class Foo(Bar): pass

它的判断流程是怎样的呢?

首先判断 Foo 中是否有 这个属性?如果有,Python 会在内存中通过 创建一个名字为 Foo 的类对象(注意,这里是类对象)。如果 Python 没有找到 ,它会继续在 Bar(父类)中寻找 属性,并尝试做和前面同样的操作。如果 Python在任何父类中都找不到 ,它就会在模块层次中去寻找 ,并尝试做同样的操作。如果还是找不到 ,Python 就会用内置的 type 来创建这个类对象。

其实 就是定义了 class 的行为。类似于 class 定义了 的行为, 则定义了 class 的行为。可以说,class 是 的 。

现在,我们基本了解了 属性,但是,也没讲过如何使用这个属性,或者说这个属性可以放些什么?

答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到 type 或者子类化 type 的东东都可以。

元类的主要目的就是为了当创建类时能够自动地改变类。

通常,你会为API 做这样的事情,你希望可以创建符合当前上下文的类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定 。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。

幸运的是, 实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。

# 元类会自动将你通常传给‘type’的参数作为自己的参数传入

40e290f04c6b16d13ff59405ac072e35.png

b661c379a20432770c696665c8a5c733.png

# 请记住,'type'实际上是一个类,就像'str'和'int'一样# 所以,你可以从type继承class (type): # __new__ 是在之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在中做些事情 # 还有一些高级的用法会涉及到改写特殊方法,但是我们这里不用

22e32d5dc7e19941455e56eb422e18bf.png

# 复用type.__new__方法 # 这就是基本的OOP编程,没什么魔法 return type.__new__(, , , )

你可能已经注意到了有个额外的参数 ,这并没有什么特别的。类方法的第一个参数总是表示当前的实例,就像在普通的类方法中的 self 参数一样。当然了,为了清晰起见,这里的名字我起的比较长。但是就像 self 一样,所有的参数都有它们的传统名称。因此,在真实的产品代码中一个元类应该是像这样的:

3e9637b65ea3be83389c6ee0e5ecda1f.png

如果使用 super 方法的话,我们还可以使它变得更清晰一些,这会缓解继承(是的,你可以拥有元类,从元类继承,从 type 继承)

045e40bd6e39a840e889cd8c1dbb488d.png

通常我们都会使用元类去做一些晦涩的事情,依赖于自省,控制继承等等。确实,用元类来搞些“黑暗魔法”是特别有用的,因而会搞出些复杂的东西来。但就元类本身而言,它们其实是很简单的:

五、使用元类