类的进阶
一 isinstance(obj,cls)和issubclass(sub,super)
class Foo: def __init__(self,name): self.name = name obj = Foo("egon") print(isinstance(obj,Foo)) l = list([1,2,3]) print(isinstance(l,list)) -----------------结果----------- True True
class Foo: pass class Bar(Foo): pass print(issubclass(Bar,Foo)) ------------------------结果---------- True
二.反射
# 反射类中的名字 getattr(类名,'静态属性') getattr(类名,'类方法')() getattr(类名,'静态方法')() # 反射对象中的名字 getattr(对象名,'对象属性') getattr(对象名,'方法名')() # 反射模块中的名字 import 模块名 getattr(模块名,'模块中的变量') getattr(模块名,'模块中的函数')() getattr(模块名,'模块中的类名') # 反射当前模块中的名字 import sys getattr(sys.modules[__name__],'变量') getattr(sys.modules[__name__],'函数')() getattr(sys.modules[__name__],'类名')
1.python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
class People: country = "china" def __init__(self,name): self.name = name p = People("egon") print(p.__dict__) #------------------------- print(hasattr(p,"name")) print("name" in p.__dict__) print(hasattr(People,"country")) ---------------------------------- {'name': 'egon'} True True True
class People: country = "china" def __init__(self,name): self.name = name def walk(self): print("%s is working" % self.name) p = People("egon") print(getattr(p,"country")) #print(p.country) # china print(getattr(p,"walk")) #print(p.walk) #<bound method People.walk of <__main__.People object at 0x101979278>>
class People: country = "china" def __init__(self,name): self.name = name def walk(self): print("%s is working" % self.name) p = People("egon") setattr(p,"age",15) print(p.__dict__) {'age': 15, 'name': 'egon'}
class People: country = "china" def __init__(self,name): self.name = name def walk(self): print("%s is working" % self.name) p = People("egon") print(p.__dict__) {'name': 'egon'} delattr(p,"name") print(p.__dict__) {}
import sys def add(): print("add") def delete(): print("delete") def update(): print("update") def search(): print("search") this_module = sys.modules[__name__] while True: cmd = input("please input: ") if not cmd:continue if hasattr(this_module,cmd): func = getattr(this_module,cmd) func()
2.反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,
lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?
即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
#Ftpclent.py 文件 class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器 %s ' %addr) self.addr=addr def get(self): print("get") #Ftpserver.py 文件 from day32 import FtpClient obj = FtpClient.FtpClient('192.168.1.1') if hasattr(obj,"get"): func = getattr(obj,"get") func() print("other func")
好处二:动态导入模块(基于反射当前模块成员)
#导入字符串 __import__("time") #使用模块 import importlib t = importlib.import_module("time") print(t.time()) 1493026292.217129
三.__setattr__,__delattr__,__getattr__
#初始化和赋值时触发__setattr__运行 #发现__dict__没有name 和 age class Foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): print("__setattr__ key:%s value:%s"%(key,value)) obj = Foo("egon") __setattr__ key:name value:egon obj.age = 18 __setattr__ key:age value:18 print(obj.__dict__) #发现__dict__没有name 和 age {} #初始化和赋值都在__dict__中找到 class Foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): print("__setattr__ key:%s value:%s"%(key,value)) self.__dict__[key] = value obj = Foo("egon") __setattr__ key:name value:egon obj.age = 15 __setattr__ key:age value:15 print(obj.__dict__) {'name': 'egon', 'age': 15} #初始化赋值时判断如果不是数字不可以赋值 class Foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): if not isinstance(value,int): raise TypeError("type error") self.__dict__[key] = value obj = Foo(15) obj.age = 15 print(obj.__dict__) {'age': 15, 'name': 15}
class Foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): self.__dict__[key] = value def __delattr__(self, item): self.__dict__.pop(item) obj = Foo("egon") print(obj.__dict__) {'name': 'egon'} del obj.name print(obj.__dict__) {}
#属性不存在时候运行 class Foo: def __init__(self,name): self.name = name def __getattr__(self, item): print("__getattr__ %s" % item) obj = Foo("egon") print(obj.name) egon print(obj.xxx) __getattr__ xxx None
四. 二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
#自定义自己的数据类型,追加和插入,如果不是数字,报错 class List(list): def append(self, p_object): if not isinstance(p_object,int): raise TypeError("Type error") super().append(p_object) def insert(self, index, p_object): if not isinstance(p_object,int): raise TypeError("Type error") super().insert(index,p_object) l = List([1,2,3]) print(l) [1, 2, 3] l.append("4") #不能追加字符串 l.insert(0,-1) print(l) [-1, 1, 2, 3]
import time class Open: def __init__(self,filepath,mode="r",encode="utf-8"): self.x = open(filepath,mode=mode,encoding=encode) self.filepath = filepath self.mode = mode self.encoding = encode def write(self,value): log_time = time.strftime('%Y-%m-%d %X') self.x.write("%s %s" % (log_time,value)) def __getattr__(self, item): return getattr(self.x,item) f = Open("b.txt","a+") f.write("11111111\n") f.seek(0) print(f.read())
#基于继承来定制自己的数据类型 class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上类型检查' if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增自己的属性' index=len(self)//2 return self[index] l=List([1,2,3]) print(l.mid) 基于授权来定制自己的数据类型: class Open: def __init__(self,filepath,mode,encode='utf-8'): self.f=open(filepath,mode=mode,encoding=encode) self.filepath=filepath self.mode=mode self.encoding=encode def write(self,line): print('write') self.f.write(line) def __getattr__(self, item): return getattr(self.f,item) f=Open('a.txt','w') f.write('123123123123123\n') print(f.seek) f.close()
五.反射作业
1.基于授权定制自己的列表类型,要求定制的自己的__init__方法, 2.定制自己的append:只能向列表加入字符串类型的值 3.定制显示列表中间那个值的属性(提示:property) 4.其余方法都使用list默认的(提示:__getattr__加反射)
class List: def __init__(self,x): self.seq=list(x) def append(self,value): if not isinstance(value,str): raise TypeError('must be str') self.seq.append(value) @property def mid(self): index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): print("111") return str(self.seq) l=List([1,2,3]) l.append('1') print(l.mid) l.insert(0,123)
六.__setitem__,__getitem,__delitem__
#把对象操作属性模拟成字典的格式 class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, item): self.__dict__.pop(item) f = Foo("egon") print(f.name) egon f.name = "egonlin" print(f.__dict__) {'name': 'egonlin'} f["age"] = 18 print(f.__dict__) {'name': 'egon', 'age': 18} del f["name"] print(f.__dict__) {}
七.__slots__
''' 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。 ''' class Foo: __slots__='x' f1=Foo() f1.x=1 f1.y=2#报错 print(f1.__slots__) #f1不再有__dict__ class Bar: __slots__=['x','y'] n=Bar() n.x,n.y=1,2 n.z=3#报错 __slots__使用
八.__next__和__iter__实现迭代器协议
class Foo: def __init__(self, end,start=0): self.start = start self.end = end def __iter__(self): return self def __next__(self): if self.start >= self.end: raise StopIteration n = self.start self.start+=1 return n f = Foo(10) for i in f: print(i)
class Range: def __init__(self,start,stop): self.start = start self.stop = stop def __iter__(self): return self def __next__(self): if self.start >= self.stop: raise StopIteration("StopIteration Error") n = self.start self.start += 1 return n obj = Range(0,15) for i in obj: print(i)
class Range: def __init__(self, *args): self.args = args if len(self.args) == 2: self.start = self.args[0] self.end = self.args[1] else: self.start = 0 self.end = self.args[0] def __iter__(self): return self def __next__(self): n = self.start if n < self.end: n = self.start self.start += 1 return n raise StopIteration for i in Range(1,10): print(i) for i in Range(10): print(i)
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(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' %i,end='')
九.__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') --------------------结果------------- 执行我啦 ------->
class Foo: def __del__(self): print('执行我啦') f1=Foo() print('------->') --------------------结果------------- -------> 执行我啦
十.__enter__和__exit__
with open('a.txt') as f: '代码块' 上述叫做上下文管理协议,即with语句,为了让一个对象兼容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中代码块执行完毕时执行我啊') with Open('a.txt') as f: print('=====>执行代码块') -----------------结果---------------- 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #------------------------------->不会执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) return True with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #------------------------------->会执行
import time class Open: def __init__(self,filepath,mode="r",encode="utf-8"): self.x = open(filepath,mode=mode,encoding=encode) self.filepath = filepath self.mode = mode self.encoding = encode def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.x.close() return True def write(self,value): log_time = time.strftime('%Y-%m-%d %X') self.x.write("%s %s" % (log_time,value)) def __getattr__(self, item): return getattr(self.x,item) with Open("b.txt","a+") as f : #obj = Open().__enter__() ---> obj = f f.write("11111111\n")
十一.__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ __call__
十二.__str__
class Foo: def __init__(self,name,age): self.name = name self.age = age def __str__(self): return "名字是%s 年龄是%s" %(self.name,self.age) f1 = Foo("agon",18) print(f1) # --------------结果---------- 名字是agon 年龄是18
十三.__new__
十四. 创建类
1. 类在创建的时候执行type的__init__方法 类相当于type的对象 2. 类() = type对象() 相当于实例化 执行type的__call__方法 3. type的__call__方法会调用 - 调用类的__new__方法 用于创建对象 - 调用类的__init__方法 用于初始化 4. 对象() 执行类的__call__方法
-------------------------------(一)------------------------------- class SingletonType(type): def __init__(self,*args,**kwargs): super(SingletonType,self).__init__(*args,**kwargs) class Foo(metaclass=SingletonType): pass 类默认是由type创建也可以指定metaclass创建 在父类构造方法继续让type执行 相当于没做什么操作 对象是由类创建的 -------------------------------(二)------------------------------- class SingletonType(type): def __init__(self,*args,**kwargs): print(self) super(SingletonType,self).__init__(*args,**kwargs) class Foo(metaclass=SingletonType): pass 此时在父类里面打印print(self) 发现结果为<class '__main__.Foo'> 对象是类创建 创建对象时候类的__init__方法自动执行 类是type创建 创建类时候type的__init__方法自动执行 -------------------------------(三)------------------------------- 对象() 执行类的__call__方法 类() 执行type的__call__方法 执行__call__方法内部会调用(类的__new__方法和__init__方法) class SingletonType(type): def __init__(self,*args,**kwargs): print(self) super(SingletonType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): # cls 表示 SingletonType类 obj = cls.__new__(cls, *args, **kwargs) # obj 为创建的对象 class Foo(metaclass=SingletonType): def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs) -------------------------------(四)------------------------------- class SingletonType(type): def __init__(self,*args,**kwargs): print(self) super(SingletonType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): # cls 表示 SingletonType类 obj = cls.__new__(cls, *args, **kwargs) cls.__init__(obj,*args, **kwargs) # 实例化 底下两者效果相同 #obj.__init__(*args, **kwargs) return obj class Foo(metaclass=SingletonType): def __init__(self): pass def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs) obj = Foo()
元类是用来如何创建类的 正如类是创建对象的模版一样 元类的实例是类 正如类的实例为对象 一个类如果没有声明自己的元类,默认他的元类就是type, 也可以通过继承type来自定义元类 def __init__(self,name,age): pass obj = type('foo',(object,),{'x':1,'__init__':__init__}) #参数一: 类名 #参数二: 继承类 #参数三: 类的属性 方法 type 实例化结果就是一个类