python类之杏彩平台制作特殊属性和魔术方法

一 python特殊属性杏彩平台制作 Q> 1279829431【源码链接】dashengba.com

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-8classA:passclassB(A):passdefC():passprint(A.__name__,B.__name__,C.__name__,sep='\n')

3 module

类定义所在的模块名

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB:passprint(A.__module__,B.__module__)

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

4 class

对象或类所属的类

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

5 bases

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

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passclassC(B):passclassE:passclassD(E,C):passprint(A.__bases__,B.__bases__,C.__bases__,D.__bases__,sep='\n')

结果如下

6 DOC

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

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

结果如下 

7 mro

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

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passclassC(B):passclassE:passclassD(E,C):passprint(A.__mro__,B.__mro__,C.__mro__,D.__mro__,sep='\n')

结果如下

8 dict

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

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

结果如下 

9 dir

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

dir() 对于不同类型的对象具有不同的行为:

1 如果对象是模块对象,则列表包含模块的属性名

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

结果如下

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

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

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

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

结果如下 

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

#!/usr/local/bin/python3.6#coding:utf-8classA:a='1234'def__init__(self):self.x=10classB(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 实例属性之魔术方法

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-8classX:def__init__(self,name):self.name=nameself.x=10print ("init  instance")def__del__(self):        print ('delete {}'.format(self.name))a=X('a')del  a# 因为python自身的垃圾回收机制,而del删除实例的操作不确定何时执行,因此需要使用del进行清除处理

结果如下 

3 hash

1 简介

hash 中最基础的hash就是取模运算。

list 不能hash的原因 

list 源码: 其中hash=None,在调用None的时候自然是不能hash的

判断是否可hash

#!/usr/local/bin/python3.6#coding:utf-8fromcollectionsimportHashableclassX:def__init__(self,x):self.x=xdef__hash__(self):return1print(isinstance(X(1),Hashable))print(isinstance([],Hashable))

结果如下 

2 定义不可哈希类型

#!/usr/local/bin/python3.6#coding:utf-8fromcollectionsimportHashableclassX:def__init__(self):self.x=10__hash__=Noneprint(isinstance(X(),Hashable))#判断是否可hash,返回为bool类型print(hash(X()))

结果如下 

3 实例

实例如下

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

结果如下 

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

结果如下

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

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

结果如下 

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

结果如下

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):return1def__eq__(self, other):# 此处是判断是否相等的原因,及就是其值必须相等的情况下才能说是同一个,而不是hash相同就认为是同一个returnFalsea=X()lst=[a,a]print(lst)s=set(lst)print(len(s))forxins:print(hash(x))

结果如下

4 结论

set判断是否是同一个的步骤:

1 先进行内存地址的判断,及is判断,若内存地址相同,则肯定是同一个

2 若内存地址不同,再进行eq 判断,及就是==进行判断,若相同,则是同一个,若不同,则不是同一个,此处和hash没有任何关系

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

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

hash 相同不代表一样

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

结果如下

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

5 练习:

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

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self,x,y):self.x=xself.y=ydef__hash__(self):returnhash((self.x,self.y))# 此处返回是一个元组,是不可变数据类型,此处可直接进行hashdef__eq__(self, other):returnself.x==other.xandself.y == other.ya1=Point(3,4)a2=Point(3,4)print ( a1 is a2 )print (a1==a2)

结果如下

4 bool

1简介

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

2 实例

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

结果如下

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

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

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__bool__(self):# 同时存在,则以bool为准 returnFalsedef__len__(self):return1print (bool(Point()))

结果如下

这也就是为啥空的字典和空集合以及空列表为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-8classPoint:def__init__(self):self.x=3self.y=4def__repr__(self):returnstr([self.x,self.y])#此处的返回必须使用字符串进行包裹,否则会报错print (Point())

结果如下 

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

