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函数,它可以在不改动其他函数的前提下,对函数的功能进行扩充。

装饰器用于以下场景:

引入日志、函数执行时间统计、执行函数后清理功能、权限校验、缓存

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 使用装饰器

装饰器的装饰过程:被装饰函数作为参数,传递给装饰器,并且返回值覆盖原函数

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 装饰器怎么接收参数

被装饰函数有参数怎么办?
装饰器的返回值,接收参数,并传递给被装饰的函数

装饰器怎么接收自己的参数
创建一个函数来接收参数,然后返回原来的装饰器

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循环进行使用

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:调用本函数是,只能传递位置参数

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函数

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)
posted @ 2023-06-07 11:10  万溪汇海  阅读(36)  评论(0编辑  收藏  举报