python基础之 面向对象之反射

时间:2023-03-09 21:52:04
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})

with处理pickle写入

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)

with处理pickle读取

 __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参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

返回系列