结果如下

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

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

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-8classA:def__init__(self,x):self.x=xdef__lt__(self, other):returnself.x  < other.xdef__eq__(self, other):returnself.x == other.xdef__ne__(self, other):returnself.x  != other.xdef__sub__(self, other):returnself.x - other.xprint (A(10)

结果如下

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

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

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__iadd__(self, other):# 此处定义的是+= 返回的是self=self+other# self.x += other.xprint ('__iadd__')returnA(self.x+other.x )#此处方法相同def__isub__(self, other):        print ('__isub__')self.x -= other.xreturnselfa1=A(10)a2=A(20)print ('*'*30)a1+=a2print (a1.x,a2.x)a1-=a2print (a1.x,a2.x)

functools.total_ordering 的应用

默认的

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

结果如下

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

结果如下

3 总结

运算符重载的应用场景

往往是面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式,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-8classItem:def__init__(self,name,*args):self.name=nameself.lst=list(args)def__len__(self):returnlen(self.lst)def__iter__(self):returniter(self.lst)# 此处返回是一个迭代器,必须是一个迭代器def__add__(self, other):# 此处使用+ 号返回一个列表self.lst.append(other)returnselfdef__getitem__(self, index):# 此处应用于列表时,表示为索引,此处应用于字典时,表示keyifindex  > len(self.lst):            print ('Key Error')else:returnself.lst[index]def__setitem__(self, index, value):# 此处表示修改属性列表中的值ifindex  > len(self.lst):            print ('Key Error')else:self.lst[index]=valuereturnself# def __missing__(self, key): # 此方法只能适用于字典的处理#    passdef__repr__(self):returnstr(self.lst)# 此处对其进行可视化处理a=Item('mysql',12,3,45,678,8909)print (len(a))# 此处调用了__iter__方法foriina: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)

结果如下

8 可调用对象

1 简介

在python中一切皆对象,函数也不例外 

可调用对象

方法

__call__类中出现该方法,实例就可以像函数一样调用,

可调用对象: 定义一个类,并实例化得到其实例,将实例像函数一样调用。调用是实例的,不是类的。

2 实例

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

结果如下

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

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

结果如下 

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

3 练习

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

方法1

#!/usr/local/bin/python3.6#coding:utf-8classA:def__call__(self,num):a,b=0,1foriinrange(num):print(b)            a,b=b,a+bA()(10)

结果如下

方法2

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self):self.lst=[1,1,2]def__call__(self,num):ifnum <3:returnself.lst[:num]else:foriinrange(num-3):self.lst.append(self.lst[-1]+self.lst[-2])returnself.lstprint (A()(10))

结果如下

添加功能如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):self.lst=[1,1,2]def__len__(self):returnlen(self.lst)def__call__(self,x):iflen(self.lst)  >x:returnself.lst[:x]foriinrange(2,x):self.lst.append(self.lst[i]+self.lst[i-1])returnself.lstdef__getitem__(self, item):ifitem <0:returnNoneiflen(self) >item:returnself.lst[item]def__iter__(self):returniter(self.lst)a=A()print (a(10))print (a[4])forxina:print (x)

结果如下

9 上下文管理

1 推导过程

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

推导过程

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

结果如下

提示需要添加 __enter__属性

添加如下

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

结果如下 

提示需要添加 __exit__属性

#!/usr/bin/poython3.6#conding:utf-8# 此处为默认的上下文管理# with  open('test')  as f:#    passclassA:def__enter__(self):passdef__exit__(self, exc_type, exc_val, exc_tb):passwithA()asf:pass

结果如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__enter__(self):print('__enter__')def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')withA()asf: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-8classA:def__init__(self):print('init instance')def__enter__(self):print('__enter__')return1def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')p=A()withpasf:# 此处的p是__enter__的返回值,是f的参数,若此处__enter__无return,则默认返回为None,无意义print(p==f)# 此处用于比较p和f的关系 print(pisf)print(p)print(f)

上述结论如下:

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

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

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

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

结果如下

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

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

结果如下

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

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

结果如下 

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

之前的计算时长方式

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimeimportsysdefwapper(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))returnretreturn_wapper@wapperdefadd(x,y):time.sleep(2)returnx+yadd(4,5)

结果如下 

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

#!/usr/bin/poython3.6#conding:utf-8import  datetimeimport  timeclassTimer:def__init__(self,fn):self.fn=fndef__enter__(self):self.start_time=datetime.datetime.now()returnself.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))return1defadd(x,y):returnx+yp=Timer(add)with  p  asf:# 此处调用的是__enter__的返回值,重命名为ftime.sleep(2)    print (f(4,5))

结果如下

5 类装饰器的应用

#!/usr/bin/poython3.6#conding:utf-8import  datetimeimport timefrom functools import  wrapsclassA:def__init__(self,fn):self.fn=fndef__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))returnret@A#add=A(add)defadd(x,y):    time.sleep(2)returnx+yprint  (add(10,20))

结果如下

6 进行属性覆盖如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):self.fn=fndef__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))returnret@A  #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此处打印出文档

结果如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):self.__doc__=fn.__doc__# 此处只能进行部分的属性覆盖操作self.__name__=fn.__name__        self.fn=fndef__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))returnret@A  #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此处打印出文档

