python基础之 面向对象之反射
1.isinstance和issubclass
issubclass(Son,Foo) 判断雷与类之间的是否有继承关系,接受两个参数,一个是疑似子类,一个是疑似父类,判断Son是否是Foo的子类 obj = 1 isinstance(obj,int) 判断obj是否是int类型,成人继承关系,更多的时判断对象与类之间的关系,结果判断包括这个类,也包括这个类的父类 type(obj) in int 判断obj是否是int类型,只是承认这个对象的类,不承认所有的继承关系
2.什么是反射
官方解释:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
python中的反射:
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
用字符串类型的属性名/方法名找到属性的值或者方法的内存地址
所有可以反射的内容实际上都是变量
内存地址存的是"具体的值",直接能取到结果
内存地址中存的是"函数\方法\类",取到的时内存地址
3.反射例子
class Manager: # 管理员用户 def __init__(self,name): self.name = name def create_course(self): # 创建课程 print('in Manager create_course') def create_student(self): # 给学生创建账号 print('in Manager create_student') def show_courses(self): # 查看所有课程 print('in Manager show_courses') def show_students(self): # 查看所有学生 print('in Manager show_students') admin= Manager('alex') operate_lst = [('创建课程','create_course'),('创建学生账号','create_student'), ('查看所有课程','show_courses'),('查看所有学生','show_students')] for index,opt in enumerate(operate_lst,1): print(index,opt[0]) num = input('请输入您要做的操作 :') if num.isdigit(): num = int(num) if hasattr(admin,operate_lst[num-1][1]): getattr(admin,operate_lst[num-1][1])()
class A: Country = '中国' @classmethod def show(cls): print('国家 : ',cls.Country) print(getattr(A,'Country')) # print(A.Country) getattr(A,'show')() # A.show()
import re ret = re.findall('\d+','2985urowhn0857023u9t4') print(ret) ret = getattr(re,'findall')('\d+','2985urowhn0857023u9t4') print(ret) import time print(time.time()) print(getattr(time,'time')())
#反射本文件中的内容 :只要是出现在全局变量中的名字都可以通过getattr(modules[__name__],字符串数据类型的名字)都可以反射,其中的__name__指的是当前文件地址 from sys import modules print(modules) #其中modules是字典类型,字典中的key是模块的名字,字典中的value是模块相对应的文件地址 #语法 a = 1 b = 2 getattr(modules[__name__],'变量名') #函数名 def func(a,b): print('in func',a,b) getattr(modules[__name__],'func') # func func(1,2) getattr(modules[__name__],'func')(1,2) # func #类名 class Course: def func(self): print('in func') print(Course) 'Course' print(getattr(modules[__name__],'Course')) # Course getattr(modules[__name__],'Course')() # 实例化的过程
4.总结
# hasattr和getattr # 只要是a.b这种结构,都可以使用反射 # 用对象\类\模块反射,都只有以下场景 # 这种结构有两种场景 # a.b b是属性或者变量值 # getattr(a,'b') == a.b # a.b() b是函数或者方法 # a.b() # getattr(a,'b')() # a.b(arg1,arg2) # getattr(a,'b')(arg1,arg2) # a.b(*args,**kwargs) # getattr(a,'b')(*args,**kwargs) # 如果是本文件中的内容,不符合a.b这种结构 # 直接调用func() # getattr(sys.modules[__name__],'func')() # 直接使用类名 Person() # getattr(sys.modules[__name__],'Person')() # 直接使用变量名 print(a) # getattr(sys.modules[__name__],'a') # 所有的getattr都应该和hasattr一起使用 # if hasattr(): getattr() # setattr 只用来修改或者添加属性\变量,不能用来处理函数或者是其他方法 # a.b = value # setattr(a,'b',value) # delattr 只用来删除 属性\变量 # del a.b 删除属性 相当于删除了a对象当中的b属性 # delattr(a,'b')
5.内置函数
__call__()函数
class A: def __call__(self, *args, **kwargs): #触发机制为实例化的对象加上扩号()来调用一下__call__() print('in call') A()() #实例化对象()自动执行__call__内容,Flask框架中用的较多
with 上下文处理
class A: def __enter__(self): print('start') def __exit__(self, exc_type, exc_val, exc_tb): #exc_type是异常的对象类型,exc_value是异常的值,exc_tb是一个traceback对象,对象中包含出错的行数、位置等数据。 print('exit') with A(): #实例化一个对象,但是未赋值 print('啦啦啦啦') #有点像是生成器的with上下文处理,其中with 与__enter__方法和__ecit__()方法混合使用
#在打印print('啦啦啦啦')之前,想执行以下__enter__函数,然后在执行with,最后执行__exit__函数
非内存资源使用with去关闭
内存资源使用垃圾回收机制和手动去关闭(xx.close())
上下文管理模块
import pickle class mypickledump(): def __init__(self,path,mode='ab'): self.path = path self.mode = mode def __enter__(self): print('start') self.info = open(self.path,self.mode) return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') self.info.close() def pickle_dump(self,text): pickle.dump(text,self.info) with mypickledump('pickle_file',mode='ab') as info: #as info的值是__enter__函数的返回值 print("func") info.pickle_dump({1,2,3,4,5,6}) info.pickle_dump({1, 2, 3, 4, 5, 6}) info.pickle_dump({1, 2, 3, 4, 5, 6})
class mypickleload(): def __init__(self,path,mode='rb'): self.path = path self.mode =mode def __enter__(self): print('start') self.info = open(self.path,self.mode) return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') self.info.close() def pickle_load(self): while True: try: ret = pickle.load(self.info) yield ret except EOFError: break with mypickleload('pickle_file','rb') as info: for item in info.pickle_load(): print(item)
__new__():构造函数 面试必考
#必须先走__new__方法开辟空间,没有的话,就去父类找
class Foo(): def __new__(cls, *args, **kwargs): #先执行,cls永远不能使用self参数,因为self是之后才被创建出来 触发机制为:在类被实例化的时候,必须先执行__new__方法 print('in new') obj = object.__new__(cls) #self在这个地方才被创建出来,在内存中开辟一块属于这个对象的内存地址,并且在这个类中间存放一个类指针,然后赋值给__init__的第一个参数self print (obj) return (obj) def __init__(self): #后执行 print('init',self) Foo() #实例化一个Foo的对象分为两步:包括创建对象和初始化对象。 #其中__new__是构造方法,是在实例创建之前被调用的,因为他的任务就是创建实例,然后返回该实例,是个静态方法 #__init__是当实例对象创建完后被调用,然后设置对象属性的初始值
单例模式是日常应用中最广泛的模式了,其目的就是令到单个进程中只存在一个类的实例,保证一个类无论 被实例化多少次,始终使用的是同一块内存地址从而可以实现数据的共享,节省系统开销,防止io阻塞等等 class A: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = object.__new__(cls) return cls._instance def __init__(self,name=None,age=None): self.name = name if age: self.age = age a1 = A('kobe',84) print(a1) a2 = A('kobe',83) print(a2) a3 = A('kobe',82) print(a3) print(a1.age) print(A.__dict__) 因为类A本省没有__new__方法,只能借助期父类object来重写__new__方法。然后将类的一个实例绑定到类变量_instance上; 如果cls._instance为None,则说明该类还没有被实例化过,new一个该类的实例,并返回;如果cls._instance不为None,直接返回_instance,代码如下:
__del__():析构函数
class A: def __init__(self,name,age): self.name = name self.age = age() def __del__(self): #触发机制是碰到del obj(一定是对象,不能是删除属性)函数的时候先来执行__del__(self)函数,但是没有del 的时候,在python解释器执行到最后的时候也会执行一次__del__函数 # 只和del obj语法有关系,在执行del obj之前会来执行一下__del__中的内容 print('执行我啦') obj = A('alex',84) print(obj.name) print(obj.age) del obj # 这个变量已经没了 time.sleep(1) print('lalala') # 在所有的代码都执行完毕之后,所有的值都会被python解释器回收
__del__析构方法,再删除一个对象之前,归还一些操作系统资源 一般不关心内存级别的资源,而是关心操作系统的资源 python解释器会在不使用一个值的时候,会自动删除 只和del obj语法有关系,在执行del obj之前会来执行一下__del__ 在所有的代码都执行完毕之后,所有的代码都会被python解释器回收 在我们操作系统的资源要及时归还,否则会占用大量的系统的资源,如及时 关闭文件句柄,和网络资源,数据库链接等 那这个和with中的__exit__的区别是什么那? with中的__exit__是关心内存级别的资源浪费,如及时关闭文件句柄,和网络资源,数据库链接等
__str__&__repr__
class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period def __str__(self): '''打印这个对象的时候 自动触发__str__''' '''使用%s进行字符串的拼接的时候 自动触发__str__''' return '%s,%s,%s'%(self.name,self.price,self.period) python = Course('python',25000,'6 months') print(python) print('course %s'%python) print(f'course {python}') 如果,不实现str方法,那么对象打印出来只是一串地址 ======================================================================================================== class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period def __repr__(self): # 备胎 return '%s,%s,%s'%(self.name,self.price,self.period) def __str__(self): return self.name python = Course('python',25000,'6 months') print(python) print('course %s'%python) print(f'course {python}') print('course %r'%python)
如果str存在,repr也存在 那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__ 而repr(obj)和%r格式化字符串,都会调用__repr__ 如果str不存在,repr存在 那么print(obj),字符串格式化format,%s,%r 和repr(obj)都调用__repr__ 如果str存在,repr不存在 那么print(obj)和使用字符串格式化format,%s这两种方式 调用的都是__str__ repr(obj)和%r格式化字符串 都会打印出内存地址 执行过程: 打印对象 先走自己的str,如果没有,走父类的,如果除了object之外的所有父类都没有str 再回来,找自己的repr,如果自己没有,再找父类的。 repr是str的备胎 和所有的字符串格式化以及直接打印这个对象相关 other: 有了repr或者str在打印对象的时候 就不会显示用户不关心的内存地址了 增强了用户的体验 在程序开发的过程中 如果我们需要频繁打印对象中的属性,需要从类的外部做复杂的拼接,实际上是一种麻烦 如果这个拼接工作在类的内部已经完成了,打印对象的时候直接就能显示
面向对象中重要的方法
mro() #用来查看类的继承顺序 print(类名.mro()) __bases__ #用来显示继承了多少个类 print(类名.__bases__) __dict__ #用来查看类属性或者变量的属性或者内存空间的属性 print(类名.__dict__)/print(对象名象名.__dict__) __name__ #用来显示当前文件 Python在import其它模块时,是从sys.path中搜索的。sys.path的初始值会受到PYTHONPATH、PYTHONHOME等环境变量的影响。也可以在脚本运行过程中动态修改sys.path从而import自己需要的模块。 sys.modules是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules都将记录这些模块。 字典sys.modules对于加载模块起到了缓冲的作用。当某个模块第一次导入,字典sys.modules将自动记录该模块。 当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。 字典sys.modules具有字典所拥有的一切方法,可以通过这些方法了解当前的环境加载了哪些模块 import sys print(sys.modules[__name__]) print(sys.modules[__name__].__dict__) print(sys.modules.values()) print(sys.modules.keys()) print(sys.modules.items())
重点:什么是cls?
cls是表示的时类本身,在类的方法里面用的到。
在类的__new__函数里面的第一个参数就是cls,代表的时这个类的本身,类方法里面可以用的到,类方法是与类本身有关而与实例化无关的
那么@staticmethod和classmethod两种类方法的区别是什么那?
既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
从它们的使用上来看,
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

上帝说要有光,于是便有了光;上帝说要有女人,于是便有了女人!
浙公网安备 33010602011771号