Python魔法
一、什么是Python魔法
Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradigm),你不仅可以使用面向对象的方式来编写程序,还可以用面向过程的方式来编写相同功能的程序(还有函数式、声明式等)。Python的多范式依赖于Python对象中的特殊方法(special method)。
特殊方法名的前后各有两个下划线。特殊方法又被称为魔法方法(magic method)。定义了许多Python语法和表达方式,正如我们在下面的例子中将要看到的。当对象中定义了特殊方法的时候,Python也会对它们有“特殊优待”。比如定义了__init__()方法的类,会在创建对象的时候自动执行__init__()方法中的操作。
可以通过dir()来查看对象所拥有的特殊方法,比如dir(1)。
运算符
Python的运算符是通过调用对象的特殊方法实现的。比如:
'abc' + 'xyz' # 连接字符串
实际进行了如下操作:
'abc'.__add__('xyz')
所以,在Python中,两个对象是否能进行加法运算,首先就要看相应的对象是否有__add__()方法。一旦相应的对象有__add__()方法,即使这个对象从数学上不可加,我们都可以用加法的形式,来表达obj.__add__()所定义的操作。在Python中,运算符起到简化书写的功能,但它依靠特殊方法实现。
Python不强制用户使用面向对象的编程方法。用户可以选择自己喜欢的使用方式(比如选择使用+符号,还是使用更加面向对象的__add__()方法)。特殊方法写起来总是要更费事一点。
内置函数
与运算符类似,许多内置函数也都是调用对象的特殊方法。比如:
len([1,2,3]) # 返回表中元素的总数
实际上做的是
[1,2,3].__len__()
相对与__len__(),内置函数len()也起到了简化书写的作用。
对于内置的对象来说(比如整数、表、字符串等),它们所需要的特殊方法都已经在Python中准备好了。而用户自己定义的对象也可以通过增加特殊方法,来实现自定义的语法。特殊方法比较靠近Python的底层,许多Python功能的实现都要依赖于特殊方法。
二、一些常用的特殊方法
1、__doc__
表示类的常用信息
class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) #输出类的描述信息
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
#!/usr/bin/env python # -*- coding:utf-8 -*- class C: def __init__(self): self.name = 'wupeiqi'
from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
3. __init__
构造方法,通过类创建对象时,自动触发执行。
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo('wupeiqi') # 自动执行类中的 __init__ 方法 print(obj.name, obj.age)
4. __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): pass
5. __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 执行 __init__ obj() # 执行 __call__
6. __dict__
Python中,一切皆对象,这些对象是由一个类型实例化而来;那么说,这些对象就有属性和方法,属性可以存储一些值。而从直观上来看,任何可以存储东西的事物,我们都可以说它有一个空间(只有有空间,才能存储东西嘛),而在编程中,我们一般使用术语“名字空间”或“命名空间”(namespace)来称呼它。这样,我们就得出,每个对象都有一个名字空间。而在Python中,我们使用对象的__dict__属性来保存该对象的名字空间中的东西,__dict__是一个字典(“键-值”对,一般“键”就是属性名或方法名,“值”就是属性的值或方法名所指向的真正的方法实体对象)。
class Province: country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print('func') # 获取类的成员,即:静态字段、方法、 print(Province.__dict__) # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} obj1 = Province('HeBei',10000) print(obj1.__dict__) # 获取 对象obj1 的成员 # 输出:{'count': 10000, 'name': 'HeBei'} obj2 = Province('HeNan', 3888) print(obj2.__dict__) # 获取 对象obj1 的成员 # 输出:{'count': 3888, 'name': 'HeNan'}
7. __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo: def __str__(self): return 'flash' obj = Foo() print(obj) # 输出:flash
8、__getitem__、__setitem__、__delitem__、__getattribute__
在Python中,重载__getattr__、__setattr__、__delattr__和__getattribute__方法可以用来管理一个自定义类中的属性访问。其中,__getattr__方法将拦截所有未定义的属性获取(即,当要访问的属性已经定义时,该方法不会被调用,至于定义不定义,是由Python能否查找到该属性来决定的);__getattribute__方法将拦截所有属性的获取(不管该属性是否已经定义,只要获取它的值,该方法都会调用),由于此情况,所以,当一个类中同时重载了__getattr__和__getattribute__方法,那么__getattr__永远不会被调用,另外,__getattribute__方法仅仅存在于Python2.6的新式类和Python3的所有类中;__setattr__方法将拦截所有的属性赋值;__delattr__方法将拦截所有的属性删除。说明:在Python中,一个类或类实例中的属性是动态的(因为Python是动态的),也就是说,你可以往一个类或类实例中添加或删除一个属性。
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__ obj['k2'] = 'wupeiqi' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
10. __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
#!/usr/bin/env python # -*- coding:utf-8 -*- obj = iter([11,22,33,44]) for i in obj: print(i)
11. __new__ 和 __metaclass__
阅读以下代码:
class Foo(object): def __init__(self): pass obj = Foo() # obj是通过Foo类实例化的对象
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
print(type(obj)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
a) 普通方式
class Foo(object): def func(self): print 'hello flash'
b) 特殊方式
def func(self): print 'hello flash' Foo = type('Foo',(object,), {'func': func}) #type第一个参数:类名 #type第二个参数:当前类的基类 #type第三个参数:类的成员 obj = Foo() obj.func() #输出结果:hello flash
==》 类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
1 class MyType(type): 2 3 def __init__(self, what, bases=None, dict=None): 4 super(MyType, self).__init__(what, bases, dict) 5 6 def __call__(self, *args, **kwargs): 7 obj = self.__new__(self, *args, **kwargs) 8 9 self.__init__(obj) 10 11 class Foo(object): 12 13 __metaclass__ = MyType 14 15 def __init__(self, name): 16 self.name = name 17 18 def __new__(cls, *args, **kwargs): 19 return object.__new__(cls, *args, **kwargs) 20 21 # 第一阶段:解释器从上到下执行代码创建Foo类 22 # 第二阶段:通过Foo类创建obj对象 23 obj = Foo()
三、反射
python中的反射功能由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 def func(self): 7 return 'func' 8 9 obj = Foo() 10 11 # #### 检查是否含有成员 #### 12 print(hasattr(obj, 'name')) 13 print(hasattr(obj, 'func'))
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 def func(self): 7 return 'func' 8 9 obj = Foo() 10 11 12 # #### 获取成员 #### 13 print(getattr(obj, 'name')) 14 print(getattr(obj, 'func'))
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 def age(self): 7 return '18' 8 9 obj = Foo() 10 11 # #### 设置成员 #### 12 setattr(obj, 'name', 'tony') 13 print(obj.name)
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 obj = Foo() 7 8 # #### 删除成员 #### 9 delattr(obj, 'name') 10 print(hasattr(obj, 'name')) 11 12 13 #输出结果:False
需求:代码如下,使用其他方式获取obj对象中的name变量指向内存中的值 “flash”
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 # 不允许使用 obj.name 7 obj = Foo()
有以下两种方式:
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 # 不允许使用 obj.name 7 obj = Foo() 8 9 print(obj.__dict__['name'])
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'flash' 5 6 # 不允许使用 obj.name 7 obj = Foo() 8 9 print(getattr(obj, 'name'))
附录:如何调用魔术方法
一些魔术方法直接和内建函数相对,在这种情况下,调用他们的方法很简单,但是,如果是另外一种不是特别明显的调用方法,这个附录介绍了很多并不是很明显的魔术方法的调用形式。
参考:
http://www.cnblogs.com/vamei/archive/2012/11/19/2772441.html
http://www.cnblogs.com/wupeiqi/articles/5017742.html
http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html