Python3 函数进阶1

闭包函数

什么是闭包函数

闭包函数本质上就是函数嵌套和高阶函数

闭包函数的满足条件:

  • 必须嵌套函数
  • 内嵌函数必须引用外部函数的变量
  • 外部函数必须返回内嵌函数的函数对象(函数名)
# outer 是一个闭包函数
def outer():
    x = 1
    def inner():
        print(X)  # 内嵌函数引用了外部函数里的变量
    return inner  # 外部函数返回内嵌函数的函数对象

f1 = outer()
f1()

闭包函数的作用

闭包函数不仅返回了内嵌函数的函数对象, 还在其身上包裹了一层(外部函数)的局部作用域. 这使得, 无论该函数对象在何处被调用, 都优先使用包裹在自己身上的这层作用域.

# 常规函数
import requests

def get(url):
    response = requests.get(url)
    print(f'done:{url})

          
# 如果后续要多次调用get函数爬取同一网站, 则每次都要输入一次网址          
get('https://www.python.org')
get('https://www.python.org')
get('https://www.python.org')
          
# 使用默认形参也只能解决一个网站
get('https://www.cnblogs.com/bigb/')
get('https://www.cnblogs.com/bigb/')
get('https://www.cnblogs.com/bigb/')      

我们可以用闭包函数解决上述问题

# 闭包函数
import requests

# url是外部函数outer的形参, 可以把其看成函数outer局部作用域中的变量
def outer(url): 
    def get():
        response = requests.get(url)
        print(f'done:{}')
     return get

# 在外部函数空间作用域中存在 url = 'https://www.python.org'这个变量, 内部函数可以永久引用
python = outer('https://www.python.org')

python()  # get()
python()
python()
        

装饰器

什么是装饰器

装饰器本质上就是一个闭包函数, 为被装饰对象添加额外功能

装饰器的实现必须遵循两大原则:

  • 不修改被装饰对象源代码
  • 不修改被装饰对象的调用方式

无参装饰器

下面我们就用装饰器给既定函数f1增加计时功能

import time

def f1():
    '''被装饰函数'''
    print('this is f1')
    time.sleep(1)  # 程序休眠1秒
    
 # 定义time_count装饰器函数, func是形参名, 用来接收函数对象  
 def time_count(func):
	'''装饰器函数'''
    
    # wrapper是辅助函数
    def wrapper():
		start = time.time()
    	func()  # 使用函数对象调用函数
    	end = time.time()
    	print(f'程序耗费时长为: {end - start}')
        
     return wrapper


f1 = time_count(f1)  # wrapper 
f1()  # wrapper()
   
'''
this is f1
程序耗费时长为: 1.0000574588775635
''' 

如果f1是个有参函数我们该怎样通过装饰器传入参数呢?

由上述例子我们知道, f1() = wrapper(), 因此我们给wrapper传参即可

import time

def f1(a):
    '''被装饰函数'''
    print('this is f1, 参数:', a)
    time.sleep(1)  # 程序休眠1秒
    
 # 定义time_count装饰器函数, func是形参名, 用来接收函数对象  
 def time_count(func):
    '''装饰器函数'''
    
    # wrapper是辅助函数
    def wrapper(*args,**kwargs):
		start = time.time()
    	func(*args,**kwargs)  # 使用函数对象调用函数
    	end = time.time()
    	print(f'程序耗费时长为: {end - start}')
        
     return wrapper


f1 = time_count(f1)  # wrapper 
f1(1)  # wrapper()



'''
this is f1, 参数: 1
程序耗费时长为: 1.0000572204589844
'''

如果函数f1有返回值我们如何通过装饰器实现呢?

由于f1() = wrapper(), 因此f1 的返回值就是wrapper的返回值

import time

def f1(a):
    '''被装饰函数'''
    print('this is f1, 参数:', a)
    time.sleep(1)  # 程序休眠1秒
    return a
    
 # 定义time_count装饰器函数, func是形参名, 用来接收函数对象  
 def time_count(func):
    '''装饰器函数'''
    
    # wrapper是辅助函数
    def wrapper(*args,**kwargs):
		start = time.time()
    	res = func(*args,**kwargs)  # 使用函数对象调用函数
    	end = time.time()
    	print(f'程序耗费时长为: {end - start}')
        return res
        
     return wrapper


f1 = time_count(f1)  # wrapper 
res = f1(1)
print(res)


'''
this is f1, 参数: 1
程序耗费时长为: 1.0000569820404053
1
'''

还是不理解装饰器吗? 那就记住装饰器的模板吧!

def deco(func):
    def wrapper(*args,**kwargs):
        # 添加功能
        res = func(*args, **kwargs)
        # 添加功能
        return res
    return wrapper

装饰器语法糖:

就是在被装饰函数上面单独写上@装饰器名 , 它实现的功能是f1 = time_count(f1)

@time_count
def f1(a):
    '''被装饰函数'''
    print('this is f1, 参数:', a)
    time.sleep(1)  # 程序休眠1秒
    return a


res = f1(1)
print(res)


'''
this is f1, 参数: 1
程序耗费时长为: 1.0000569820404053
1
'''

有参装饰器

我们现在写一个登陆功能的装饰器

# 登陆装饰器

username_list = []

def login(func):
    def wrapper(*args, **kwargs):

        if username_list:
            print('请勿重复登陆')
            res = func(*args, **kwargs)
            return res

        username_inp = input('请输入用户名: ')
        pwd_inp = input('请输入密码: ')

        # 从user_info读取用户信息
        with open('user_info', 'r', encoding='utf-8') as fr:
            for user_info in fr:
                name, pwd = user_info.strip().split(':')

                if name == username_inp and pwd == pwd_inp:
                    print('登录成功')
                    username_list.append(username_inp)

                    res = func(*args, **kwargs)  # 先调用 再返回
                    return res
            else:
                print('用户名密码错误')

        res = func(*args, **kwargs)

        return res

    return wrapper

上面我只实现了从user_info里面读取用户密码信息进行判断.

现在我们想实现管理员登陆则从admin_info里面读取信息, 用户登陆则从user_info里面读取信息, 该怎么办呢?

username_list = []

def sanceng(role):
    
    def login(func):
        
        def wrapper(*args, **kwargs):
            
            if username_list:
            print('请勿重复登陆')
            res = func(*args, **kwargs)
            return res

        	username_inp = input('请输入用户名: ')
        	pwd_inp = input('请输入密码: ')

        	# 从{role}_info读取用户信息
        	with open(f'{role}_info', 'r', encoding='utf-8') as fr:
            	for user_info in fr:
                	name, pwd = user_info.strip().split(':')

                	if name == username_inp and pwd == pwd_inp:
                    	print('登录成功')
                    	username_list.append(username_inp)

                    	res = func(*args, **kwargs)  # 先调用 再返回
                    	return res
            		else:
               			 print('用户名密码错误')
                   
		return wrapper
    
    return login


# 那我们在装饰 被装饰对象 时就要输入一个参数
@sanceng(admin)
def f1():
    pass

  
原文地址:https://www.cnblogs.com/bigb/p/11574779.html