python深入学习(一):类与元类(metaclass)的理解

0x00 前言

最近准备开始着手毕设的东西,所以打算把过程中一些关于python的一些我平时不怎么使用的地方学习并写出来分享下,不过由于有些地方是我个人的理解,所以多少可能会有偏差,希望师傅们看到能帮我指出来。

昨天在看代码的时候遇到了元类(metaclass),相信平时如果大家用python方面的工具用的较多的话也会比较常见到这个,但即便如此却没有去深入研究过这个东西

先分享一个链接:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

0x01 关于类、对象、类的实例

首先先谈谈python中的类、对象和实例。 在C++中接触到这些东西,不过我的大部分概念仅仅停留在class关键字上面。也经常说道这三个名词,可能平时用着用着也没有深究过,渐渐也就用混淆了这三个词。

1.1 python中静态与动态创建类

在这里,一直以来我的理解是类就是通常意义上的class,而对象就是类的实例化,换言之,类就是一组用来描述如何生成一个对象的代码段。但是实际上在python里面实例和对象有区别,待会我们会说,先来看个例子。 例如如下:

class test():
    pass
mine=test()  #test是类,mine是实例也是对象

但是,Python中的类还远不止如此。类同样也是一种对象。因为只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。 即test本身也是一个对象,可以进行任何对象的操作,例如赋值、传参、增加属性等等。

>>> class test():
...     pass
...
>>> print test
__main__.test
>>> test.a='a'
>>> hasattr(test,'a')
>>> print test()
<__main__.test instance at 0x7f8e5caccea8>
>>> print test().a
a

另外在python中除了用关键字class静态创建类以外还可以用type()函数动态创建类,这也是广为人知的type函数不广为人知的用法,具体用法如下:

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

>>> Hello = type('Hello', (object,), dict(hello="hello world!"))
>>> h=Hello()
>>> h.hello
'hello world!'
---------------------------------------------------------
For example2:

>>> def func(self, name='world'):
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=func))
>>> h = Hello()
>>> h.hello()
Hello, world.
---------------------------------------------------------

通过type的用法我们知道类也是可以动态创建的。

1.2 python里面的类和实例

我们分为python2和python3来说, 在python2中,我们通过下面这个例子来说明

class A: #旧式类
    pass
class B(object):#新式类
    pass
print B.__class__
print A.__class__

得到这个结果

<type 'type'>
Traceback (most recent call last):
  File "class.py", line 9, in <module>
    print A.__class__
AttributeError: class A has no attribute '__class__'

第一个print打印了,但是第二个报错了,我们知道__class__会告诉我们当前实例是哪个类的实例。这也就意味着,B是一个类也是实例,但A只是一个类,不是实例,这也就是旧式类和新式类的区别。 再看

#encoding:utf-8
class A:
    pass
class B(object):
    pass
b = B()
a = A()

print "---------------------"
print A
print B
print "---------------------"
print a
print b
print "---------------------"
print type(A)
print type(B)
print "---------------------"
print type(a)
print type(b)
print "---------------------"

结果

---------------------
__main__.A
<class '__main__.B'>
---------------------
<__main__.A instance at 0x7ff0f37087e8>
<__main__.B object at 0x7ff0f3713290>
---------------------
<type 'classobj'>
<type 'type'>
---------------------
<type 'instance'>
<class '__main__.B'>
---------------------

多少能够理解了把。尤其是第二组打印结果,a是A的一个实例,b是B的一个对象。 即总结起来这么说,自建类实例化生成的对象的类型是object,内置类实例化生成的对象的类型是instance。 尤其是第三组打印结果,也说明自建类和内置类的区别,自建类的类型是classobj,而内置类的类型是type,这一点在后续会解释的。

大家可能会问,既然自建对象和内置对象不同,但是我们平时使用的时候都是一样的使用啊? 这个问题就是python官方打算解决的,因为他底层会用大量代码来掩盖二者的差异,从而让我们觉得在使用中二者一样。 所以在python3里面所有类都是内置类了。如下:

class A:
    pass
class B(object):
    pass

即上述代码都默认自己懂继承自object类了。从而几乎取缔了实例instance的存在。在python3中测试如下:

>>> class A:
...     pass
...
>>> class B(object):
...     pass
...
>>> a=A()
>>> b=B()
>>> print(a)
<__main__.A object at 0x7fbb06f81198>
>>> print(b)
<__main__.B object at 0x7fbb0526add8>

可以看到已经没有区别了。

所以看得出官方的态度就是支持大家更多的使用内置类,而非自建类,即平时我们写代码的时候还是尽量多使用B的创建方式,继承OBJECT

好的,接下来还有一个问题就是我们执行print type(B)的时候打印的值为什么是<type 'type'>

进入下一个主题。

0x02 关于元类metaclass

2.1 元类是什么?

我们之前说过在python里面的类本身也是一个对象,元类就是创建所有python类的类 即可以通过这个

Anyclass=Metaclass()
Object=Anyclass()

所以大家应该猜到了把,实际上type就是这个元类,即python所有的类都是由type创建的,这也是为什么type可以用来动态创建类的原因。

换言之,元类type就是创建python类这种对象的东西,即可以称为一个类工厂,当然,我们也可以创建自己的元类。

2.2 __metaclass__属性

这个就是用的比较多的地方。 你可以在写一个类的时候为其添加__metaclass__属性

class Foo(object):
    __metaclass__ = something
    ......
    ......

你首先写下class Foo(object),但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。

即在python创建类的时候,python会在内存中通过__metaclass__创建一个名字为Foo的类对象。如果Python没有找到__metaclass__,它会继续在父类中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,ython就会用内置的type来创建这个类对象。

这就是__metaclass__属性,理解这个一定要区分好与继承的关系。继承是在类已经在内存创建好了之后继承相应的属性和方法,而这个属性的功能比继承更强更大,但是简而言之它的功能其实也很简单 就是

1)   拦截类的创建
2)   修改类
3)   返回修改之后的类