美文网首页python基础投资
Python--魔法方法学习

Python--魔法方法学习

作者: Bling_ll | 来源:发表于2017-11-16 09:29 被阅读38次

    1、什么叫魔法方法?

    • 魔法方法:Python解释器自动给出默认的,是可以给你的类增加魔力的特殊方法。如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用(不重载则会使用默认的)。你可以根据需求,重写这些方法去定义自己想要的行为,而这一切都是自动发生的。
    • 魔法方法经常是两个下划线包围来命名的(比如__init____del__
    • 函数与方法的区别:
    • 魔法方法是针对class而言的,脱离了”类“谈magic_method是没有意义的
    • 网上的一些魔法方法详解:http://blog.csdn.net/koko66/article/details/42709279,可以学习一下

    2、__init__(self[,...])(构造器,注意前后都是双下划线)

    • 只要实例化一个类,该方法在创建对象时会被自动调用

      #实例化创建一个对象,系统会自动调用了__init__方法
      >>>wangwu = Person('王五','男','16')
      >>>wangwu.sheep()
      王五:不嗨了,早睡早起身体棒!
      

      可在类里初始化好name,sex,age三个值,此时实例化Person时可不用传参,直接用默认的值。

      class Person1():#定义一个Person1类
          def __init__(self):
              self.name='旺仔牛奶' #默认name
      >>>a=Person1()
      >>>a.name
      '旺仔牛奶'
      

      若无初始化具体的值,实例化Person时又没有传入参数,则会报错。

      >>>lisi = Person()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: __init__() missing 3 required positional arguments: 'name', 'sex', and 'age'
      
    • 小结:

      1、根据“需求”(需要初始化时,如上面的Person类,需要初始化name,sex,age)才去重写__init__方法

      2、有了__init__方法,在创建实例的时候,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去

      3、该方法的返回值为None,不能试图让其返回其他值!

      class A:
            def __init__(self):
            return 'A'
      >>>a = A()#会报错
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: __init__() should return None, not 'str'
      

    3、__new__(cls[, ...])

    • 实例化调用的第一个方法,它只取下cls参数(类参数),并把其他参数传给 __init__,这个方法不常去重写,但也有适用场景:继承一个不可变的类型,如元组、字符串,但又需去修改值时,可重写该方法。

      #全部转为大写字母
      class CapStr(str):           #继承str类
          def __new__(cls,string):    #重写__new__方法,在初始化时就将传入字符串全部转为大写
              string = string.upper()
              return str.__new__(cls,string) #返回一个类对象str的__new__方法,参数是修改为大写的string
      >>>a = CapStr("abcd")
      ABCD
      

    4、__del__(self[, ...])

    • 当对象将要被销毁时,此方法被自动调用,类似于析构器!注:是当已经没有任何变量引用该对象时,python的垃圾回收机制会将其自动销毁,此时才会去调用__del__方法!

      del x ==x.__del__() 是错误的!
      
      class C:
          def __init__(self):
              print('我是__inti__方法,我被调用了')
             def __del__(self):
                  print('我是__del__方法,我被调用了')
      
      >>>a = C()    #__inti__方法在对象创建时就会被自动调用
      我是__inti__方法,我被调用了
      >>>b=a
      >>>c=b
      >>>del c     #此时不会调用__del__方法
      >>>del b
      >>>del a      #所有对a的引用都被del后,__del__才会被调用
      我是__del__方法,我被调用了
      
    • 注意:当解释器退出的时候,如果对象还存活着,这里不能确保__del__一定会被执行!所以__del__ 不能替代一些良好的编程习惯(比如连接用完了将其关掉)!

    5、算术运算魔法方法

    • 当对象进行算术操作时,即触发对应的算术魔法方法

      1、__add__(self, other), 定义加法行为 : +

      2、__sub__(self, other) ,定义减法行为 :-

      3、__mul__(self, other) ,定义乘法行为 :*

      4、__truediv__(self, other) ,定义真除法行为 :/

      (算数运算,反运算,增量赋值运算,一元操作符等其他的方法见上面给出的魔法方法详解链接)

      class New_int1(int):#继承int类
            pass
      >>> a = New_int1(3)
      >>> b = New_int1(4)
      >>> a+b #加号行为,继承int类,则会去调用int中的__add__(self, other)魔法方法
      7
      >>> a-b
      -1
      ------------------------------------------------------------------------------------
      class New_int2(int): #继承int类
            def __add__(self,other): #重写加法行为,令New_int类中的加法运算来做int中的减法运算
                return int.__sub__(self,other) 
          def __sub__(self,other):
                return int.__add__(self,other)
      
      >>> a = New_int2(3)
      >>> b = New_int3(4)
      >>> a+b
      -1
      >>> a-b
      7
      

    6、属性访问控制

    • __getattr__(self, name):定义当用户试图访问一个不存在的属性时的行为

      class Test(object):
        def __init__(self,city):
            self.city = city
        def __getattr__(self,name):
            print('don\'t have the attribute:%s'%name)
      >>> a = Test('广州')
      >>> a.city
      '广州'
      >>> a.name
      don't have the attribute:name
      #这里并没有name属性,在找不到属性的情况下,正常的继承object的对象都会抛出AtrributeError的错误。但是这里通过__getattr__魔法方法改变了找不到属性时候的类的行为。输出了查找的属性的参数。
      
    • __getattribute__(self, name): 定义当该类的属性被访问时的行为(拦截所有的属性,包括存在的属性)。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)

      class Test(object):
        def __init__(self,city):
            self.city = city
        def __getattribute__(self,name):
            print('__getattribute__:%s'%name)
            return name
      >>> a = Test('广州')
      >>> a.city #__getattribute__会拦截所有的属性,包括已存在的。
      __getattribute__:city
      '广州'
      >>> a.name
      __getattribute__:name
      'name'
      
    • __setattr__(self, name,value):定义当一个属性被设置时的行为。不管对象的某个属性是否存在,它都允许你为该属性进行赋值。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)

      class Test(object):
        def __init__(self,city):
            self.city = city
        def __setattr__(self,name,value):
            return super().__setattr__(name,value) #调用基类的__setattr__方法(推荐)
      >>> a = Test('广州')
      >>> a.name ='小茗童鞋'
      >>> a.name
      '小茗童鞋'
      --------------------------------------------------------------------------------------------
      #若在 __setattr__不调用基类方法,可以采用给对象的特殊属性__dict__赋值的方法(__dict__会以字典形式显示当前对象的所有属性和对应的值)
      >>> a.__dict__
      {'city': '广州', 'name': '小茗童鞋'}
      class Test(object):
        def __init__(self,city):
            self.city = city
        def __setattr__(self,name,value):
            self.__dict__[name] = value
      >>> b = Test('深圳')
      >>> b.sex = '男'
      >>> b.sex
      '男'
      >>> b.__dict__
      {'city': '深圳', 'sex': '男'}
      --------------------------------------------------------------------------------------------
      #无限递归例子:
      class Test(object):
        def __init__(self,city):
            self.city = city
        def __setattr__(self,name,value):
            self.name = value
      #执行初始化方法self.city = city的时候,就会调用__setattr__方法,而这里的__setattr__方法里面的self.name = value又会调用自身,所以导致无限递归。故使用该魔法方法的时候要特别注意!
      >>> a = Test('广州')
      Traceback (most recent call last):
      File "<pyshell#36>", line 1, in <module>
          a = Test('广州')
      File "<pyshell#35>", line 3, in __init__
          self.city = city
      File "<pyshell#35>", line 5, in __setattr__
          self.name = value
      RecursionError: maximum recursion depth exceeded while calling a Python object
      
    • __delattr__(self, name):定义当一个属性被删除时的行为。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)

       class Test(object):
        def __init__(self,city):
            self.city = city
        def __delattr__(self,name):
            print('__delattr__:%s'%name)
            return super().__delattr__(name) #调用基类的__delattr__方法(推荐)
      >>> a = Test('广州')
      >>> del a.city
      __delattr__:city
      >>> a.city
      Traceback (most recent call last):
        File "<pyshell#73>", line 1, in <module>
          a.city
      AttributeError: 'Test' object has no attribute 'city' 
      
    • property(fget=None, fset=None, fdel=None, doc=None):是一个类,主要功能是为了方便类内部函数的调用(使得在外部不用调用类方法,可以直接以属性方式操作)

      class C:
        def __init__(self,size=10):
            self.size = size
        def getSize(self):
            return self.size
        def setSize(self,value):
            self.size = value
        def delSize(self):
            del self.size
        x = property(getSize,setSize,delSize) #通过property函数,使得x与size直接挂钩    
      >>> c = C()
      >>> c.x = 1 
      >>> c.x
      1
      >>> c.size
      1
      >>> del c.x
      >>> c.size
      Traceback (most recent call last):
        File "<pyshell#16>", line 1, in <module>
          c.size
      AttributeError: 'C' object has no attribute 'size'
      

    7、描述符(property的原理)

    描述符就是将某种特殊类型的类(类里含有下述魔法方法之一)的实例指派给另一个类的属性。

    1、__get__(self, instance, owner):用于访问属性,它返回属性的值

    2、__set__(self, instance, value):将在属性分配操作中调用,不返回任何内容

    3、__delete__(self, instance):控制删除操作,不返回任何内容

    class MyDecriptor:#称为描述符类
        def __get__(self,instance,owner):
            print("geting...",self,instance,owner)
        def __set__(self,instance,value):
            print("seting...",self,instance,value)
        def __delete__(self,instance):
            print("deleting...",self,instance)      
    class Test:
        x = MyDecriptor() #此处说明MyDecriptor就是x的描述符,类似property使用
    >>> test = Test()
    >>> test.x #调用__get__方法
    geting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98> <class '__main__.Test'>
    #此处self:描述符类本身的一个实例 <__main__.MyDecriptor object at 0x0000000003570FD0>
    #instance:拥有者类的实例 <__main__.Test object at 0x0000000003570F98>
    #owner:拥有者类本身 <class '__main__.Test'>
    >>> test.x ='哈哈哈' #调用__set__方法
    seting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98> 哈哈哈
    >>> del test.x #调用__delete__方法
    deleting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98>
    
    ------------------------------------------------------------------------------------------------
    #定义一个自己的property类
    class MyProperty:#称为描述符类
        def __init__(self,fget=None,fset=None,fdel=None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
        def __get__(self,instance,owner):
            return self.fget(instance)
        def __set__(self,instance,value):
            self.fset(instance,value)
        def __delete__(self,instance):
            self.fdel(instance) 
    class C:
        def __init__(self):
            self._x = None
        def getX(self):
            return self._x
        def setX(self,value):
            self._x = value
        def delX(self):
            del self._x
        x = MyProperty(getX,setX,delX)
    >>> c = C()
    >>> c.x = '哈哈' #直接以访问属性形式调用描述类MyProperty
    >>> c.x
    '哈哈'
    >>> c._x
    '哈哈'
    >>> del c.x
    >>> c._x
    Traceback (most recent call last):
      File "<pyshell#125>", line 1, in <module>
        c._x
    AttributeError: 'C' object has no attribute '_x'
    

    8、容器类型-魔法方法

    1、容器是对数据的封装

    2、python的容器类型分为可变类型(如list、dict)和不可变类型(如string、tuple)。可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。

    魔法方法 定义 备注
    __len__(self) 求容器的大小(注意与capacity的区别) 可变和不可变容器均具备 __len____getitem__
    __getitem__(self, key) 获取容器中指定元素的行为
    __setitem__(self, key, value) 设置容器中指定元素的行为 只有可变容器拥有 __setitem____delitem__
    __delitem__(self, key) 删除容器中指定元素的行为
    __iter__(self) 定义迭代器中元素的行为
    __reversed__(self) 当调用reversed()函数时 仅当序列可以是有序的时候实现它,例如对于列表或者元组。
    __contains__(self, item) 成员运算符in/ not in的行为
    class FunctionalList:
        ''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''
        def __init__(self, values=None):
            if values is None:
                self.values = []
            else:
                self.values = values
        def __len__(self):
            return len(self.values)
        def __getitem__(self, key):
            return self.values[key]
        def __setitem__(self, key, value):
            self.values[key] = value
        def __delitem__(self, key):
            del self.values[key]
        def __iter__(self):
            return iter(self.values)
        def __reversed__(self):
            return FunctionalList(reversed(self.values))
        def append(self, value):
            self.values.append(value)
        def head(self):
            # 获取第一个元素
            return self.values[0]
        def tail(self):
            # 获取第一个元素之后的所有元素
            return self.values[1:]
        def init(self):
            # 获取最后一个元素之前的所有元素
            return self.values[:-1]
        def last(self):
            # 获取最后一个元素
            return self.values[-1]
        def drop(self, n):
            # 获取所有元素,除了前N个
            return self.values[n:]
        def take(self, n):
            # 获取前N个元素
            return self.values[:n]
    >>> a = FunctionalList([1,2,7,10,3])
    >>> a.head()
    1
    >>> a.take(2)
    [1, 2]
    >>> a.init()
    [1, 2, 7, 10]
    >>> a.tail()
    [2, 7, 10, 3]
    >>> b = iter(a) #调用迭代器行为
    >>> for i in b:
            print(i)    
    1
    2
    7
    10
    3
    >>> from collections import Iterator #可以使用isinstance()判断一个对象是否是Iterator对象
    >>> isinstance(b, Iterator)
    True
    

    9、迭代器--魔法方法

    1、可迭代对象Iterable:可以直接作用于for循环的对象统称为可迭代对象,使用isinstance()判断一个对象是否是Iterable对象。

    >>> from collections import Iterable
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('hhh', Iterable)
    True
    >>> isinstance(10, Iterable)
    False
    

    2、迭代器Iterator:可以被next()函数调用并不断返回下一个值的对象称为迭代器,使用isinstance()判断一个对象是否是Iterator对象。

    • 内置函数:iter(),next()
    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    #生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
    #把list、dict、str等Iterable变成Iterator可以使用iter()函数
    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    
    • 魔法方法: __iter__(self), __next__(self)
    >>> string = 'abcd'
    >>> it = iter(string)
    #每次调用next(it),就计算出it的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
    >>> next(it) 
    'a'
    >>> next(it)
    'b'
    >>> next(it)
    'c'
    >>> next(it)
    'd'
    >>> next(it)
    Traceback (most recent call last):
      File "<pyshell#7>", line 1, in <module>
        next(it)
    StopIteration
    --------------------------------------------------------------------------------------------
    #定义一个斐波那契迭代器
    class Fibs:
        def __init__(self,n=10):
            self.a = 0
            self.b = 1
            self.n = n
        def __iter__(self):
            return self #返回迭代器本身
        def __next__(self):
            self.a,self.b = self.b,self.a+self.b
            if self.a > self.n:#控制迭代数量
                raise StopIteration
            else:
                return self.a       
    >>> fibs = Fibs()
    >>> for each in fibs:
        print(each) 
    1
    1
    2
    3
    5
    8
    

    10、__call__(self,name)

    任何类,只需要定义一个__call__()方法,就可以直接对实例(对象)进行调用。

    class Person(object):
        def __init__(self, name, gender):
            self.name = name
            self.gender = gender
        def __call__(self, friend):
            print('My name is %s...' % self.name)
            print('My friend is %s...' % friend)        
    >>> p = Person('Bob', 'male')
    >>> p('Tim') #直接对实例进行调用,即是把这个类型的对象当作函数来使用
    My name is Bob...
    My friend is Tim...
    #对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象。
    

    如何判断一个变量是对象还是函数?(即判断一个对象是否能被调用,能被调用的对象就是一个Callable对象)

    #用callabel()函数判断
    >>> callable(Person('Bob', 'male'))
    True
    >>> callable(max)
    True
    >>> callable([1, 2, 3])
    False
    

    11、__str____repr__

    __str__()返回用户看到的字符串,__repr__()返回程序开发者看到的字符串,即__repr__()是为调试服务的。

     class Animal(object):
        def __init__(self,name):
            self.name = name
    >>> print(Animal('Cat')) #打印出来不好看
    <__main__.Animal object at 0x0000000003558978>
    #改进
    class Animal(object):
        def __init__(self,name):
            self.name = name
        def __str__(self): #定义一个__str__()方法,返回一个好看的字符串
            return 'Animal object (name: %s)' % self.name   
    >>> print(Animal('Cat'))
    Animal object (name: Cat)
    #但若不用print,打印出来的仍是不好看,因为直接显示变量调用的不是__str__(),而是__repr__()
    >>> a = Animal('Cat')
    >>> a
    <__main__.Animal object at 0x0000000003558C18>
    #解决:再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的
    class Animal(object):
        def __init__(self,name):
            self.name = name
        def __str__(self): 
            return 'Animal object (name: %s)' % self.name
        __repr__ = __str__ 
    >>> print(Animal('Cat'))
    Animal object (name: Cat)
    >>> a = Animal('Cat')
    >>> a
    Animal object (name: Cat)
    

    相关文章

      网友评论

        本文标题:Python--魔法方法学习

        本文链接:https://www.haomeiwen.com/subject/fpnkvxtx.html