• 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

      

    运维因自动化而有趣!
  • 相关阅读:
    图片延迟加载
    Python开发【第一篇】:初识Python
    JavsScript+dom
    Django学习系列18:使用迁移创建生产数据库
    Django学习系列17:在模板中渲染待办事项
    Django学习系列16:处理完POST请求后重定向
    Django学习系列15:把POST请求中的数据存入数据库
    Django学习系列14:第一个数据库迁移
    Django学习系列13:Django ORM和第一个模型
    Django学习系列12:把Python变量传入模板中渲染
  • 原文地址:https://www.cnblogs.com/Rambotien/p/5547881.html
Copyright © 2020-2023  润新知