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__)


#输出类的描述信息
__doc__

  2. __module__ 和  __class__

    __module__ 表示当前操作的对象在那个模块

    __class__     表示当前操作的对象的类是什么  

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = 'wupeiqi'
day6/aa.py
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类
index.py

  3. __init__

    构造方法,通过类创建对象时,自动触发执行。

class Foo:

    def __init__(self, name):
        self.name = name
        self.age = 18


obj = Foo('wupeiqi')             # 自动执行类中的 __init__ 方法
print(obj.name, obj.age)        
View Code

  4. __del__

    析构方法,当对象在内存中被释放时,自动触发执行。

  注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。  

class Foo:

    def __del__(self):
        pass
View Code

  5. __call__

    对象后面加括号,触发执行。

  注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() 

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print '__call__'


obj = Foo() # 执行 __init__
obj()       # 执行 __call__
View Code

  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'}
View Code

   7. __str__

    如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。  

class Foo:

    def __str__(self):
        return 'flash'


obj = Foo()
print(obj)
# 输出:flash
View Code

  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__ 
View Code

  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()
View Code

 

 三、反射

 

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'))
hasattr
 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'))
getattr
 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)
setattr
 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
delattr

需求:代码如下,使用其他方式获取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

  

 

posted @ 2016-06-09 21:36  Rambotien  阅读(226)  评论(0编辑  收藏  举报