类 进阶
isinstance 和 issubclass
通过例子看:
isinstance
class Foo(object): def __init__(self,name,sex): self.name=name self.sex=sex def printer(self): print('kkkkkkkkkkkk') f1=Foo('abc','male') print(isinstance(f1,Foo))#f1是不是Foo的对象
issubclass
class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar,Foo)) # Bar 是不是Foo 的子类
#反射 
类有属性,对象有属性,只要是通过字符获取的都是反射,在python面向对象中的反射:通过字符的形式操作对象相关的属性python的一切事物都是对象
>>> class Foo(object): def __init__(self,name,sex): self.name=name self.sex=sex def printer(self): print('kkkkk') >>> f1=Foo >>> hasattr(f1,'name') #判断 f1 对象中有没有‘name’这个方法或属性 True
getattr
>>> getattr(f1,'sex') #获取 f1 对象中的‘sex’ 属性或方法 'male' >>> getattr(f1,'abcd') Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> getattr(f1,'abcd') AttributeError: 'Foo' object has no attribute 'abcd'
setattr
>>> setattr(f1,'name','david') #设置 f1 对象中 'name' 方法或属性的值,有则更改为‘david’ ,没有则添加至__dict__字典中 >>> print(f1.name) david >>> setattr(f1,'age',32) >>> print(f1.age) 32 >>> print(f1.__dict__) # 查看对象的数据属性存放
{'age': 32, 'sex': 'male', 'name': 'david'}
delattr
>>> delattr(f1,'age') #删除对象的属性值 >>> print(f1.__dict__) {'sex': 'male', 'name': 'david'} >>> delattr(f1,'sex') >>> print(f1.__dict__) {'name': 'david'}
反射当前模块属性:
反射当前模块的属性: 先定义一个类 >>> class test: name='tony' sex='man' def __init__(self): print('hello') def priter(self): print('this printer %s'%self.sex) print(self.name) >>> this_module=sys.mod >>> this_module=sys.modules[__name__] #这个 __name__ 说的是这个模块唯一标识导入系统中的模块 >>> print(this_module) <module '__main__' (built-in)> >>> hasattr(test,'__init__') #相当于 '__init__' in test.__dict__ ,hasattr 做的就是这个事情 True >>> p=test() hello >>> setattr <built-in function setattr> #修改其属性 >>> setattr(p,'name','liang') >>> print(p.name) liang
反射的用途:
#两个程序员写一个FTP,Tony,Hua,Tony写程序有些需要用到Hua 所写的类, ,但是Hua 因为有事外出,没有完成要写的类,Tony想到了反射可以继续完成自己的代码,等Hua回来再继续完成类的定义,并且去实现Tony想要的功能。
#反射的好处就是,可以事先定义好接口,接口只有在完成后才会真正执行,这就实现了‘即插即用’,这就是一种‘后期绑定’,就是可以事先把主要的逻辑写好(只定义接口),然后再去实现接口的功能
两个文件,一个是ftpserver, 一个是ftpclient ,tony负责Client端的编写,Hua负责Server端的编写
tony没有写完
class FtpCli: def __init__(self,addr): print('正在连接服务器 [%s]' % addr) self.addr=addr
Hua可以判断tony客户端写了没有,这就用到了反射,如果写了就执行那一部分的代码,如果没有就继续写其它模块:
import FtpClient f1=FtpClient.FtpCli('192.168.1.1') # print(f1) if hasattr(f1,'get'): #判断 tony的代码中有没有写get功能。 有则获取运行 func_get=getattr(f1,'get') func_get() else: #没有则继续运行其它的代码 print('other runing')
实际应用:二次加工(包装)
#基于继承的原理来定制自己的数据类型
利用反射定制自己的数据类型,如列表、字典,我们可以定制‘值’ 必须是字符串,如:数据库的ID号,必须是int型。那这个时个可以用到“反射”
>>> class List(list): #定义列表类,继承 python 的list def append(self,value): #重写它的append方法 if not isinstance(value,int): #判断输入的值是不是 int类型 raise TypeError('Must be int') #不是int类型,则举出 ‘必须是int’ 的错误 else: super().append(value) #如果是整型,则使用超类(list)的append方法添加值到列表。 自定义的类是没有append 这些方法的 >>> l1=List([1,2,2,3,4,5,]) #实例化List 类 [1, 2, 2, 3, 4, 5] >>> l1.append(0) [1, 2, 2, 3, 4, 5, 0] >>> l1.append('3') Traceback (most recent call last): File "<pyshell#106>", line 1, in <module> l1.append('3') File "<pyshell#100>", line 4, in append raise TypeError('Must be int') TypeError: Must be int >>>
上面的都理解没有问题:
>>> l1.insert(0,'fda') 这又是怎么回事呢? 这里的insert 不是我们自己定义的List 中的 insert ,因为没有经过自己定制,那l1就自己执行超类的 insert 方法了。 除非,自己再定制一个 insert ,再进行一次判断,不能insert 字符串。
>>> l1
['fda', 1, 2, 2, 3, 4, 5, 0]
授权:
不能用继承,实现字典的功能: 字典的值只能是str类型:
#不使用继承 完成字典的数据定制,字典的值必须是int , class Dict: def __init__(self,key,value): # 设置值:字典只有key和value if not isinstance(value,int): # 设置 字典的值只能是int 类型 raise TypeError('must be int') # 不是int 类型则报出错误 else: self.key=key #如果是字典类型 则把key的值传进来 self.c = {key: value} # 初始一个字典,这样就可以限制 健和值了 self.value = value #值 赋给self.value def fu_oprate(self,key,value): #字典操作 ,也是有键和值 if not isinstance(value,int): # 条件判断 传进来的值只能是int ,不是整型则报出错误 raise TypeError('must be int') # 报出错误 if not isinstance(key,str): #条件判断‘键’ ,只能是字符串类型 raise TypeError('must be str') #不是字符串类型,报错 else: self.c[key]=value #如果上述条件都成立,那就将 '值' 赋给‘键’ return self.c #返回整个字典 di1=Dict('name',‘tony’) #实例化 Dict print(di1.fu_oprate('age','fifteen')) # 操作字典,这里就已经限制了‘值’的数据类型,这就已经完成自定义数据类型的定制
__getattribute__
回顾__getattr___
>>> class Foo: def __init__(self,x): self.x=x #print(f1,x) 执行的是这里,(过程是先在f1里面找, f1里没有就找类的f1) def __getattr__(self,item): print('exe is me') #f1.xxxxxxx 执行的是这里 , >>> f1=Foo(10) #实例化Foo(10) 并传入值 >>> print(f1.x) 10 >>> print(f1.__dict__) # 自己的属性值存放的名称空间。 {'x': 10} >>> f1.xxxxxxx #不存在的属性访问,触发__getattr__ exe is me
__getattribute__
再看一个例子:
>>> class Foo: def __init__(self,x): self.x=x def __getattr__(self,item): print('执行的是我') def __getattribute__(self,item): print('不管是否存在,我都会执行') raise AttributeError('lalalalalalalalalalala') >>> f1=Foo(100) >>> f1.x 不管是否存在,我都会执行 执行的是我 >>> f1.xxxxxxxxxx 不管是否存在,我都会执行 执行的是我 >>> print(f1.x) 不管是否存在,我都会执行 执行的是我 None #结论: 当__getattribute__与 __getattr__同时存在,只会执行__getattribute__,除非__getattribute__在执行过程中抛出异常AttributeError
描述符(__get__ , __set__ , __delete__)
1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(), __set__() ,__delete__()中的一个,这也被称为描述符协议,
__get__():调用 一个属性时会触发
__set__():为一个属性赋值时会触发
__delete__(): 采用del 删除属性时会触发
定义一个描述符:
class Foo: def __get__(self,instance,owner): pass def __set__(self,instance,value): pass def __delete__(self,instance): pass
描述符是干什么的,描述符的作用是用来代理另外一个类的属性(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性 调用/赋值/删除/ 并不会触发这三个方法 class descrip: def __get__(self,instance,owner): print('trigger get') def __set__(self,instance,value): print('trigger set') def __delete__(self,instance,value): print('trigger delete ') >>> F1=descrip() >>> F1.name='tony' >>> F1.name 'tony' >>> del F1.name #以上都没有触发其中的方法,那什么时候才会触发 这三个方法?
描述符应用于何时?何地?
>>> class Str: def __get__(self,instance,owner): print('Str call') def __set__(self,instance,value): print('Str set.....') def __delete__(self,instance): print('Str delete') >>> class Int: def __get__(self,instance,owner): print('Int call') def __set__(self,instance,value): print('Ins set....') def __delete__(self,instance): print('Int delete....') >>> class human: name=Str() age=Int() def __init__(self,name,age): self,name=name self.age=age #何时运行 >>> h1=human('tony',23) #实例化操作触发了两个类 set 的运行 Str set..... Ins set.... 描述符Str 的使用: >>> h1.name Str call >>> h1.name='liang' Str set..... >>> del h1.name Str delete #描述符 Int 的使用: >>> h1.age Int call >>> h1.age=20 Ins set.... >>> del h1.age Int delete.... #查看一下类和对象的名称空间 >>> h1=human('tony',23) Str set..... Ins set.... >>> print(human.__dict__) {'__module__': '__main__', 'name': <__main__.Str object at 0x0000000003684160>, '__weakref__': <attribute '__weakref__' of 'human' objects>, '__dict__': <attribute '__dict__' of 'human' objects>, '__init__': <function human.__init__ at 0x000000000367DAE8>, '__doc__': None, 'age': <__main__.Int object at 0x0000000003684320>} >>> print(h1.__dict__) #对象中的dict是空的 ,说明都是值都是存放在类里的。 {} >>> ##################################################################################################### >>> print(type(h1)) <class '__main__.human'> >>> print(type(human)) <class 'type'> >>> print(type(h1).__dict__ == human.__dict__) #对象 h1明明是空的 ,为何等于human.__dict__是True ,因为human 是由type 产生的。而h1 由human实例化得来 True >>>
描述符分两种:
一:数据描述符:至少实现了__get__() 和 __set__()
>>> class Test: def __set__(self,instance,value): print('set') def __get__(self,instance,owner): print('get')
二:非数据描述符:没有实现__set__()
>>> class Test: def __get__(self,instance,owner): print('get')
#####
注意:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类(python3 都是新式类)
二、必须把描述符定义成这个类的类属性,不能定义到构造函数中
三、要严格遵循该优先级,优先级由高到低 分别是
1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、找不到属性触发__getattr__()
类属性 高于 数据描述符
>>> class Str: def __get__(self,instance,owner): print('Str call') def __set__(self,instance,value): print('Str set....') def __delete__(sel,instance): print('Str delete.....') >>> class Human: name=Str() def __init__(self,name,age): #name被Str类代理,age被Int 类代理 self.name=name self.age=age #在类中定义描述符,它就是一个类属性,存在于类的属性字典中,而不是实例属性字典 #那既然描述符被定义成了一个类属性,直接通过类名也可以调用。 >>> Human.name #,调用类属性name,本质就是在调用描述符Str,触发了__get__() Str call >>> Human.name='tony' #赋值为什么没有触发 __get__() >>> del Human.name # 删除也没有触发。 >>> 这是因为优先级的原因。类属性赋值 高于描述符的 级别。所以没有触发。
数据描述符 高于 实例属性
>>> class Str: def __get__(self,instance,owner): print('Str call') def __set__(self,instance,value): print('Str set....') def __delete__(sel,instance): print('Str delete.....') >>> class Human: name=Str() def __init__(self,name,age): self.name=name self.age=age >>> Human.name Str call >>> Human.name='tony' >>> del Human.name >>> >>> h2=human('tonyyy',20) Str set..... Ins set.... ##如果描述符是一个数据描述符(即有__get__又有__set__),那么h2.name的调用与赋值都是触发描述符的操作,与h2本身无关了,相当于覆盖了实例的属性 >>> h2.name='tonyyyyyyyyyyyyyyyyyyyyyyy' Str set..... >>> h2.name Str call >>> print(h2.__dict__) #实例的属性字典中没有name ,因为name是一个数据描述符,优先级高于实例属性,查看\赋值\删除\ 都是跟描述符有关,与实例无关 {} >>> del h2.age Int delete.... >>>
实例属性 高于 非数据描述符
>>> class test: def func(self): print('I\'m back') >>> t1=test() >>> t1.func() #调用类的方法,也可以说是调用非数据描述符 #函数是一个非数据描述符对象(一切皆对象) I'm back >>> >>> print(dir(test.func)) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] >>> print(hasattr(test.func,'__set__')) False >>> print(hasattr(test.func,'__get__')) True >>> print(hasattr(test.func,'__delete__')) False 描述符在应用的时候都是实例成一个类属性,函数就是一个由非描述符类实例化得到的对象,字符串也是。 >>> t1.func='this instance attr' >>> print(t1.func) this instance attr >>> del t1.func #删除掉了非数据 >>> t1.func() I'm back
非数据描述符 高于 找不到
>>> class test: def func(self): print('I\'m back agin') def __getattr__(self,item): print('not found ,of couse find me ',item) >>> t1=test() >>> t1.xxxxxxxxxxx not found ,of couse find me xxxxxxxxxxx #找不到,不会报错,而是先找实例的__getattr__属性 >>> t1.func <bound method test.func of <__main__.test object at 0x00000000036849B0>> >>> t1.func() # I'm back agin >>>
__setitem__ __getitem__ __delitem__
>>> class test: def __init__(self,name): self.name=name def __getitem__(self,item): print(self.__dict__[item]) def __setitem__(self,key,value): self.__dict__[key]=value def __delitem__(self,key): print('del obj[key] i\'m running') self.__dict__.pop(key) def __delattr__(self,item): print('del obj.key , i\'m running') self.__dict__.pop(item) >>> t1=test('liang') >>> t1['age']=18 >>> t1['age2']=23 >>> del t1.age1 del obj.key , i'm running #这里触发了__delattr__(self,item): Traceback (most recent call last): File "<pyshell#249>", line 1, in <module> del t1.age1 File "<pyshell#245>", line 13, in __delattr__ self.__dict__.pop(item) KeyError: 'age1' >>> del t1['age'] del obj[key] i'm running #这里触发 __delitem__(self,key): >>> t1['name']='tony' >>> print(t1.__dict__) {'name': 'tony', 'age2': 23} >>>
___slots__
直接看效果,__slots__ 可以节省内存, 如下,指定的属性可以使用,没有指定则报出没有属性 目的可以节省内存,如果是字典的话,来一个对象都会创建一个名称空间。 >>> class test: __slots__='x' >>> t1=test() >>> t1.x=1 >>> t1.y=2 Traceback (most recent call last): File "<pyshell#271>", line 1, in <module> t1.y=2 AttributeError: 'test' object has no attribute 'y' 同时对象也没有__dict__ >>> print(f1.__slots__) Traceback (most recent call last): File "<pyshell#272>", line 1, in <module> print(f1.__slots__) AttributeError: 'Foo' object has no attribute '__slots__'
再来一个例子:
>>> class Bar: __slots__=['x','y'] >>> n=Bar() >>> n.x,n.y=1,2 >>> n.z Traceback (most recent call last): File "<pyshell#278>", line 1, in <module> n.z AttributeError: 'Bar' object has no attribute 'z' >>> n.x 1 >>> n.y 2 >>>
再看:
>>> class test: __slots__=['name','age'] >>> t1=test() >>> t1.name Traceback (most recent call last): File "<pyshell#285>", line 1, in <module> t1.name AttributeError: name >>> t1.name='tony' >>> t1.age=29 >>> print(t1.__slots__) ['name', 'age'] >>> t2=test() >>> t2.name='liang' >>> t2.age=22 >>> print(t2.__dict__) #对象中已经没有属性字典__dict__,统一归 __slots__管,节省内存 Traceback (most recent call last): File "<pyshell#293>", line 1, in <module> print(t2.__dict__) AttributeError: 'test' object has no attribute '__dict__' >>> print(test.__dict__) {'__module__': '__main__', 'name': <member 'name' of 'test' objects>, '__slots__': ['name', 'age'], '__doc__': None, 'age': <member 'age' of 'test' objects>} >>>
__next__ 和__iter__实现迭代器协议:
如果有 __next__ 方法在的话,这个方法会自动的next到下一个,直到碰到StopIteration,但是 for 可以 自处理这个StopIteration
>>> class test: #实现迭代器 def __init__(self,x): self.x=x def __iter__(self): return self def __next__(self): n=self.x self.x+=1 return self.x >>> t1=test(0) >>> for i in t1: print(i) KeyboardInterrupt >>> >>> for i in t1: print(i) 1 2 3 . . . . . . . . . . . . .. . . .
自定义一个range
range 有一个开始值和结尾值。
>>> class test: def __init__(self,start,stop): self.num=start self.stop=stop def __iter__(self): return self def __next__(self): if self.num>=self.stop: raise StopIteration n=self.num self.num+=1 return n >>> t1=test(0,5) >>> from collections import Iterable,Iterator >>> print(isinstance(t1,Iterator)) True >>> for i in test(0,5): print(i) 0 1 2 3 4
Fibonacci 数列
>>> class Fib: def __init__(self): self.a=0 self.b=1 def __iter__(self): return self def __next__(self): self.a,self.b=self.b,self.a+self.b return self.a >>> f1=Fib() >>> print(next(f1)) 1 >>> print(next(f1)) 1 >>> print(next(f1)) 2 >>> print(next(f1)) 3 >>> print(next(f1)) 5 >>> print(next(f1)) 8 >>> for i in f1: if i>100: break print(i) 13 21 34 55 89
__doc__ 在编写代码强制写上‘描述信息’ ,养成良好的编程习惯
>>> class test: '''I"m description''' pass >>> print(test.__doc__) #通过类名.点 __doc__ 查看描述信息 I"m description >>>
__doc__ 无法被子类继承
>>> class test: 'i\'m description' pass >>> class Bar(test): pass >>> print(Bar.__doc) #无法继承该属性 Traceback (most recent call last): File "<pyshell#9>", line 1, in <module> print(Bar.__doc) AttributeError: type object 'Bar' has no attribute '__doc' >>>
__module__ 和 __class__
完工~~~~~~