Day 21 python :面向对象 类的相关内置函数 /单例模式 /描述符
1、isinstance(obj,cls) 检查obj是否是类cls的对象;
备注:用isinstance 的时候,产生实例后,会显示实例既是父类的实例,也是子类的实例
class Mom: gender = "woman" def __init__(self,name,weight): self.name = name self.weight = weight def clean_up1(self): return "% 正在擦桌子" % self.name m1 = Mom("xfd",110) print(isinstance(m1,Mom))#True
2、issubclass(sub,super)检查sub类是否是super类的派生类
class Father: pass class Mom(Father): gender = "woman" def __init__(self,name,weight): self.name = name self.weight = weight def clean_up1(self): return "% 正在擦桌子" % self.name m1 = Mom("xfd",110) print(issubclass(Father,Mom))#False 判断Mom是不是Father的父类
3、 __getattribute__
#_getattrbute__方法对类实例的每个属性访问都有效。 #__getattr__方法对类及其实例未定义的属性有效 #当同时定义__getattribute__和__getattr__时,__getattr__方法不会再被调用 #如果getattribute 挂掉后,getattr会运行 class Mom(Father): gender = "woman" def __init__(self,name,weight): self.name = name self.weight = weight def __getattr__(self, item): print("执行getattr") def __getattribute__(self, item): print("执行getattribute") m1 = Mom("xfd",110) print(m1.name)#执行getattribute print(m1.ads)#执行getattribute
二、__setitme__/__getitme__/__delitme__
class Mom: gender = "woman" def __init__(self,name,weight): self.name = name self.weight = weight def __setitem__(self, key, value): print("执行setitem") self.__dict__[key] = value def __getitem__(self, item): print("执行getitem") return self.__dict__[item] def __delitem__(self, key): print("delitme") #self.__dict__.pop(key) m1 = Mom("xfd",110) m1['gender']="woman" #执行setitem print(m1.__dict__) del m1['gender'] print(m1.__dict__) #中括号的这种操作,触发item ,点的方式触发attr的方法
三、__str__、__repr__,__format__
前言:
lis = list('xianfangdi') print(lis) #输出:['x', 'i', 'a', 'n', 'f', 'a', 'n', 'g', 'd', 'i'] class test: pass t1 = test() print(t1) #输出<__main__.test object at 0x000001B152E32828> f = open("test.txt","w") print(f) #输出:<_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'> #为什么同样都是产生实例,为什么结果不同呢?
1、__str__
class Test: def __init__(self,name): self.name = name def __str__(self): # return "str方法" return "名字是%s" %self.name t1 = Test('xfd') print(t1)#名字是xfd # 输出:str方法 #这个可以控制打印实例的显示类型 #打印操作实际是调用系统str的操作
2、__repr__
class Test: def __init__(self,name): self.name = name def __repr__(self): # return "repr方法" return "名字是%s" %self.name t1 = Test('xfd') t1#名字是xfd 是在解释器里面触发 print(t1) # 输出:repr方法 #repr(t1) 实际上调用的是t1.__repr__ #是在解释器里面触发 #如果str和repr共存,会找str,如果没有str,就把repr当做替代品显示
#str 和 repr只能return 字符串
3、在类里面自定制__format__方法
# #一般我们用format传的是字符串 # x='{0},{1},{2}'.format('dog',"cat","pag") # print(x)#输出dog,cat,pag # #我们用类来来呢? # class Date: # def __init__(self,year,mon,day): # self.year=year # self.mon=mon # self.day=day # d1=Date(2016,12,26) # # x='{0.year}{0.mon}{0.day}'.format(d1) # #d1.year d1.mon de.dad == 2016.12.26 # y='{0.year}:{0.mon}:{0.day}'.format(d1) # z='{0.mon}-{0.day}-{0.year}'.format(d1) # print(z)#12-26-2016 format_dic={ 'ymd':'{0.year}{0.mon}{0.day}', 'm-d-y':'{0.mon}-{0.day}-{0.year}', 'y:m:d':'{0.year}:{0.mon}:{0.day}' }#定义时间格式 class Date: def __init__(self,year,mon,day): self.year=year#2016 self.mon=mon#12 self.day=day#26 def __format__(self, format_spec): #format_spec == 字典的key if not format_spec or format_spec not in format_dic: format_spec='ymd' fm=format_dic[format_spec] #fm ={0.year}{0.mon}{0.day} return fm.format(self) #{0.year}{0.mon}{0.day}.format(d1) d1=Date(2016,12,26) # format(d1) #d1.__format__() # print(format(d1)) print(format(d1,'ymd')) print(format(d1,'y:m:d')) print(format(d1,'m-d-y')) print(format(d1,'m-d:y')) #当执行format 方法的时候有就是在触发 类的__format__
四、__slots__
解释:
1、slots类变量,变量的值可以可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2、用用点。来访问数据属性本质就是再访问类或者对象的__dic__属性字典(类的字典是共享的,而每个实例是独立的)
3、为何使用__slots__后,__slots__j就会为实例使用一种更加紧凑的内部表示,实例通过一个很小的固定大小的数组来构建,而不是每个实例定义一个
字典,这跟元祖列表类似,再__slots__中列出的属性名再内部映射到这个数组的指定小标上,使用__slots__一个不好的地方我们不能给实例添加新的属性了,只能使用__slots__中定义的属性名
4、注意:__slots__的很多特性都依赖于普通的基于字典的实现,另外定义之后,类不在支持一些普通特性了,比如多继承,大多数情况下,你应该只在哪些经常被使用到的用作数据结构的类上定义__stots__ 比如程序中需要创建摸个类的百万个实例对象。
关于__slots__的一个常见误区是他可以作为一个封装公祖来放置用户给实例增加新的属性。尽管使用__slots__可以达到这样的摸底,但是这个斌不是他的初衷。更多的是用来做一个内存优化的工具。
class Foo: __slots__=['name','age'] #类似于{'name':None,'age':None} # __slots__='name' #{'name':None} f1=Foo() # f1.name='xfd' # print(f1.name) # f1.age=18 # 报错,设置属性时,触发setattr,实际上是找f1.__dict__ # 现在没有__dict__肯定没有了,取而代之 是f1.__slots__(实例没有去找类了) print(Foo.__slots__) print(f1.__slots__) #都输出 ['name', 'age'] f1.name='XFD' f1.age=17 print(f1.name)#print(f1.age)# 可以获取值 f2=Foo() print(f2.__slots__)#['name', 'age'] #F2 也不能创建,只能用name ,age
五、__doc__ 描述信息
class Test: '描述信息' pass print(Test.__doc__)#描述信息 #该属性无法继承
六、__module__ ,__class__
from bin.E import G g1 = G() print(g1.name) print(g1.__module__)#bin.E #g1,来自 bin下的E模块 print(g1.__class__) #g1是由bin.E.G产生的
七、析构方法 __del__ / __call__
注:此方法一般无需定义,python3是一个高级语言,程序员再使用时,无需关心内存的分配和释放,因为此工作都是交给解释器来执行,所有,析构函数的调用是由解释器在进行垃圾回收时自动触发执行
1、__call__ 对象后面加()触发执行
备注:构造方法的执行是由创建对象触发,即: 对象= 类名();而队友call方法的执行是由对象后加()触发的。
例:类(),实例()
class System: # def __init__(self,name): # self.name = name def __call__(self, *args, **kwargs): print("实例执行") f1 = System() f1()#实例执行 System()#触发call方法 然后产生实例 #一个对象加(),可以去执行,执行的是类下面的call方法
八、__next__,__iter__实现迭代器协议
class Foo: def __init__(self,n): self.n=n def __iter__(self): return self def __next__(self): if self.n == 13: raise StopIteration('终止了') #抛出异常 self.n+=1 return self.n #可迭代协议,必须包含iter方法 #有next 就是迭代器 f1=Foo(10) # print(f1.__next__()) # print(f1.__next__()) # print(f1.__next__()) # print(f1.__next__()) for i in f1: # obj=iter(f1)------------>f1.__iter__() print(i) #obj.__next_()
#1,1,2,3,5,8 class Fib: def __init__(self): self._a=1 self._b=1 def __iter__(self): return self def __next__(self): if self._a > 100: raise StopIteration('终止了') self._a,self._b=self._b,self._a + self._b #a,b = 1,1+1 #a(1),b(2) = 2,3 return self._a f1=Fib() print(next(f1)) print(next(f1)) print(next(f1)) print(next(f1)) print(next(f1)) print('==================================') for i in f1: print(i)
九、__new__方法
我们知道实例化init会自动执行, 其实在init方法之前,还有一个new方法也会自动执行,你可以在new里执行一些实例化前的定制动作
class Person: # class person(object): def __init__(self,name): self.name = name print("name") # 先执行new def __new__(cls, *args, **kwargs): # new方法负责执行__init__,进行一些实例初始化前的工作 """ cls : 代表Person这个类本身 :param args: :param kwargs: 类的参数 :return: """ return object.__new__(cls) # 调用父类的__new__方法,必须这么干 ,要不然__init__方法就不会执行了 p = Person("XFD")
1、设计模式:开发软件有一套模式,开发手机端的,有一套模式,开发一套网络软件,也有一条模式
设计模式一共有23种,有一个组织叫gof
单例模式:单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
例如:你windows系统中,你用一个方法打开了一个软件,然后又用另外一个方法,还打开这个软件,但是这个软件不会产生新的,还是你刚才打开的,没有任何变化。
什么情况下用单例?
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
class Print(object): tasks = []#打印任务列表 instance = None #公共属性,都可以访问,存放第一个实例对象 def __init__(self,name,): self.name = name # self.job = job def add_job(self,job): self.tasks.append(job) print("[%s] 打印了 [%s],这是[%s]个任务" %(self.name,job,len(self.tasks))) def __new__(cls, *args, **kwargs): #只有第一次实例化的时候,正常运行,后面每次实例化,并不创建一个新的实例 if cls.instance is None: #进行正常实例化,并把实例化的对象存放在cls.instance中 obj = object.__new__(cls) #这个是实例化的过程 cls.instance = obj #把实例化好的对象存下来 ↑ return cls.instance #以后每次实例化,直接返回第一次存的实例对象 #在上一次实例的基础上,再执行_init_ p1 = Print("xfd") p2 = Print('sjc') p1.add_job('word') p2.add_job("excel") #感觉有问题,为什么名字没变?
十、描述符(__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 定义一个描述符
2、 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age #代理后,给name,age,获取,设置值,删除值时就会触发get,set,del p1=People('alex',18) #描述符Str的使用 p1.name p1.name='egon' del p1.name #描述符Int的使用 p1.age p1.age=18 del p1.age #我们来瞅瞅到底发生了什么 print(p1.__dict__) #空 print(People.__dict__)#{'__module__': '__main__', 'name': <__main__.Str object at 0x000001CC3517A860>, # 'age': <__main__.Int object at 0x000001CC3517A898>,
3、 注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性 # 类名.x = 1
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
4、描述符的应用
例:利用描述符功能,类的传参的时候必须为一种数据类型比如说,必须为str类型的参数才可以。(类型检测)
class Typed: def __init__(self,key): self.key = key #{name,age,salarty} def __get__(self, instance, owner): # instance:实例对象s1 owner:实例的拥有者 类 salary return instance.__dict__[self.key] def __set__(self, instance, value):#instance: value: if not isinstance(value,str):#只能判断一个类型! str写死了 #isinstance 检查是不是这个类型 instance.__dict__[self.key] = value # instance:实例对象s1 value: 就是传进的值 raise TypeError("你传入的不是字符串") class Salary: name = Typed("name")#触发typed 类,被代理了 #参数name 传进了 Typed 类的key age = Typed("age") salarty = Typed("salarty") def __init__(self,name,age,salary): self.name = name self.age = age self.salarty = salary s1 = Salary('sjc',12,3,) print(s1.__dict__)
#智能判断一个类型,如果多个类型都要判断
class Typed: def __init__(self,key,e_type): self.key = key #{name,age,salarty} self.e_type = e_type #{str,int,float} def __get__(self, instance, owner): # instance:实例对象s1 owner:实例的拥有者 类 salary return instance.__dict__[self.key] def __set__(self, instance,value,):# instance:实例对象s1 value: 就是传进的值 if not isinstance(value,self.e_type):#检测传进来的值是否匹配 raise TypeError("你传入的%s不是%s" %(value,self.e_type)) instance.__dict__[self.key] = value class Salary: name = Typed("name",str)#触发typed 类,被代理了 #参数name 传进了 Typed 类的key age = Typed("age",int) salarty = Typed("salarty",float)# 这里加上类型 def __init__(self,name,age,salary): self.name = name self.age = age self.salarty = salary s1 = Salary("sunjinchao",12,2,) print(s1.__dict__)
十一、__enter__ 和 __exit__
上下文管理协议,及with语句(with open (test")as f:),为了让一个对象兼容with语句,必须在这个对象里面 声明 __enter__,__exit__方法。
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') # f = Open('a.txt') with Open('a.txt') as f: print('=====>执行代码块') #出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊
class Open: def __init__(self,name): self.name=name def __enter__(self): return self #with 的过程中,其实是在触发enter def __exit__(self, exc_type, exc_val, exc_tb): #with 语句完成之后,才会触发__exit__ #exc_type: 异常类型 #exc_val:异常错误的值 #exc_tb:异常追踪信息 return True # 这样的话,程序异常会报错,但是不会终止程序的运行 #with 代码块的代码出现异常后,会执行__exit__ # 出现异常后,with代码块里面的后面的内容不会运行 with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name)