python类之特殊属性和魔术方法

发布时间:2019-09-22 07:44:41编辑:auto阅读(1672)

    一 python特殊属性

    1 总述

    属性 含义
    _name_ 类,函数,方法等的名字
    _module_ 类定义所现在的模块名
    _class_ 对象或类所属的类
    _bases_ 类的基类的元素,顺序为他们在基类列表中出现的顺序
    _doc_ 类/函数的文档字符传,如果没有定义则为None
    _mro_ 类的mro,class.mro()返回
    _dict_ 类或实例的属性,可写的字典
    _dir_ 返回了类或者对象所有成员列表,dir()函数调用的是_dir_(),如果提供了_dir_(),则返回属性列表,否则尽可能从__dict__属性中收集信息

    2 name

    获取类和函数的名字

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        pass
    class  B(A):
        pass
    def  C():
        pass
    print (A.__name__,B.__name__,C.__name__,sep='\n')

    python类之特殊属性和魔术方法

    3 module

    类定义所在的模块名

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        pass
    class B:
        pass
    print  (A.__module__,B.__module__)

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    import  disp
    print (disp.A.__module__,disp.B.__module__,sep='\n')

    python类之特殊属性和魔术方法

    4 class

    对象或类所属的类

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        pass
    class B(A):
        pass
    a=A()
    b=B()
    print (A.__class__,B.__class__,sep='\n')  #类所属的类是class
    print (a.__class__,b.__class__,sep='\n')  # 对象所属的类是实实在在的类

    python类之特殊属性和魔术方法

    5 bases

    类的基类的元组,顺序是他们在基类列表中出现的顺序

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        pass
    class B(A):
        pass
    class C(B):
        pass
    class E:
        pass
    class D(E,C):
        pass
    print (A.__bases__,B.__bases__,C.__bases__,D.__bases__,sep='\n')

    结果如下

    python类之特殊属性和魔术方法

    6 DOC

    文档字符串,针对类和函数有效,若不存在,则返回为None

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        '''this  is  class'''
        pass
    def B():
        '''this is  function'''
        pass
    class C:
        pass
    print (A.__doc__,B.__doc__,C.__doc__,sep='\n')

    结果如下
    python类之特殊属性和魔术方法

    7 mro

    类的mro。返回多继承中的查找顺序

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        pass
    class B(A):
        pass
    class C(B):
        pass
    class E:
        pass
    class D(E,C):
        pass
    print (A.__mro__,B.__mro__,C.__mro__,D.__mro__,sep='\n')

    结果如下

    python类之特殊属性和魔术方法

    8 dict

    类或者实例的属性,可写的字典

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        a=10
        def  __init__(self,x):
            self.x=5
    a=A(3)
    
    print (A.__dict__)
    print (a.__dict__)

    结果如下
    python类之特殊属性和魔术方法

    9 dir

    dir 返回了类或者对象所有成员名称列表,dir()函数调用的是_dir_(),如果提供了_dir_() ,则返回属性的列表,否则会尽量从__dict__属性中收集

    dir() 对于不同类型的对象具有不同的行为:
    1 如果对象是模块对象,则列表包含模块的属性名

    #!/usr/local/bin/python3.6
    #coding:utf-8
    import  re
    def  foo(x):
        y=1
    print (dir())  # 输出当前模块信息,此处会打印当前导入的模块和导入的函数
    print (dir(re))
    print ('+'*20)
    print (dir(foo))

    结果如下
    python类之特殊属性和魔术方法

    2 如果对象是类型或者类对象,列表包含类的属性名,以及其他基类的属性名

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class A:
        a='1234'
        def __init__(self):
            pass
    class  B(A): # 此处调用父类,其dir中会包含父类的属性
        pass
    print (dir())  # 输出当前模块信息,此处会打印当前导入的模块和导入的函数
    print ('*'*40)
    print (dir(A),dir(B),sep='\n')  # 此中DIR属性父类和子类是完全相同的,但dict中却是不同的
    print (A.__dict__,B.__dict__,sep='\n')

    python类之特殊属性和魔术方法

    3 如果是对象,列表包含对象的属性名,它的类的属性名和基类的属性名

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class A:
        a='1234'
        def __init__(self):
            self.x=10
    class  B(A): # 此处调用父类,其dir中会包含父类的属性
        pass
    a=A()
    print (dir())  # 输出当前模块信息,此处会打印当前导入的模块和导入的函数
    print ('*'*40)
    print (dir(A),dir(B),dir(a),sep='\n') #此处若是打印实例的属性,则会吧类的属性也打印上来

    结果如下
    python类之特殊属性和魔术方法

    4此处对属性名进行了重写操作

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class A:
        a='1234'
        def __init__(self):
            self.x=10
    class  B(A): # 此处调用父类,其dir中会包含父类的属性
        def __dir__(self): # 此处是针对实例设置的,和类本身并无关系
            return ['this is class A '] # 此处是dir返回是列表,若使用字符串,则会处理成列表进行返回
    a=A()
    b=B()
    print (dir())  # 输出当前模块信息,此处会打印当前导入的模块和导入的函数,以及实例后的对象
    print ('*'*40)
    print (dir(A),dir(B),dir(a),dir(b),sep='\n') #此处若是打印实例的属性,则会吧类的属性也打印上来

    结果如下
    python类之特殊属性和魔术方法

    10 _slots_ 槽位

    1 问题引出

    都是字典惹的祸
    字典为了提升查询效率,必须用空间换时间
    一般来说一个对象,属性多一点,都存储在字典中便于查询,问题不大,但是数百万个对象,那么字典就占得有点大了,这个时候,python便提供了_slots_

    2 实验代码

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class A:
        x=123
        __slots__ = ('p1',)  # 此处只放置了一个槽位,则只能使用p1变量,不能使用其他
        def __init__(self):
            self.p1=1
            self.p2=2
        def show(self):
            print ('this is  test1,{}'.format(self.p1))
    print (A().__dict__)

    结果如下
    python类之特殊属性和魔术方法

    此处的p2属性不能使用

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class A:
        x=123
        __slots__ = ('p1','p2')  # 此处只放置了一个槽位,则只能使用p1变量,不能使用其他
        def __init__(self):
            self.p1=1
            self.p2=2
        def show(self):
            print ('this is  test1,{}'.format(self.p1))
    print ('slots',A().__slots__)  #实例的此属性可以被打印出来,但实例的字典属性却不存在
    print (A.__dict__)  # 类的字典属性不受影响
    print ('dict',A().__dict__)

    结果如下
    python类之特殊属性和魔术方法

    添加实例属性如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class A:
        x=123
        __slots__ = 'p1','p2'  # 此处只放置了一个槽位,则只能使用p1变量,不能使用其他
        def __init__(self):
            self.p1=1
            self.p2=2
        def show(self):
            print ('this is  test1,{}'.format(self.p1))
    print ('slots',A().__slots__)  #实例的此属性可以被打印出来,但实例的字典属性却不存在
    print (A.__dict__)  # 类的字典属性不受影响
    A.p4=300  #类添加属性,只会影响类的__dict__,实例中不会显示,而__slots__ 不会对类造成影响
    try:
        setattr(A(),'p5',30)
    except  AttributeError  as  a:
        print (A(),'不能添加属性')
    finally:
        print (A().__slots__) #查看
    
    try:
        A().P3=300
    except  AttributeError  as  a:
        print (A(),'不能添加属性')
    finally:
        print (A().__slots__) #查看

    结果如下

    python类之特殊属性和魔术方法

    _slots_ 告诉解释器,实例的属性都叫什么,一般来说,既要节约内存,最好还是使用元祖来存储__slots__梳理,这样就阻止了实例产生__dict__来保存实例的属性

    继承

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class A:
        x=123
        __slots__ = 'p1','p2'  # 此处只放置了一个槽位,则只能使用p1变量,不能使用其他
        def __init__(self):
            self.p1=1
            self.p2=2
        def show(self):
            print ('this is  test1,{}'.format(self.p1))
    class B(A):
        def __init__(self):
            super().__init__()
            self.b1=200
    b=B()
    b.b2=300
    print (b.__dict__)  # 继承则失效 

    结果如下
    python类之特殊属性和魔术方法

    3 应用场景

    使用需要构建在数百万以上对象,且内存容量较为紧张的场景

    二 python 实例属性之魔术方法

    1 分类

    描述 方法
    初始化和销毁 _init__和_del\
    在字典和set中使用 _hash_
    布尔类型,常用于判断语句 _bool_
    可视化,用于输出对应的类型 _str__和_repr\
    运算符重载 _eq_,_ne_,_gt_,__lt__等
    容器和大小相关和操作相关属性 _getitem_,__setitem__等
    可调用对象,将实例化的对象当成一个函数去调用,一旦可以当函数调用 _call_
    上下文管理(with open(x) as f 等形式 _enter_,_exit_
    反射 _getattr_, _setattr_,_delattr_
    描述器 Object._get_(self,instance,owner)Object._set_(self,instance,value)Object._delete_(self,instance)

    2 初始化和销毁

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self,name):
            self.name=name
            self.x=10
            print ("init  instance")
        def __del__(self):
            print ('delete {}'.format(self.name))
    a=X('a')
    del  a  # 因为python自身的垃圾回收机制,而del删除实例的操作不确定何时执行,因此需要使用del进行清除处理

    结果如下
    python类之特殊属性和魔术方法

    3 hash

    1 简介

    hash 中最基础的hash就是取模运算。
    list 不能hash的原因
    list 源码: 其中hash=None,在调用None的时候自然是不能hash的

    python类之特殊属性和魔术方法

    判断是否可hash

    #!/usr/local/bin/python3.6
    #coding:utf-8
    from   collections import Hashable
    class X:
        def __init__(self,x):
            self.x=x
        def __hash__(self):
            return 1
    print (isinstance(X(1),Hashable))
    print (isinstance([],Hashable))

    结果如下
    python类之特殊属性和魔术方法

    2 定义不可哈希类型

    #!/usr/local/bin/python3.6
    #coding:utf-8
    from collections  import  Hashable
    class X:
        def __init__(self):
            self.x=10
        __hash__=None
    
    print (isinstance(X(),Hashable))  #判断是否可hash,返回为bool类型
    print (hash(X()))

    结果如下
    python类之特殊属性和魔术方法

    3 实例

    实例如下

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self):
            self.x=10
        def __hash__(self):  # 此处是定义的是实例的hash,和类没关系
            return  None  # 此处设置hash的返回为None,模拟列表
            # 另一种方式定义不可哈希类型  __hash__=None
    class Y:  #此类未设置相关的hash属性
        def __init__(self):
            pass
    class Z: # 此类定义了hash的返回值为1 ,则实例化后调用hash返回为1
        def __hash__(self):
            return  1
    print (hash(Y())) # 此处返回整数
    print (hash(Z())) # 此处返回为固定数
    print (hash(X()))  #进行判断是否能够进行hash操作,未进行hash,直接抛出异常

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self):
            self.x=10
        def __hash__(self):
            return 1
    lst=[X(),X()] # 此处进行实例化并装载到列表中
    print  (lst) # 此处打印列表的值,指定的是内存地址
    
    s=set(lst)  # 此处定义一个集合,集合本身具有去重功能,上述的hash的返回值是相同的,则说明其hash相同,则表明其key相同,常理推论可得
    # 其会走set的去重属性进行处理
    print (len(s))
    for  i in  s:  # 打印其中的值,如下
        print (hash(i))

    结果如下
    python类之特殊属性和魔术方法

    此处s集合中的元素hash后的结果是相同的,但是其却没有进行去重操作,则此处表明hash相等的函数不一定就是相同的,此处没有直接的相等关系

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self):
            self.x=10
        def __hash__(self):
            return 1
        def __eq__(self, other):  # 此处是判断是否相等的原因,及就是其值必须相等的情况下才能说是同一个,而不是hash相同就认为是同一个,此处返回bool值,当然可以是0或非0的数
            return True
    lst=[X(),X()] # 此处进行实例化并装载到列表中
    print  (lst) # 此处打印列表的值,指定的是内存地址
    
    s=set(lst)  # 此处定义一个集合,集合本身具有去重功能,
    # 其会走set的去重属性进行处理
    print (len(s))
    for  i in  s:  # 打印其中的值,如下
        print (hash(i))

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self):
            self.x=10
        def __hash__(self):
            return 1
        def __eq__(self, other):  # 此处是判断是否相等的原因,及就是其值必须相等的情况下才能说是同一个,而不是hash相同就认为是同一个
            return False
    lst=[X(),X()] # 此处进行实例化并装载到列表中
    print  (lst) # 此处打印列表的值,指定的是内存地址
    
    s=set(lst)  # 此处定义一个集合,集合本身具有去重功能,上述的hash的返回值是相同的,则说明其hash相同,则表明其key相同,常理推论可得
    # 其会走set的去重属性进行处理
    print (len(s))
    for  i in  s:  # 打印其中的值,如下
        print (hash(i))

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self):
            self.x=10
        def __hash__(self):
            return 1
        def __eq__(self, other):  # 此处是判断是否相等的原因,及就是其值必须相等的情况下才能说是同一个,而不是hash相同就认为是同一个
            return False
    a=X()
    lst=[a,a]
    print (lst)
    s=set(lst)
    print (len(s))
    for x  in  s:
        print  (hash(x))

    结果如下
    python类之特殊属性和魔术方法

    4 结论

    set判断是否是同一个的步骤:
    1 先进行内存地址的判断,及is判断,若内存地址相同,则肯定是同一个
    2 若内存地址不同,再进行eq 判断,及就是==进行判断,若相同,则是同一个,若不同,则不是同一个,此处和hash没有任何关系


    及就是: 同假为假,一真一假为真,同真为真

    默认的能够使用hash的原因是由于在基类object中实现了hash方法,一般的,不同的内存地址的hash是不相同的,两个对象的hash相同,叫hash冲突

    hash 相同不代表一样

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class X:
        def __init__(self,x):
            self.x=x
        def __hash__(self):
            return 1
        def __eq__(self, other):  # 此处是判断是否相等的原因,及就是其值必须相等的情况下才能说是同一个,而不是hash相同就认为是同一个
            return self.x==other.x#  此处表示二元操作,前面调用方法self.x,后面的作为参数传递进去进行处理,other表示第二个对象对应的方法
    
    print  (hash(X(4)))
    lst=[X(4),X(6)]
    t=tuple(lst)
    s=set(lst)
    print (s)
    for x  in  s:
        print  (hash(x))

    结果如下

    python类之特殊属性和魔术方法

    __hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__来判断2个对象是否相等,

    5 练习:

    设计二维坐标类Point,比较2个坐标是否相等

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self,x,y):
            self.x=x
            self.y=y
        def __hash__(self):
            return  hash((self.x,self.y))   # 此处返回是一个元组,是不可变数据类型,此处可直接进行hash
        def  __eq__(self, other):
            return self.x==other.x  and  self.y == other.y
    a1=Point(3,4)
    a2=Point(3,4)
    print ( a1 is a2 )
    print (a1==a2)

    结果如下
    python类之特殊属性和魔术方法

    4 bool

    1简介

    _bool_ 内建函数bool(), 或者对象放在逻辑表达式的位置,调用这个函数返回布尔值,没有定义_bool_,就找_len_ 返回长度,非0为真,如果__len__也没有定义,则所有的实例都返回是真。

    2 实例

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:  # 此类未定义len和bool,因此其返回值为恒真
        def  __init__(self):
            self.x=3
            self.y=4
        # def __bool__(self):
        #     return False
    
    print (bool(Point()))

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def __bool__(self): # 此处定义了bool的返回值为False,则调用bool()返回结果应该为False
            return False
    print (bool(Point()))

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        # def __bool__(self): # 此处定义了bool的返回值为False,则调用bool()返回结果应该为False
        #     return False
        def __len__(self): # 此处用于当bool不存在时的找寻位置,为0则表示为空,则为False
            return 0
    print (bool(Point()))

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def __bool__(self): # 同时存在,则以bool为准 
            return False
        def __len__(self): 
            return 1
    print (bool(Point()))

    结果如下
    python类之特殊属性和魔术方法

    这也就是为啥空的字典和空集合以及空列表为False的原因了,因为其没有定义bool,因此其只能通过访问len来实现了 。

    5 可视化

    1 简介

    方法 意义
    _repr_ 内建函数repr()对一个对象获取字符串表达式,如果一个类定义了_repr__但没有定义_str\,那么在请求该类的实例的"非正式"的字符串也将调用_repr_()
    _str_ str() 函数,内建函数format,print()函数调用,需要返回对象的字符串表达式
    _bytes_ bytes 的时候,返回一个独享的bytes表达,及返回bytes对象

    2 基础实例

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def  __repr__(self):
            return str([self.x,self.y])  #此处的返回必须使用字符串进行包裹,否则会报错
    print (Point())

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def  __repr__(self):
            return str([self.x,self.y])  #此处的返回必须使用字符串进行包裹,否则会报错
        def  __str__(self):  # 若存在此属性,则上述的表达式将不会被调用
            return  'abcdefg'
    print (Point())

    结果如下

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def  __repr__(self):
            return str([self.x,self.y])  #此处的返回必须使用字符串进行包裹,否则会报错
        def  __str__(self):  # 若存在此属性,则上述的表达式将不会被调用
            return  'abcdefg'
    print (Point())
    p1=Point()
    p2=Point()
    lst=[p1,p2]
    for x in  lst:
        print (x)
    print (lst)

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class Point:
        def  __init__(self):
            self.x=3
            self.y=4
        def  __repr__(self):
            return str([self.x,self.y])  #此处的返回必须使用字符串进行包裹,否则会报错
        def  __str__(self):  # 若存在此属性,则上述的表达式将不会被调用
            return  'abcdefg'
    print (Point())
    p1=Point()
    p2=Point()
    lst=(p1,p2)
    for x in  lst:
        print (x)
    print (lst)
    print (*lst)  #进行解包处理,此时是针对于对象上的,此时应该调用的是str

    python类之特殊属性和魔术方法

    3 结论

    上述实例证明,当str和repr同时存在时,如果输出结果直接作用于对象上,则调用str方法,否则将调用repr方法

    6 运算符重载

    1 简介

    operator 模块提供以下的特殊方法,可以将类的实例使用下面操作符来进行操作

    运算符 特殊方法 含义
    <,<=,==,>,>=,!= _lt_,_le_,_eq_,_gt_,_ge_,_ne_ 比较运算符
    +,-,*,/,%,//,**,divmod _add_,_sub_,_mul_,_truediv_,_mod_,_floordiv_,_pow_,_divmod_ 算数运算符,移位,位运算也有对应的方法
    +=,-=,*=,/=,%=,//=,**= _iadd_,_isub_,_imul_,_itruediv_,_imod_,_ifloordiv_,_ipow_

    2 实验

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __init__(self,x):
            self.x=x
        def __lt__(self, other):
            return  self.x  < other.x
        def __eq__(self, other):
            return  self.x == other.x
        def __ne__(self, other):
            return  self.x  != other.x
        def __sub__(self, other):
            return  self.x - other.x
    
    print (A(10)<A(5))
    print (A(10)==A(5))
    print (A(10) != A(5))
    print (A(10)-A(5))

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __init__(self,x):
            self.x=x
        def __iadd__(self, other):  # 此处定义的是+= 返回的是self=self+other
            self.x += other.x
            print ('__iadd__')
            return  self  # 此处返回的是一个实例,可通过调用其方法来实现此方法是否执行
    
    a1=A(10)
    a2=A(20)
    print ('*'*30)
    a1+=a2
    print (a1.x,a2.x)

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __init__(self,x):
            self.x=x
        def __iadd__(self, other):  # 此处定义的是+= 返回的是self=self+other
            # self.x += other.x
            print ('__iadd__')
            return  A(self.x+other.x )  #此处方法相同
    
    a1=A(10)
    a2=A(20)
    print ('*'*30)
    a1+=a2
    print (a1.x,a2.x)

    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __init__(self,x):
            self.x=x
        def __iadd__(self, other):  # 此处定义的是+= 返回的是self=self+other
            # self.x += other.x
            print ('__iadd__')
            return  A(self.x+other.x )  #此处方法相同
        def  __isub__(self, other):
            print ('__isub__')
            self.x -= other.x
            return  self
    
    a1=A(10)
    a2=A(20)
    print ('*'*30)
    a1+=a2
    print (a1.x,a2.x)
    a1-=a2
    print (a1.x,a2.x)

    python类之特殊属性和魔术方法

    functools.total_ordering 的应用

    默认的

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __init__(self,x):
            self.x=x
        def __lt__(self, other):  # 此处定义的是小于,现需要使用大于等于,则默认会报错
            return self.x  < other.x
    
    print (A(1) >= A(2))

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    from  functools  import total_ordering
    @total_ordering
    class  A:
        def  __init__(self,x):
            self.x=x
        def __lt__(self, other):  # 此处定义的是小于,现需要使用大于等于,则默认会报错
            return self.x  < other.x
    
    print (A(1) >= A(2))
    print (A(1)==A(2))
    print (A(1) != A(2))

    结果如下
    python类之特殊属性和魔术方法

    3 运算符重载中的反向方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class Add:
        def __init__(self,x:int):
            self.x=x
        def __add__(self, other):
            print ('add',self)
            return self.x+other.x
        def __iadd__(self, other):
            print ('iadd',self)
            return  self.x+other.x
        def __radd__(self, other):
            print ('radd',self)
            return  self.x+other.x
    class B:
        def __init__(self,x):
            self.x=x
    a=Add(3)
    b=B(4)
    print (a+b)
    print (b+a) 

    结果如下

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class Add:
        def __init__(self,x:int):
            self.x=x
        def __add__(self, other):
            print ('add',self)
            return self.x+other.x
        def __iadd__(self, other):
            print ('iadd',self)
            return  self.x+other.x
        def __radd__(self, other):
            print ('radd',self)
            return  self+other # 此处self调用了add,其相当于实例self.__add__(other)进行处理的
    class B:
        def __init__(self,x):
            self.x=x
    a=Add(3)
    b=B(4)
    print (a+b)
    print (b+a)

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    # this is test
    class Add:
        def __init__(self,x):
            self.x=x
        def __add__(self, other):
            print ('add',self)
            return self.x+other
        def __iadd__(self, other):
            print ('iadd',self)
            return  self.x+other.x
        def __radd__(self, other):
            print ('radd',self)
            return  self+other # 此处self调用了add,其相当于实例self.__add__(other)进行处理的
    class B:
        def __init__(self,x):
            self.x=x
    a=Add(3)
    b=B(4)
    
    print (1+a)
    print (1+b)

    结果如下
    python类之特殊属性和魔术方法

    结论如下:

    b+a 等价于 b.add(a),但是B类没有实现add方法,就去找a的__radd__方法 
    
    1+a 等价于1.add(a),而int 类型实现了__add__放方法,不过这个方法对于这种加法的返回值是NotImplemented,解释器发现了这个值,就会对第二个操作对象执行__radd__进行调用。

    4 总结

    运算符重载的应用场景
    往往是面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式,int 类中,几乎实现了所有操作符,可以作为参考

    7 容器相关方法

    1 简介

    内建方法 含义
    _len_ 内建函数len(),返回对象的长度(>=0的整数),其实即使吧对象当作容器类型来看,就如同list或dict,bool()函数调用的时候,如果没有_bool_()方法,则会看_len_()方法是否存在,存在返回非0为真,第三方库中可能存在size,其和len的含义相同
    _iter_ 迭代器时,调用,返回一个新的迭代器对象
    _contains_ in成员运算符,没有实现,就调用__iter__方法遍历
    _getitem_ 实现self[key]访问,序列对象,key接受整数为索引,或者切片,对于set和dict,key为hashable,key不存在时引KeyError异常
    _setitem_ 和__getitem__的访问相似,是设置值的方法
    _missing_ 字典使用_getitem_()调用时,key不存在执行该方法

    2 实例

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  Item:
        def __init__(self,name,*args):
            self.name=name
            self.lst=list(args)
        def  __len__(self):
            return len(self.lst)
        def __iter__(self):
            return iter(self.lst)  # 此处返回是一个迭代器,必须是一个迭代器
        def __add__(self, other):  # 此处使用+ 号返回一个列表
            self.lst.append(other)
            return self
        def __getitem__(self, index):  # 此处应用于列表时,表示为索引,此处应用于字典时,表示key
            if  index   > len(self.lst):
                print ('Key Error')
            else:
                return self.lst[index]
        def __setitem__(self, index, value): # 此处表示修改属性列表中的值
            if  index   > len(self.lst):
                print ('Key Error')
            else:
                self.lst[index]=value
                return   self
        # def __missing__(self, key): # 此方法只能适用于字典的处理
        #     pass
        def  __repr__(self):
            return str(self.lst)  # 此处对其进行可视化处理
    a=Item('mysql',12,3,45,678,8909)
    print (len(a))
    # 此处调用了__iter__方法
    for i  in a:
        print (i)
    print ('++++++++++++++++')
    print (a[2])  # 此处调用了__getitem__方法,用于获取值
    a+10  # 此处使用__add__方法进行加入,此处追加到列表的末尾
    print (a[-1])  # 获取列表的最后一个元素,则得到此值
    a[1]=20  # 使用__setitem__方法修改属性
    print (a[1])  #返回对应位置的值
    a+10+20+30+40  # 此处进行连加操作,因为其add方法返回是self,因此每次赋值后都会增加
    print (a)

    结果如下
    python类之特殊属性和魔术方法

    8 可调用对象

    1 简介

    在python中一切皆对象,函数也不例外
    可调用对象
    方法
    __call__类中出现该方法,实例就可以像函数一样调用,
    可调用对象: 定义一个类,并实例化得到其实例,将实例像函数一样调用。调用是实例的,不是类的。

    2 实例

    #!/usr/local/bin/python3.6
    #coding:utf-8
    def  foo():
        print (foo.__module__,foo.__name__)
    
    foo.__call__()# 此处的方法和下面的相同,皆是调用该函数
    foo()
    print (dir(foo))

    结果如下

    python类之特殊属性和魔术方法

    函数的可调用原因是函数实现了\call()方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    def  foo():
        print (foo.__module__,foo.__name__)
    
    print (foo.__call__) # 此处返回一个函数对象是一个wrapper
    foo.__call__()# 此处的方法和下面的相同,皆是调用该函数
    foo()

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def __init__(self):
            self.x=1
        def __call__(self, *args):  # 此处的第一个是self,表明其是给实例使用的,并不是给类使用的
            return   args  # 此处返回一个元组
    
    print (A()(12344))  # 此处第一个括号是实例化,第二个是传递参数并调用实例

    python类之特殊属性和魔术方法

    3 练习

    利用封装完成斐波那契额数列

    方法1

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def  __call__(self,num):
            a,b=0,1
            for  i in  range(num):
                print (b)
                a,b=b,a+b
    A()(10)

    结果如下
    python类之特殊属性和魔术方法

    方法2

    #!/usr/local/bin/python3.6
    #coding:utf-8
    class  A:
        def __init__(self):
            self.lst=[1,1,2]
        def  __call__(self,num):
            if  num < 3:
                return   self.lst[:num]
            else:
                for  i in range(num-3):
                    self.lst.append(self.lst[-1]+self.lst[-2])
                return  self.lst
    print (A()(10))

    结果如下

    python类之特殊属性和魔术方法

    添加功能如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  A:
        def __init__(self):
            self.lst=[1,1,2]
        def  __len__(self):
            return len(self.lst)
        def  __call__(self,x):
            if  len(self.lst)  >  x:
                return  self.lst[:x]
            for  i  in   range(2,x):
                self.lst.append(self.lst[i]+self.lst[i-1])
            return  self.lst
        def __getitem__(self, item):
            if item < 0:
                return  None
            if  len(self) > item:
                return self.lst[item]
        def __iter__(self):
            return  iter(self.lst)
    
    a=A()
    print (a(10))
    print (a[4])
    for x in a:
        print (x)

    结果如下

    python类之特殊属性和魔术方法

    9 上下文管理

    1 推导过程

    文件IO操作可以对文件对象进行上下文管理,使用with...as语法
    推导过程

    #!/usr/bin/poython3.6
    #conding:utf-8
    # 此处为默认的上下文管理
    # with   open('test')  as f:
    #     pass
    
    class A:
        pass
    
    with  A()  as  f:
        pass

    结果如下
    python类之特殊属性和魔术方法

    提示需要添加 __enter__属性
    添加如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    # 此处为默认的上下文管理
    # with   open('test')  as f:
    #     pass
    
    class A:
        def  __enter__(self):
            pass
    
    with  A()  as  f:
        pass

    结果如下
    python类之特殊属性和魔术方法

    提示需要添加 __exit__属性

    #!/usr/bin/poython3.6
    #conding:utf-8
    # 此处为默认的上下文管理
    # with   open('test')  as f:
    #     pass
    
    class A:
        def  __enter__(self):
            pass
        def  __exit__(self, exc_type, exc_val, exc_tb):
            pass
    with  A()  as  f:
        pass

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __enter__(self):
            print ('__enter__')
        def  __exit__(self, exc_type, exc_val, exc_tb):
            print ('__exit__')
    with  A()  as  f:
        pass

    由此图可知,其调用顺序是先调用_enter_,后调用_exit_

    2 属性

    方法 意义
    _enter_ 进入于此对象相关的上下文,如果存在该方法,with语法会把该方法的返回值作为绑定到as字句中指定的变量上
    _exit_ 退出与此对象的上下文

    exit 中变量的含义:
    1 exc_type: 异常类型,如果没有异常,则返回是None
    2 exc_tb:异常追踪信息,如果没有异常,则是None
    3 exc_va :异常对应的值,如果没异常,则是None
    此处的return 用于压制异常,若此处是False,则会抛出异常,等效True 或 False

    缺少了enter 进不去,缺少了exitc出不来

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('init instance')
        def  __enter__(self):
            print ('__enter__')
            return 1
        def  __exit__(self, exc_type, exc_val, exc_tb):
            print ('__exit__')
    p=A()
    with  p  as  f:  # 此处的p是__enter__的返回值,是f的参数,若此处__enter__无return,则默认返回为None,无意义
        print (p==f) # 此处用于比较p和f的关系 
        print (p is f)
        print (p)
        print (f)

    python类之特殊属性和魔术方法

    上述结论如下:
    实例化对象的时候,并不会调用enter,进入with语句块会调用__enter__方法,然后执行语句体,最后离开with语句块的时候,调用__exit__方法

    with 可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。

    3 上下文管理对异常的处理方式

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('init instance')
        def  __enter__(self):
            print ('__enter__')
            return 1
        def  __exit__(self, exc_type, exc_val, exc_tb):
            print ('__exit__')
    p=A()
    with  p  as  f:  # 此处的p是__enter__的返回值,是f的参数,若此处__enter__无return,则默认返回为None,无意义
        raise Exception('Error')  # 此处抛出异常,一般的,抛出异常后,语句将不会再次执行
        print (p==f) # 此处用于比较p和f的关系
        print (p is f)
        print (p)
        print (f)

    结果如下

    python类之特殊属性和魔术方法

    由此证明,当异常抛出时,exit对应的语句仍然会被执行。

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  sys
    class A:
        def  __init__(self):
            print ('init instance')
        def  __enter__(self):
            print ('__enter__')
            return 1
        def  __exit__(self, exc_type, exc_val, exc_tb):
            print ('__exit__')
    p=A()
    with  p  as  f:  # 此处的p是__enter__的返回值,是f的参数,若此处__enter__无return,则默认返回为None,无意义
        sys.exit()  # 此处的是直接退出
        print (p==f) # 此处用于比较p和f的关系
        print (p is f)
        print (p)
        print (f)

    结果如下
    python类之特殊属性和魔术方法

    上述证明,此处满足上述清理工作,上下文管理非常安全,能够保证变量的顺利清除工作。

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  sys
    class A:
        def  __init__(self):
            print ('init instance')
        def  __enter__(self):
            print ('__enter__')
            return self
        def  __exit__(self, exc_type, exc_val, exc_tb):
            print ('__exit__')
            print (exc_tb) #追踪信息
            print (exc_type)  # 类型
            print (exc_val)  # 值
            return  1  # 此处设置为1 是压制异常,不让其出现
    p=A()
    with  p  as  f:  # 此处的p是__enter__的返回值,是f的参数,若此处__enter__无return,则默认返回为None,无意义
        raise   Exception('Error1234454')
        print (p==f) # 此处用于比较p和f的关系
        print (p is f)
        print (p)
        print (f)

    结果如下
    python类之特殊属性和魔术方法

    4 通过此方法进行函数执行时长计算

    之前的计算时长方式

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import  time
    import  sys
    def  wapper(fn):
        def _wapper(*args,**kwargs):
            start_time=datetime.datetime.now()
            ret  =  fn(*args,**kwargs)
            delta=(datetime.datetime.now()-start_time).total_seconds()
            print ("{} 函数的执行时间为: {}".format(fn.__name__,delta))
            return  ret
        return  _wapper
    @wapper
    def  add(x,y):
        time.sleep(2)
        return  x+y
    
    add(4,5)

    结果如下
    python类之特殊属性和魔术方法

    使用上下文管理的方式统计函数执行时间

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import  time
    class Timer:
        def  __init__(self,fn):
            self.fn=fn
        def  __enter__(self):
            self.start_time=datetime.datetime.now()
            return  self.fn  # 此处对应的是as前面的值
        def __exit__(self, exc_type, exc_val, exc_tb):
            delat=(datetime.datetime.now()-self.start_time).total_seconds()
            print ("函数{} 的执行时间为: {}".format(self.fn.__name__,delat))
            return  1
    def  add(x,y):
        return  x+y
    p=Timer(add)
    with   p  as f:  # 此处调用的是__enter__的返回值,重命名为f
        time.sleep(2)
        print (f(4,5))

    结果如下
    python类之特殊属性和魔术方法

    5 类装饰器的应用

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import time
    from functools import  wraps
    class A:
        def __init__(self,fn):
            self.fn=fn
        def __call__(self,*args,**kwargs):  #实例调用支持的方法
            self.start_time=datetime.datetime.now()
            ret = self.fn(*args,**kwargs)
            delta=(datetime.datetime.now()-self.start_time).total_seconds()
            print ("{} 函数的执行时间为: {}".format(self.fn.__name__,delta))
            return  ret
    @A   #add=A(add)
    def add(x,y):
        time.sleep(2)
        return  x+y
    print  (add(10,20))

    结果如下

    python类之特殊属性和魔术方法

    6 进行属性覆盖如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import time
    from functools import  wraps
    class A:
        def __init__(self,fn):
            self.fn=fn
        def __call__(self,*args,**kwargs):  #实例调用支持的方法
            self.start_time=datetime.datetime.now()
            ret = self.fn(*args,**kwargs)
            delta=(datetime.datetime.now()-self.start_time).total_seconds()
            print ("{} 函数的执行时间为: {}".format(self.fn.__name__,delta))
            return  ret
    @A   #add=A(add)
    def add(x,y):
        '''this is function'''
        time.sleep(2)
        return  x+y
    print  (add(10,20))
    print  (add.__doc__) # 此处打印出文档

    结果如下

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import time
    from functools import  wraps
    class A:
        def __init__(self,fn):
            self.__doc__=fn.__doc__  # 此处只能进行部分的属性覆盖操作
            self.__name__=fn.__name__
            self.fn=fn
        def __call__(self,*args,**kwargs):  #实例调用支持的方法
            self.start_time=datetime.datetime.now()
            ret = self.fn(*args,**kwargs)
            delta=(datetime.datetime.now()-self.start_time).total_seconds()
            print ("{} 函数的执行时间为: {}".format(self.fn.__name__,delta))
            return  ret
    @A   #add=A(add)
    def add(x,y):
        '''this is function'''
        time.sleep(2)
        return  x+y
    print  (add(10,20))
    print  (add.__doc__) # 此处打印出文档

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  datetime
    import time
    from functools import  wraps
    class A:
        def __init__(self,fn):
            wraps(fn)(self)  # 调用此方法完成属性的覆盖操作,此处第一个是原函数,后面是现在的函数
            self.fn=fn
        def __call__(self,*args,**kwargs):  #实例调用支持的方法
            self.start_time=datetime.datetime.now()
            ret = self.fn(*args,**kwargs)
            delta=(datetime.datetime.now()-self.start_time).total_seconds()
            print ("{} 函数的执行时间为: {}".format(self.fn.__name__,delta))
            return  ret
    @A   #add=A(add)
    def add(x,y):
        '''this is function'''
        time.sleep(2)
        return  x+y
    print  (add(10,20))
    print  (add.__doc__) # 此处打印出文档

    结果如下
    python类之特殊属性和魔术方法

    7 contextlib.contextmanager

    是一个装饰器实现上下文管理,装饰一个函数,而不像类一样可以实现__enter__和__exit__方法

    对下面的函数有要求,必须有yield,也就是这个函数必须返回一个和生成器,且只有一个yield的值。

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   contextlib
    @contextlib.contextmanager
    def foo():
        print ('enter')
        yield
        print ('exit')
    
    with  foo() as f:
        pass

    结果如下

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   contextlib
    @contextlib.contextmanager
    def foo():
        print ('enter')
        yield  [1,2,34,56,5867,856,867,]  # 此处相当分界线,用于分割上面和下面的执行
        print ('exit')
    
    with  foo() as f:
        print (f)

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   contextlib
    @contextlib.contextmanager
    def foo():
        print ('enter')
        yield  [1,2,34,56,5867,856,867,]  # 此处相当分界线,用于分割上面和下面的执行
        print ('12344exit')
    
    with  foo() as f:
        try:
            raise Exception
        finally:
            print ('exit')
        print (f)

    结果如下

    python类之特殊属性和魔术方法

    修改异常捕获如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    import   contextlib
    @contextlib.contextmanager
    def foo():
        print ('enter')
        try:
            yield  [1,2,34,56,5867,856,867,]  # 此处相当分界线,用于分割上面和下面的执行
        finally:  # 放置在此处能够执行后面的相关操作
            print ('12344exit')
    
    with  foo() as f:
        raise  Exception
        print (f)

    结果如下
    python类之特殊属性和魔术方法

    总结: 如果业务逻辑简单可以使用函数加装饰器的方式,如果业务复杂,用类的方式加__enter__和__exit__更方便

    8 上下文的应用场景

    1 增强功能
    在代码执行的前后增加代码,以增强其功能,类似装饰器的功能

    2 资源管理
    打开了资源需要关闭,例如文件对象,网络链接,数据库链接等

    3 权限验证

    在执行代码之前,做权限的验证,在enter 中处理
    在代码进入的时候进行处理,在权限出去则不管

    10 反射

    1 概述

    运行时:区别于编译时,指的是程序被加载到内存中执行的时候。


    反射:python中,能够通过一个对象,找出其type,class,attribute或method的能力,成为反射或自醒。


    具有反射能力的函数type(),isinstance(),callable()(查看对象能否被调用),dir(),getattr()

    2 内建函数

    object 可以是类或实例 
    语法格式:
    getattr(object,name[,default]) : 通过name 返回object的属性值,当属性不存在时,将使用default返回,如果没有default,则抛出attributeError,name 必须位字符串 
    
    setattr(object,name,value) object 的属性存在,则覆盖,若不存在,则新增。
    
    hasattr(object,name)  判断对象是否有这个名字属性,name必须时字符串

    3 实例

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  A:
        x=10
        def __init__(self):
            self.x=5
    
    setattr(A,'y',20)  # 动态添加类属性位y=20
    print (A.__dict__)  # 打印属性信息列表
    a=A()
    setattr(a,'z',100)  # 实例动态增加属性
    print (getattr(A,'y')) # 查看增加的属性是否存在
    print (getattr(A,'x'))  # 定义属性是否存在
    print (getattr(a,'y'))  # 查看实例中是否存在该属性
    print (a.__dict__) # 查看实例属性信息中是否具有'z'属性
    print (A.__dict__)  # 打印属性信息列表,此处查看当实例属性信息增加后,类属性信息是否增加
    
    if hasattr(a,'z'):
        print ("{} 函数存在此属性 {}".format('a','y'))

    结果如下
    python类之特殊属性和魔术方法
    进阶

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  A:
        x=10
        def __init__(self,y):
            self.x=5
            self.y=y
    a=A(20)
    setattr(A,'printf',lambda  self: 1 ) #此处增加一个类的属性,并通过参数调用
    
    setattr(a,'myclass',lambda  : 10 ) # 此处增加一个实例属性
    print (a.printf())  # 打印此类的属性结果如下
    print (getattr(a,'printf')())  # 因为此处是函数,因此需要通过()来进行调用
    print (getattr(a,'myclass')())
    if not  hasattr(A,'sub'):  # 此处添加一个类的函数属性,实现函数之前的差运算
        setattr(A,'sub',lambda self,other : A(self.y- other.y) )
    if not  hasattr(A,'add'): # 此处添加一个类的属性,实现函数之间的和的计算
        setattr(A,'add',lambda self,other: (self.y + other.y))
    print (a.__dict__)
    print (A.__dict__)
    b1=A(10)
    b2=A(20)
    print (b2.sub(b1))
    print (b1.add(b2))

    结果如下

    python类之特殊属性和魔术方法

    注意:
    这种动态增加属性的方式是运行时改变类或者实例的方式,比装饰器和Mixin更加灵活,其具有更大的使用用途

    4 实例应用

    实现分发器
    简单雏形

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  Dispatcher:
        def cmd1(self):  # 此处在内部定义一个方法
            print ('cmd10')
        def run(self):  # 此处用于执行
            while True:
                cmd=input("plase  input str:")  #退出程序命令
                if  cmd.strip() == 'q'  or  cmd.strip() == 'quit':
                    return
                getattr(self,cmd.strip(),self.__defaltfun)()  # 此处用于获取该方法,若不存在,则执行默认程序
        def __defaltfun(self):
            print ('default')
    a=Dispatcher()
    a.run()

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  Dispatcher:
        def cmd1(self):  # 此处在内部定义一个方法
            print ('cmd10')
        def  reg(self,cmd,fn):
            if isinstance(cmd,str):
                setattr(self.__class__,cmd.strip(),fn)  # 此处使用的是类,若是实例化,则需要进行下面将self传入进去的方式进行调用
            else:
                print ('TypeError')
        def run(self):
            while True:
                cmd = input("plase  input str:")
                if  cmd.strip()  == 'q'  or  cmd.strip() == 'quit':
                    return
                getattr(self,cmd.strip(),self.defaultfun)()
        def defaultfun(self):
            print ('default')
    
    a=Dispatcher()
    a.reg('cmd2',lambda  self :print (2)) 
    a.reg('cmd3',lambda  self :print (3))
    a.run()

    结果如下

    python类之特殊属性和魔术方法

    5 反射相关魔术方法

    魔术方法 意义
    _getattr_() 当通过搜索实例,实例的类以及祖先类查不到的属性,就会调用此方法
    _setattr_() 通过访问实例属性,进行增加,修改都要调用它
    _delattr_() 当通过实例删除属性的时候调用此方法
    _getattribute_ 实例所有的属性调用都是从政方法开始

    实例如下:

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    class  Base:
        a=5
    
    class A(Base):
        m=6
        def __init__(self,x):
            print ('init')
            self.x=x  #此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次
        def __getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值
            print ('__getattr__',item)
            self.__dict__[item]=None
        def __setattr__(self, key, value): #设置一个属性时,一定要执行,至于是否生效,则另当别论
            print ('__setattr__',key,value)
        def __delattr__(self, item): #此处在删除一个实例的属性进行的操作,只要实例能找到,都能够删除
            print ('__delattr__',item)
    A.n=50 # 此处是正常的添加类属性,不会产生其他的报错
    a=A(20)
    print (a.__dict__)
    a.b  # 针对不存在的属性进行调用
    a.x=30 # 设置实例的属性变化
    a.c=200 #添加一个不存在的属性
    del  a.a # 删除一个实例的属性
    print (a.__dict__)

    结果如下

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    class  Base:
        a=5
    
    class A(Base):
        m=6
        def __init__(self,x):
            print ('init')
            self.x=x  #此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次
        def __getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值
            print ('__getattr__',item)
            self.__dict__[item]=None
        def __setattr__(self, key, value): #设置一个属性时,一定要执行,至于是否生效,则另当别论
            print ('__setattr__',key,value)
        def __delattr__(self, item): #此处在删除一个实例的属性进行的操作,只要实例能找到,都能够删除
            print ('__delattr__',item)
    
        def __getattribute__(self, item):  #此处是在字典属性之前进行拦截执行
            print ('__getattribute__',item)
    a=A(20)
    
    print (a.x)

    结果如下
    python类之特殊属性和魔术方法

    实例的所有属性的访问,第一个都会调用__getattribute__方法,其阻止了属性查找,该方法应该返回(计算后)值或者抛出一个attributeError 异常
    他的return 值将作为属性查找的结果,如果抛出attributeError 异常,则会直接调用__getattr__方法,因为表示属性没有找到

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    class  Base:
        a=5
    
    class A(Base):
        m=6
        def __init__(self,x):
            print ('init')
            self.x=x  #此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次
        def __getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值
            print ('__getattr__',item)
            # self.__dict__[item]=None
        def __setattr__(self, key, value): #设置一个属性时,一定要执行,至于是否生效,则另当别论
            print ('__setattr__',key,value)
        def __delattr__(self, item): #此处在删除一个实例的属性进行的操作,只要实例能找到,都能够删除
            print ('__delattr__',item)
    
        def __getattribute__(self, item):  #此处是在字典属性之前进行拦截执行
            print ('__getattribute__',item)
            raise  AttributeError(item)  # 此处若属性不存在,抛出异常,则直接进入getattr中机型处理
    a=A(20)
    
    print (a.x)

    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    class  Base:
        a=5
    
    class A(Base):
        m=6
        def __init__(self,x):
            print ('init')
            self.x=x  #此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次
        def __getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值
            print ('__getattr__',item)
            # self.__dict__[item]=None
        def __setattr__(self, key, value): #设置一个属性时,一定要执行,至于是否生效,则另当别论
            print ('__setattr__',key,value)
        def __delattr__(self, item): #此处在删除一个实例的属性进行的操作,只要实例能找到,都能够删除
            print ('__delattr__',item)
    
        def __getattribute__(self, item):  #此处是在字典属性之前进行拦截执行
            print ('__getattribute__',item)
            # raise  AttributeError(item)  # 此处若属性不存在,抛出异常,则直接进入getattr中机型处理
            return object.__getattribute__(self,item)  #此处表示若不存在,则直接去object中进行查找,并得到其访问的值
    a=A(20)
    
    print (a.b)

    结果如下
    python类之特殊属性和魔术方法

    注意:\getattribute\ 方法中为了避免在该方法中无限递归,实现了应该永久调用基类的同名方法以访问需要的任何属性,除非你明确知道\getattribute\方法用来做什么,否则不要使用它。

    6 总结:

    属性查找顺序
    实例调用----> \getattribute()----> instance.\dict---->instance.\class----> 继承的祖先类(知道object)的\dict\调用\getattr()

    11 描述器

    1 定义

    在python中,一个类实现了一下三种方式中的任何一种,就是描述器

    object.__get__(self,instance,owner)
    object.__set__(self,instance,value)
    object.__delete__(self,instance)

    如果仅实现了\get\,就是非数据描述器 non-data descriptor
    同时实现了\get\和\set\或者\get\和\delete\ 或者三个都实现,则称为数据描述符 data descriptor
    如果一个类的类属性设置为描述器,那么那被称为owner属主。

    2 基本实践

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
    
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=100  # 此处B类实例的属性为x=100
    
    print  (B.x.a1)  # 此处通过调用B类而调用B类的类属性x,进而调用A类的实例的a1方法.必须是先初始化,然后再进行相关的调用
    
    b=B()  # 此处调用从类开始,会执行A和B的init方法
    print  (b.x)   #此处调用的是实例B的x属性,其值是100,此处对x.a1没有属性,因为其被self.x=100覆盖了

    结果如下

    python类之特殊属性和魔术方法

    默认查找顺序: 类加载时,类变量需要先生成,而类B的x属性是类A的实例,因此需要先执行类A的初始化,进而执行B的初始化操作。

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            return (self,instance,owner)
    
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=100  # 此处B类实例的属性为x=100
    
    print (B.x)  # 此处x对应的a1的属性被拦截,上述返回为x实例,instance为B类实例的返回,owner为B类,及就是属性所属的类,self为A类的实例
    b=B()  # 对类B进行实例化
    print (b.x)  # 对类b的属性进行调用

    结果如下
    python类之特殊属性和魔术方法

    属性中的值:
    self : 类A对应的实例
    owner: 类B 
    instance 说明,及类B的实例
    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            return (self,instance,owner)
    
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=A()  # 此处B类实例的属性为调用A类的属性
    
    b=B()  # 对类B进行实例化
    print (b.x.a1)  # 对类b的属性进行调用,此处调用的是A类的属性,此处没有触动__get__魔术方法,进而说明__get__和实例无关

    python类之特殊属性和魔术方法

    结论: _get_()魔术方法只对调用的类有拦截作用,对类B下的实例无任何作用,此get是在调用子类的类属性时会执行此方法。

    通过属性描述器操作属主的状态

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            return   self # 此处返回self,则表示A的实例,A的实例当然可以调用a1方法
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=A()  # 此处B类实例的属性为调用A类的属性
    
    print (B.x.a1)# 此处因为返回的是self,及A的实例,因此此处可以调用A实例的a1方法,自然是成功的
    B.x.a1=30 #通过描述器来修改属主的状态
    print (B.x.a1)  # 打印状态

    结果如下
    python类之特殊属性和魔术方法

    此处通过返回为self的方式来达到调用类B的属性来调用类A的实例属性的目的。

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            print ('__get__',self,instance,owner)
            return   self # 此处返回self,则表示A的实例,A的实例当然可以调用a1方法
        # def  __set__(self, instance, value): #实例化B类时需要调用此方法,
        #     print ('__set__',self,instance,value)
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=100  # 此处B类实例的属性为调用A类的属性
    b=B()
    print (b.__dict__)  # 打印实例b对应的属性字典
    print ('+'*30)
    print (b.x.a1)  #此处默认的a1方法是不存在于b实例中,使用set方法将跳过b中定义的self.x方法

    结果如下
    python类之特殊属性和魔术方法

    使用_set_()方法如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            print ('__get__',self,instance,owner)
            return   self # 此处返回self,则表示A的实例,A的实例当然可以调用a1方法
        def  __set__(self, instance, value): #实例化B类时需要调用此方法,此处是将B的实例和A的实例一起送进了set方法中,value及就是B类定义的实例的属性对应的值
            print ('__set__',self,instance,value)
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=100  # 此处B类实例的属性为调用A类的属性
    b=B()
    print (b.__dict__)  # 打印实例b对应的属性字典
    print ('+'*30)
    print (b.x.a1)
    

    结果如下

    python类之特殊属性和魔术方法

    如下

    #!/usr/bin/poython3.6
    #conding:utf-8
    class A:
        def  __init__(self):
            print ('A,init')
            self.a1='a1'
        def __get__(self, instance, owner): #加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的None
            print ('__get__',self,instance,owner)
            return   self # 此处返回self,则表示A的实例,A的实例当然可以调用a1方法
        def  __set__(self, instance, value): #实例化B类时需要调用此方法,
            print ('__set__',self,instance,value)
            self.a1=value  # 若此处定义a1的返回值为value,及类B对应的实例属性x的值,则此处在访问时,其结果便是100 
    class B:
        x=A()  # 调用上述的类形成实例
        def __init__(self):
            print ('B,init')
            self.x=100  # 此处B类实例的属性为调用A类的属性
    b=B()
    print (b.__dict__)  # 打印实例b对应的属性字典
    print ('+'*30)
    print (b.x.a1)  # 此处最终访问__get__的原因是此处调用的是类的属性,而不是实例的属性,因此__get__会生效

    python类之特殊属性和魔术方法

    3 结论如下:

    当一个类的类属性是一个数据描述器时(及除了\get\方法外还有至少一种方法),对他的实例属性描述器的操作相当与对应的类的属性进行操作,及实例的字典优先级会降低,而类的字典的优先级会升高,实际的结果是当其成为数据属性描述器时,其对应的实例的字典中定义的实例属性将会消失


    属性查找顺序:
    实例的dict优先于非数据描述器(只有\get\方法),数据描述器优先于实例的\dict\
    及 数据描述器---> 实例的_dict_---> 非数据描述器---> 类的_dict_

    4 python中描述器的应用

    描述器在python中应用广泛
    python的方法包括(staticmethod)和classmethod() 都是通过非数据描述器来实现。因此实例可以重新定义和覆盖,这允许单个实例获取同一类的与其他实例不同的行为
    property() 函数实现为一个数据描述器。因此实例不能覆盖其行为
    init也是非数据描述器,基本上的魔术方法都是飞数据描述器

    5 练习

    1 实现 StaticMethod 装饰器,实现staticmethod的部分功能

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  StaticMethod:
        def __init__(self,fn):
            self.fn=fn
        def __get__(self, instance, owner): # 此方法是在调用类的属性时存在的,是在实例字典之后调用,类字典之前调用,及相当于类字典
            return self.fn
    
    class A:
        @StaticMethod  #  a=StaticMethod(a)
        def  a():
            print ('123456')
    
    x=A() #类的实例化
    x.a()  # 调用实例化的函数

    结果如下
    python类之特殊属性和魔术方法

    2 实现ClassMethod 装饰器,完成 classmethod装饰器的功能

    #!/usr/bin/poython3.6
    #conding:utf-8
    class  ClassMethod:
        def __init__(self,fn):
            print (fn)
            self.fn=fn
        def __get__(self, instance, owner):
            print (self,instance,owner)
            return  self.fn
    class A:
        @ClassMethod
        def  bar(cls):
            print (cls.__name__)
    
    f=A.bar
    print (f)
    f(A) # A.bar(A) #此处需要传入函数参数A进行处理

    结果如下
    python类之特殊属性和魔术方法

    #!/usr/bin/poython3.6
    #conding:utf-8
    from  functools import partial
    class  ClassMethod:
        def __init__(self,fn):
            print (fn)
            self.fn=fn
        def __get__(self, instance, owner):
            print (self,instance,owner)
            return  partial(self.fn,owner)  # 偏函数,使用此函数进行构建新函数的操作,此处相当于将self.fn替换成了owner,而owner为A类
    class A:
        @ClassMethod
        def  bar(cls):
            print (cls.__name__)
    
    A.bar()

    结果如下
    python类之特殊属性和魔术方法

    3 对实例的数据进行校验

    #!/usr/bin/poython3.6
    #conding:utf-8
    
    class Typed:
        def __init__(self, type):
            self.type = type
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):  # 此处的value是类Person定义的类属性中的类对应的值,及就是name和age对应的值
            if not isinstance(value, self.type):
                print ("{} is  not {} this is  type {}".format(value,self.type,type(value)))
                raise ValueError(value)
    class  Person:
        name=Typed(str)  #通过此处定义type的方式完成对类型的检测
        age=Typed(int)
        def __init__(self,name:str,age:int):
            self.name=name
            self.age=age
    Person('TOM',20)
    Person('jerry','30')
    

    结果如下
    python类之特殊属性和魔术方法

    使用函数获取参数签名的方式来进行相关的判断

    #!/usr/bin/poython3.6
    #conding:utf-8
    import  inspect
    class  Typed:
        def __init__(self,type):
            self.type=type
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            if  not  isinstance(value,self.type):
                print("{} is  not {} this is  type {}".format(value, self.type, type(value)))
                raise ValueError(value)
    class TypeAssert:
        def __init__(self, cls):
            self.cls = cls
    
        def __call__(self,name,age):
            param = inspect.signature(self.cls).parameters
            for _, v in param.items():  # 此处获取参数属性
                # print(v.name, v.annotation)
                if  v.annotation  !=v.empty:
                    setattr(self.cls,name,Typed(v.annotation))  #此处加入参数数据属性至类中
                    setattr(self.cls, age, Typed(v.annotation))
    @TypeAssert
    class  Person:
        def __init__(self,name:str,age:int):
            self.name=name
            self.age=age
    
    Person('tom',40)
    Person('tom1','50')
    Person(40,60)

    结果如下

    python类之特殊属性和魔术方法

    4 模拟Property的功能

    #!/usr/bin/poython3.6
    #conding:utf-8
    class Property:   # 数据描述器
        def __init__(self,fget,fset=None):  #此处的fget传递的是data
            self.fget=fget
            self.fset=fset
        def __get__(self, instance, owner):  # instance 是A类的实例
            if instance  is not None:
                return self.fget(instance) # 此处是将data写入
            return  self
        def __set__(self, instance, value):
            if  callable(self.fset):  #是否是可调用的
                self.fset(instance,value)
            else:
                raise  AttributeError('属性异常')
        def  setter(self,fn):  # 此处传递的是最底下的data参数,fn,
            self.fset=fn
            # return self.fset  # 此处若使用self.fset则表示返回的都是data.此处的data会做覆盖,导致最终的结果是相同的
            return self # 要想其是描述器,必须返回此值,否则返回是类A的属性
    class  A:
        def __init__(self,data):
            self.__data=data
    
        @Property  #data=Property(data)
        def data(self):
            return self.__data
        @data.setter  #data=data.setter(data),此处对应的data是上述PROPERTY 传入data生成实例的data,此处的方法是其方法
        def data(self,value):
            self.__data=value
            return  self.__data

    结果如下
    python类之特殊属性和魔术方法

关键字