最初博主是希望在python当中创建一个单列模式的类,因为python当中不像java和php当中有权限修饰符(private),所以实现起来要绕一点。
网上找了一下python实现单列模式,类似的大概有这种方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class
singleton
(
type
)
:
""
"
实现单列模式的元类
总之,metaclass的主要任务是:
拦截类,
修改类,
返回类
"
""
def
__init__
(
cls
,
classname
,
parrentstuple
,
attrdict
)
:
""
"
"
""
super
(
SigleInstance
,
cls
)
.
__init__
(
classname
,
parrentstuple
,
attrdict
)
cls
.
_instance
=
None
def
__call__
(
self
,
*
args
,
*
*
kargs
)
:
""
"
"
""
if
self
.
_instance
:
return
self
.
_instance
else
:
self
.
_instance
=
super
(
SigleInstance
,
self
)
.
__call__
(
*
args
,
*
*
kargs
)
return
self
.
_instance
|
这就是单列的元类,我把它小写了,因为type也是小写的。然后呢,在即将要实现单列的class当中这样写:
1
2
3
4
|
class
Earth
(
object
)
:
__metaclass_
_
=
singleton
def
__init__
(
self
,
a
,
b
)
:
pass
|
这样每次 创建一个 Earth()取得的始终都应该是一个实例。
关于__metaclass__ 和type这个东西可以参考深入理解Python中的元类(metaclass)。这篇文章解决了我大部分的疑惑,但是我还是没有搞清楚的是:
当__metaclass__是一个类的时候,metaclass是怎样去创建一个类的?
在这之前首先说明一下:
一。python当中一切都是对象,而对象都是由类创建,这里为了区分概念,我们不妨换一种说法:实例都是由模板创建的。
二。那么什么又是对象的type呢?type就是类型的意思。如果您对java稍微有一点了解。你会有这样的认识:
1
2
3
4
5
|
/**
* language是一个String类型的变量,值为"python"
* 在java当中,如果 Integer language = "python"就是错误的
*/
String
language
=
"python"
;
|
由于python是一门动态语言,所以在写代码的时候不必声明变量的类型,也不可能完全确定这个变量是什么类型,除非对自己代码的逻辑以及流程非常清楚,另外python在运行当中变量的类型是可以更改的。但是不能确定变量的类型,不代表就没有类型啊。python当中的变量一样是有类型的。那么怎么来看变量的类型呢?答案是使用type。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
language
=
"python"
print
type
(
language
)
# python2.7中输出:<type 'str'>
# ipython 输出 str
number
=
2
print
type
(
number
)
#输出:<type 'int'>
class
A
(
object
)
:
pass
a
=
A
(
)
print
type
(
a
)
#输出:<type '__main__.A'>
|
上面段代码分别用type查看到了各个变量的类型。根据(一)【python当中一切都是对象,而对象都是由类创建,这里为了区分概念,我们不妨换一种说法:实例都是由模板创建的】我们可不可以这样说呢:language是str的实例,str是language实例的模板。因此type(a_var)的输出就是a_var这个实例的模板。所以我们看到 type(a)的输出是,也就是说 a实例的模板是A。
class A的模板是什么呢?
1
2
|
print
type
(
A
)
#输出:<type 'type'>
|
也就是说,一个类的模板的type,类是type的一个实例,tpye实例化了一个对象,这个对象就是class。所以在python的世界里,一切都是对象,类也是对象。
那么有意思的地方来了,type的模板是什么呢,也就是,type的type是什么呢?
1
2
|
print
type
(
type
)
# 输出<type 'type'>
|
是不是很有意思,type的type是type。很绕,换成大白话说:type的模板是type自己。那么是不是就可以这样说呢?TYPE(type,为了区分说明,故意大写)是type的模板,type是TYPE的实例,因此说明type是一个实例;而TYPE是一个模板,也就是一个类!,因为TYPE==type,那么可以得出结论:
1
|
type是一个类(
class),
type也是自身的实例(
instance)
|
python当中一切都是对象,类也是对象,对于type来说,更为特殊,因为type的模板是type,也就是说,type自己创建了自己,type是自身的实例。
三。实例是由类模板创建(也就是我们平时所写的class),而类是由元类模板创建(就是__metaclass__指定的类)。所以【元类和类的关系】就类似于【实例和类的关系】。
根据博主所探讨的结果表明,__metaclass__在创建类的过程大概是这样的:当类Earth的实例 earth正要被创建的时候,
- 查找Earth当中是否有__metaclass__,如果没有查找父类是否有__metaclass__,如果没有找到,就看包当中是否有__metaclass__,如果还是没有,那直接使用type创建该类。如果找到了,就用该__metaclass__来创建类。
- 那么如果找了__metaclass__,那么系统首先创建一个__metaclass__的实例,而这个由metaclass创建的实例正好的一个 Earth类,注意是Earth类(class),而不是一个Earth的一个实例哦。
那么到这一步究竟发生了些什么呢?我们写几行代码来看一看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#!/usr/bin/env python
#-*-coding:utf-8-*-
# author : "qiulimao"
# email : "qiulimao@getqiu.com"
""
"
the module's duty
"
""
#---------- code begins below -------
class
SimpleMetaClass
(
type
)
:
def
__init__
(
self
,
*
args
,
*
*
kwargs
)
:
print
"you have create a class instance by metaclass"
super
(
SimpleMetaClass
,
self
)
.
__init__
(
*
args
,
*
*
kwargs
)
class
Earth
(
object
)
:
__metaclass__
=
SimpleMetaClass
def
sayHello
(
)
:
print
"hello world"
if
__name__
==
"__main__"
:
print
"do something that have nothing with SimpleMetaClass and Earth"
|
最后运行的结果是这样的:
1
2
3
|
you
have
create
a
class
instance
by
metaclass
#①
do
something
that
have
nothing
with
SimpleMetaClass
and
Earth
#②
|
通过这个小例子我们看到:我们并没有使用过 Earth类,也没有使用过SimpleMetaClass这个元类,但实际的结果看来,SimpleMetaClass这个模板确被使用过了,因为打印出了①,后面我们会知道,打印出①是因为python使用SimpleMetaClass模板来创建出了Earth这个类对象(不是Earth的一个实例)。这个过程我们可以用我们平常经常说的一句话来描述:这个步骤相当于实例化了一个metaclass(SimpleMetaClass)对象,而这个对象正好是Earth类。
那么这里肯定会有人问了:我平时写class的时候都是不带__metaclass__的啊?那是因为如果你不写__metaclass__,最终这个类的模板就是type。上面的代码可以看到SimpleMetaClass是继承自type的。
四。Earth类已经被metaclass所创建出来了,那么当实例化一个Earth类(也就是创建一个earth对象)的时候又发生了什么呢?
在说明这个问题之前,我们得先聊一聊__call__,__new__这两个特殊方法。对于一个实现了__call__的类,那么它的实例可以当做函数来调用。来看个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
MagicCall
(
object
)
:
def
__new__
(
cls
,
name
)
:
return
super
(
MagicCall
,
cls
)
.
__new__
(
cls
)
def
__init__
(
self
,
name
)
:
self
.
name
=
name
def
__call__
(
self
)
:
print
"you have invoked __call__ method...."
if
__name__
==
'__main__'
:
magicCall
=
MagicCall
(
"python"
)
magicCall
(
)
#输出的结果为:you have invoked __call__ method....
|
而__new__有拦截类实例化的功能,在创建一个对象的过程中,执行__init__方法时,解释器已经为对象分配了内存,实例已经存在了,__init__方法只是改变这个类当中的某些参数。而在执行__new__方法时,这个实例是不存在的,而__new__就是要创建这个实例,所以__new__必须要有返回值。
现在我们回过头来想一想:为什么创建 一个类的实例是这种写法:
1
2
|
instance
=
SomeClass
(
)
instance
=
SomeClass
(
args1
,
args2
,
.
.
.
)
|
回答这个问题,我们可以用元类来解释。我们知道类是元类的一个对象,而元类的实例都有一个__call__方法。拥有__call__方法的对象可以把对象当做一个函数调用。所以喽,我们在创建一个类的实例的时候,实际上是调用了类对象的__call__(MetaClass:__call__)这个方法。
来看一个比较长的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#!/usr/bin/env python
#-*-coding:utf-8-*-
# author : "qiulimao"
# email : "qiulimao@getqiu.com"
""
"
the module's duty
"
""
#---------- code begins below -------
class
SimpleMetaClass
(
type
)
:
def
__new__
(
cls
,
*
args
,
*
*
kwargs
)
:
print
"creating class Earth..."
return
super
(
SimpleMetaClass
,
cls
)
.
__new__
(
cls
,
*
args
,
*
*
kwargs
)
def
__init__
(
self
,
*
args
,
*
*
kwargs
)
:
print
"you have create a class instance by metaclass"
super
(
SimpleMetaClass
,
self
)
.
__init__
(
*
args
,
*
*
kwargs
)
def
__call__
(
self
,
*
args
,
*
*
kwargs
)
:
print
"__call__ in metaclass has been invoked..."
,
"the args:"
,
args
,
kwargs
return
super
(
SimpleMetaClass
,
self
)
.
__call__
(
*
args
,
*
*
kwargs
)
class
Earth
(
object
)
:
__metaclass__
=
SimpleMetaClass
def
__new__
(
cls
,
g
,
R
=
65535
)
:
print
"creating instance using __new__"
cls
.
g
=
g
cls
.
R
=
R
return
super
(
Earth
,
cls
)
.
__new__
(
cls
)
;
def
__init__
(
self
,
g
,
R
=
65535
)
:
print
"initializing instance in __init__"
print
"gravity on Earth is:%f"
%
self
.
g
def
__call__
(
self
)
:
print
self
.
g
def
sayHello
(
self
)
:
print
"hello earth,your gravity is:%f"
%
self
.
g
if
__name__
==
"__main__"
:
earth
=
Earth
(
9.8
,
R
=
65535
)
earth
(
)
earth
.
sayHello
(
)
|
不知道大众喜欢在代码中写注释的方式来讲解,还是直接写文字过程。我就写文字过程吧。
最终上面这段代码执行的结果是:
1
2
3
4
5
6
7
8
9
|
①
creating
class
Earth
.
.
.
②
you
have
create
a
class
instance
by
metaclass
③
__call__
in
metaclass
has
been
invoked
.
.
.
the
args
:
(
9.8
,
)
{
'R'
:
65535
}
④
creating
instance
using
__new_
_
⑤
initializing
instance
in
__init_
_
⑥
gravity
on
Earth
is
:
9.800000
⑦
9.8
⑧
hello
earth
,
your
gravity
is
:
9.800000
|
我们来慢慢分析。
- 首先python创建SimpleMetaClass类,这个SimpleMetaClass是元类,应该是由type创建的。
- 当创建Earth这个类时,找到了它类中有__metaclass__属性,于是,采用SimpleClass来创建这个类
- 创建Earh类时,解释器会把类名,父类元祖,类的属性三个参数传给SimpleMetaClass
- SimpleMetaClass 根据 clazzName,(parent2,parent1,..),{‘attribute’:….,’method’:”}在自己__new__方法中创建出这个Earth实例【打印出①】,然后调用自己的__init__方法初始化类的参数【打印出②】。这时,这个Earth类作为metaclass的一个实例就被创建好了。
- 接下来通过 earth = Earth(9.8,R=65535) 创建一个Earth对象实例earth。这一步实际上是调用 Earth这个类对象的__call__(SimpleMetaClass::__call__)方法来创建一个Earth的实例。【打印出③,我们还能看到调用__call__的参数】。
- 而创建earth实例的方法__new__(Earth::__new__),和__init__(Earth:__init__),将会在Earth实例中的__call__(SimpleMetaClass::__call__)当中先后得以执行【先后打印出④⑤⑥】执行完成Earth实例earth对象被返回。
- 我想⑦⑧大家应该很容易理解了。
以上就是我对元类的理解,其中如有错误的地方还请大家斧正。