python反射、装饰和生成器
1.反射
反射的原理:
- 通过字符串对象(一个字符串的变量名)对应类对象中的属性和方法,对实例对象进行修改和访问
- 对类对象进行属性和方法的操作
- 添加
- 删除
- 修改
- 查看
1.1 通过反射添加或覆盖方法
class A: def a(self): print("a方法被调用啦!") def b(self): print("a方法被调用啦!") def c(self): print("a方法被调用啦!") def d(self): print("a方法被调用啦!") x=A() y=A() #对对象添加一个实例方法 # 反射添加的方法只针对被添加的对象,对其他对象并不会造成影响 #x对象添加的方法,y对象去调用就会报错 setattr(x,"e",print) #x.e()=print() x.e("e方法被调用啦!") setattr(x,"f",input) num=x.f("请输入你的密码:") #x.f()=input() x.e(num) #对对象覆盖一个实例方法 setattr(x,"b",print) x.b("这是通过反射覆盖之后的b方法")
1.2 通过反射删除对象方法
1 class A: 2 o=888 3 4 def __init__(self): 5 self.i=666 6 def a(self): 7 print("a方法被调用啦!") 8 def b(self): 9 print("b方法被调用啦!") 10 def c(self): 11 print("c方法被调用啦!") 12 def d(self): 13 print("d方法被调用啦!") 14 15 x=A() 16 17 print(x.i) 18 delattr(x,"i") #只能删除实例属性,不能删除方法 19 #print(x.i) 20 #delattr(x,"a") #不能删除方法 21 x.a() 22 #delattr(x,"o") #不能删除类属性 23 print(x.o)
1.3 通过反射判断对象是否有指定的方法
1 class A: 2 o=888 3 4 def __init__(self): 5 self.i=666 6 def a(self): 7 print("a方法被调用啦!") 8 def b(self): 9 print("b方法被调用啦!") 10 def c(self): 11 print("c方法被调用啦!") 12 def d(self): 13 print("d方法被调用啦!") 14 15 @classmethod 16 def e(cls): 17 print("e方法") 18 19 x=A() 20 21 #可以使用内建函数hasattr()判断对象是否存在该类的类属性或者实例属性,类方法或者实例方法 22 hasattr(x,"a") #True 23 hasattr(x,"b") #True 24 hasattr(x,"v") #False 25 hasattr(x,"o") #True 26 hasattr(x,"i") #True 27 hasattr(x,"e") #True
1.4 通过反射读取方法
1 o=888 2 3 def __init__(self): 4 self.i=666 5 def a(self): 6 print("a方法被调用啦!") 7 def b(self): 8 print("b方法被调用啦!") 9 def c(self): 10 print("c方法被调用啦!") 11 def d(self): 12 print("d方法被调用啦!") 13 14 @classmethod 15 def e(cls): 16 print("e方法") 17 18 x=A() 19 f=getattr(x,"a") # 把a方法的引用传递给f 20 f() #a方法被调用啦 #那么f()=a() 21 22 f=getattr(x,"i") 23 print(f) 24 25 f=getattr(x,"e") 26 f() 27 28 f2=getattr(x,"o") 29 print(f2) 30 31 #通过x对象调用所有方法 32 for i in ["a","b","c","d","e"]: 33 f=getattr(x,i) 34 f()
2 装饰器
作用:装饰器的本质是一个python函数,它可以在不改动其他函数的前提下,对函数的功能进行扩充。
装饰器用于以下场景:
引入日志、函数执行时间统计、执行函数后清理功能、权限校验、缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 | def test1(func): def test2(root,key): if root = = "root" and key = = 123 : print ( "您的用户名和密码输入正确" ) else : print ( "您的用户名或密码输入错误" ) return test2 @test1 def test3(root,key): pass test3( "root" , 1234 ) |
函数:
- 可以作为参数传递
- 可以作为返回值返回
- 修改名字
- 新的覆盖旧的
函数也是一种变量
装饰器:
- 接受函数,并返回函数的函数
- 是一个函数,参数是函数,返回值是函数
2.1 使用装饰器
装饰器的装饰过程:被装饰函数作为参数,传递给装饰器,并且返回值覆盖原函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def logs(func): #装饰器 def f( * args, * * kwargs): print (level,datetime.datetime.now(),func.__name__, "开始调用了" ) func( * args, * * kwargs) #转发参数 print (level,datetime.datetime.now(),func.__name__, "调用结束了" ) return f @logs def add(): print ( "add is calling" ) 等同于 add = logs(add) |
2.2 装饰器怎么接收参数
被装饰函数有参数怎么办?
装饰器的返回值,接收参数,并传递给被装饰的函数
装饰器怎么接收自己的参数
创建一个函数来接收参数,然后返回原来的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import datetime def logs(level): def _logs(func): #装饰器 def f( * args, * * kwargs): print (level,datetime.datetime.now(),func.__name__, "开始调用了" ) func( * args, * * kwargs) #转发参数 print (level,datetime.datetime.now(),func.__name__, "调用结束了" ) return f return _logs # # def p(y): # return y # # #@p @logs (level = "INFO" ) #装饰器的使用 logs接收参数 才能完成调用 返回一个返回值 def add(x,y): #被装饰函数 print ( "add is calling:" ,f "{x=},{y=}" ) def sub(x,y): #被装饰函数 print ( "sub is calling:" ,f "{x=},{y=}" ) #装饰的过程:被装饰的函数作为参数,传递给装饰器,并且将返回值覆盖原来的函数 #add=logs(add) add(x = 1 ,y = 2 ) sub = logs(level = "DEBUG" )(sub) sub(x = 11 ,y = 22 ) |
3.生成器
如果函数中有yield关键字,其调用结果,则返回一个生成器
生成器是一个可迭代对象,可以被for循环遍历使用
range 就是一个生成器
在遍历时才执行,并计算返回值
生成器,属于迭代器:交给for循环进行使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def add(a,b): c = a + b print (c) yield 123 return c c = add( 1 , 2 ) print (c) #c是生成器 for i in c: #生成器:在使用数据时,才产生数据 print (f "{i=}" ) l = [ 1 , 2 , 3 ] iter_l = iter (l) #为列表创建迭代器 for i in iter_l: #for循环是为了迭代器服务的 print (i) for i in l: print (i) |
4.面试题
4.1 什么是可迭代对象、迭代器、生成器?
1)可迭代对象包含迭代器。
2)如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
3)定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法。
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和next方法),不需要再手动实现两方法。
生成器在迭代的过程中可以改变当前迭代值,而修改普通迭代器的当前迭代值往往会发生异常,影响程序的执行
具有yield关键字的函数都是生成器,yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yield
4.2.创建一个装饰器,用来校验【被装饰函数】收到的参数是否包含关键字参数,如果是,则打印 :Error:调用本函数是,只能传递位置参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def check_kwargs(func): def f( * args, * * kwargs): if kwargs: print ( "Error:调用本函数是,只能传递位置参数" ) return #raise ValueError return func( * args, * * kwargs) return f @check_kwargs def add(a,b): return a + b print (f "1+1={add(1,1)}" ) print (f "2+2={add(a=2,b=2)}" ) |
4.3.创建一个生成器,用来模拟和代替内置的range函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | def my_range(start,end = None ,step = None ): if end = = None and step = = None : #说明只有start接收到了参数 start,end = end,start #start和end互换 if start is None : start = 0 if end is None : end = 0 if step is None : step = 1 if end = = 0 : return while True : yield start start + = step if start> = end: break print ( "*" * 10 ) for i in my_range( 5 , 0 , 1 ): print (i) print ( "*" * 10 ) for i in my_range( 5 ): print (i) print ( "*" * 10 ) for i in my_range( 5 , 10 ): print (i) print ( "*" * 10 ) for i in my_range( 1 , 10 , 2 ): print (i) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)