结果如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):wraps(fn)(self)# 调用此方法完成属性的覆盖操作,此处第一个是原函数,后面是现在的函数self.fn=fndef__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))returnret@A  #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此处打印出文档

结果如下

7 contextlib.contextmanager

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

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

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')yieldprint('exit')withfoo()asf:pass

结果如下

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

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')yield[1,2,34,56,5867,856,867,]# 此处相当分界线,用于分割上面和下面的执行print('12344exit')withfoo()asf:try:raiseExceptionfinally:print('exit')print(f)

结果如下

修改异常捕获如下

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

结果如下 

总结: 如果业务逻辑简单可以使用函数加装饰器的方式,如果业务复杂,用类的方式加__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-8classA:x=10def__init__(self):self.x=5setattr(A,'y',20)# 动态添加类属性位y=20print(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__)# 打印属性信息列表,此处查看当实例属性信息增加后,类属性信息是否增加ifhasattr(a,'z'):print("{} 函数存在此属性 {}".format('a','y'))

结果如下 

进阶

#!/usr/bin/poython3.6#conding:utf-8classA:x=10def__init__(self,y):self.x=5self.y=ya=A(20)setattr(A,'printf',lambdaself:1)#此处增加一个类的属性,并通过参数调用setattr(a,'myclass',lambda:10)# 此处增加一个实例属性print(a.printf())# 打印此类的属性结果如下print(getattr(a,'printf')())# 因为此处是函数,因此需要通过()来进行调用print(getattr(a,'myclass')())ifnothasattr(A,'sub'):# 此处添加一个类的函数属性,实现函数之前的差运算setattr(A,'sub',lambdaself,other : A(self.y- other.y) )ifnothasattr(A,'add'):# 此处添加一个类的属性,实现函数之间的和的计算setattr(A,'add',lambdaself,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))

结果如下

注意:

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

4 实例应用

实现分发器

简单雏形

#!/usr/bin/poython3.6#conding:utf-8classDispatcher:defcmd1(self):# 此处在内部定义一个方法print ('cmd10')defrun(self):# 此处用于执行whileTrue:cmd=input("plase  input str:")#退出程序命令ifcmd.strip() =='q'orcmd.strip() =='quit':returngetattr(self,cmd.strip(),self.__defaltfun)()# 此处用于获取该方法,若不存在,则执行默认程序def__defaltfun(self):        print ('default')a=Dispatcher()a.run()

结果如下 

#!/usr/bin/poython3.6#conding:utf-8classDispatcher:defcmd1(self):# 此处在内部定义一个方法print ('cmd10')defreg(self,cmd,fn):ifisinstance(cmd,str):            setattr(self.__class__,cmd.strip(),fn)# 此处使用的是类,若是实例化,则需要进行下面将self传入进去的方式进行调用else:print ('TypeError')defrun(self):whileTrue:cmd = input("plase  input str:")ifcmd.strip()  =='q'orcmd.strip() =='quit':returngetattr(self,cmd.strip(),self.defaultfun)()defdefaultfun(self):        print ('default')a=Dispatcher()a.reg('cmd2',lambdaself:print(2)) a.reg('cmd3',lambdaself:print(3))a.run()

结果如下

5 反射相关魔术方法

魔术方法意义

_getattr_()当通过搜索实例,实例的类以及祖先类查不到的属性,就会调用此方法

_setattr_()通过访问实例属性,进行增加,修改都要调用它

_delattr_()当通过实例删除属性的时候调用此方法

_getattribute_实例所有的属性调用都是从政方法开始

实例如下:

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')        self.x=x#此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次def__getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值print('__getattr__',item)        self.__dict__[item]=Nonedef__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#添加一个不存在的属性dela.a# 删除一个实例的属性print(a.__dict__)

结果如下

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')        self.x=x#此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次def__getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值print('__getattr__',item)        self.__dict__[item]=Nonedef__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)

结果如下 

实例的所有属性的访问,第一个都会调用__getattribute__方法,其阻止了属性查找,该方法应该返回(计算后)值或者抛出一个attributeError 异常

他的return 值将作为属性查找的结果,如果抛出attributeError 异常,则会直接调用__getattr__方法,因为表示属性没有找到

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

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')        self.x=x#此处定义了属性,所以下面的__setattr__被执行了一次,初始化先执行,之后__setattr__这个属性再执行一次def__getattr__(self, item):#针对上述无法查找到的属性,则执行此属性,可配置其值为None来弥补此属性值print('__getattr__',item)# self.__dict__[item]=Nonedef__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中机型处理returnobject.__getattribute__(self,item)#此处表示若不存在,则直接去object中进行查找,并得到其访问的值a=A(20)print(a.b)

结果如下 

注意:\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-8classA:def__init__(self):print('A,init')        self.a1='a1'classB:x=A()# 调用上述的类形成实例def__init__(self):print('B,init')        self.x=100# 此处B类实例的属性为x=100print(B.x.a1)# 此处通过调用B类而调用B类的类属性x,进而调用A类的实例的a1方法.必须是先初始化,然后再进行相关的调用b=B()# 此处调用从类开始,会执行A和B的init方法print(b.x)#此处调用的是实例B的x属性,其值是100,此处对x.a1没有属性,因为其被self.x=100覆盖了

结果如下

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

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

结果如下

属性中的值:self : 类A对应的实例owner: 类B instance 说明,及类B的实例

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

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

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

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')        self.a1='a1'def__get__(self, instance, owner):#加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的Nonereturnself# 此处返回self,则表示A的实例,A的实例当然可以调用a1方法classB: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)# 打印状态

结果如下

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

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')        self.a1='a1'def__get__(self, instance, owner):#加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的Noneprint('__get__',self,instance,owner)returnself# 此处返回self,则表示A的实例,A的实例当然可以调用a1方法# def  __set__(self, instance, value): #实例化B类时需要调用此方法,#    print ('__set__',self,instance,value)classB: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方法

结果如下

使用_set_()方法如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')        self.a1='a1'def__get__(self, instance, owner):#加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的Noneprint('__get__',self,instance,owner)returnself# 此处返回self,则表示A的实例,A的实例当然可以调用a1方法def__set__(self, instance, value):#实例化B类时需要调用此方法,此处是将B的实例和A的实例一起送进了set方法中,value及就是B类定义的实例的属性对应的值print('__set__',self,instance,value)classB: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)

结果如下

如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')        self.a1='a1'def__get__(self, instance, owner):#加入此方法,行为被拦截,执行了init后执行了此方法,返回为None,因此后面调用的Noneprint('__get__',self,instance,owner)returnself# 此处返回self,则表示A的实例,A的实例当然可以调用a1方法def__set__(self, instance, value):#实例化B类时需要调用此方法,print('__set__',self,instance,value)        self.a1=value# 若此处定义a1的返回值为value,及类B对应的实例属性x的值,则此处在访问时,其结果便是100 classB: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__会生效

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-8classStaticMethod:def__init__(self,fn):self.fn=fndef__get__(self, instance, owner):# 此方法是在调用类的属性时存在的,是在实例字典之后调用,类字典之前调用,及相当于类字典returnself.fnclassA:@StaticMethod#  a=StaticMethod(a)defa():        print ('123456')x=A()#类的实例化x.a()# 调用实例化的函数

结果如下 

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

#!/usr/bin/poython3.6#conding:utf-8classClassMethod:def__init__(self,fn):print(fn)        self.fn=fndef__get__(self, instance, owner):print(self,instance,owner)returnself.fnclassA:    @ClassMethoddefbar(cls):print(cls.__name__)f=A.barprint(f)f(A)# A.bar(A) #此处需要传入函数参数A进行处理

结果如下

#!/usr/bin/poython3.6#conding:utf-8fromfunctoolsimportpartialclassClassMethod:def__init__(self,fn):print(fn)        self.fn=fndef__get__(self, instance, owner):print(self,instance,owner)returnpartial(self.fn,owner)# 偏函数,使用此函数进行构建新函数的操作,此处相当于将self.fn替换成了owner,而owner为A类classA:    @ClassMethoddefbar(cls):print(cls.__name__)A.bar()

结果如下 

3 对实例的数据进行校验

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

结果如下 

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

#!/usr/bin/poython3.6#conding:utf-8import  inspectclassTyped:def__init__(self,type):self.type=typedef__get__(self, instance, owner):        passdef__set__(self, instance, value):ifnotisinstance(value,self.type):            print("{} is  not {} this is  type {}".format(value,self.type, type(value)))            raise ValueError(value)classTypeAssert:def__init__(self, cls):self.cls = clsdef__call__(self,name,age):        param = inspect.signature(self.cls).parametersfor_, vinparam.items():# 此处获取参数属性# print(v.name, v.annotation)ifv.annotation  !=v.empty:setattr(self.cls,name,Typed(v.annotation))#此处加入参数数据属性至类中setattr(self.cls, age, Typed(v.annotation))@TypeAssertclassPerson:def__init__(self,name:str,age:int):self.name=nameself.age=agePerson('tom',40)Person('tom1','50')Person(40,60)

结果如下

4 模拟Property的功能

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

结果如下

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容