用装饰器简化大量if-elif代码
源码地址:https://github.com/edgedb/edgedb/blob/master/edb/common/value_dispatch.py
鸣谢原文:一日一技:使用装饰器简化大量 if…elif…代码
源码只对同步函数有效果,我将其稍作修改后,让其针对同步,异步,类中的实例方法也具备效果
先看食用方式
这里展示最复杂的例子,即:装饰类中的异步实例方法
如要查看简单的例子,请点击上方的一日一技文章,里面展示了该方式最简单的使用例子
# 示例:
import asyncio
from match_case_model import value_dispatch, value_dispatch_async, value_dispatch_class_async, value_dispatch_class
# 四个装饰器分别对应 value_dispatch: 同步函数, value_dispatch_async: 异步函数,
# value_dispatch_class_async: 异步实例方法, value_dispatch_class: 同步实例方法
class Demo:
@value_dispatch_class_async
async def eat(self, fruit, *args, **kwargs):
"""
主函数,需要选择4个装饰器之一来装饰,这是类中的异步实例方法,所以使用该装饰器
:param fruit: 分支选择参数
:param args, kwargs: 选择分支失败后用来接收原本传给分支的额外参数
"""
return f"I don't want a {fruit}..."
@eat.register('apple') # 注册 fruit == 'apple' 时的函数
async def _eat_apple(self, fruit, feel):
"""
分支函数(被@主函数.register("XXX")装饰的函数)
:param fruit: 调用主函数时传入的分支选择参数,该函数体内可能用不上这个参数,但是必须要接收该参数
:param feel: 分支函数需要的额外参数,只要有一个分支函数接收该参数,那么所有分支函数都要接收该参数
"""
return f"I love {fruit},{feel}"
# @eat.register('eggplant') # 一个函数可以接受多个注册,但推荐使用下面那个方式
# @eat.register('squash')
# async def _eat_what(self, fruit, feel):
# return f"I didn't know {fruit} is a fruit!"
@eat.register_for_all(('eggplant', 'squash')) # 一个函数接受多个注册的方式
async def _eat_what(self, fruit, feel): # 这个分支无需使用feel参数,但还是需要接收
return f"I didn't know {fruit} is a fruit!"
if __name__ == '__main__':
a = Demo()
print(asyncio.run(a.eat("apple", 'happy')))
print(asyncio.run(a.eat("eggplant", 'happy')))
print(asyncio.run(a.eat("origin", 'happy')))
#----------------------------------
# >>> I love apple,happy
# >>> I didn't know eggplant is a fruit!
# >>> I don't want a origin...
修改后的源码
实际是对源码复制了4份,分别做修改后以应对4钟不同的使用场景。
"""
源码:https://github.com/edgedb/edgedb/blob/master/edb/common/value_dispatch.py
按参数值进行分派 适用于 python < 3.10
python >= 3.10 请直接使用 match case 语句
"""
import functools
def value_dispatch(func):
"""
适用于同步函数
"""
registry = {}
@functools.wraps(func)
def wrapper(arg0, *args, **kwargs):
"""
arg0: 即是调用函数时,传递的那个 选择分组的参数
"""
try:
delegate = registry[arg0]
except KeyError:
pass
else:
# 这里传入arg0参数,需要在定义分组函数时,接收该参数,尽管有时并不会使用该参数
return delegate(arg0, *args, **kwargs)
return func(arg0, *args, **kwargs)
__register_tools(wrapper, registry)
return wrapper
def value_dispatch_async(func):
"""
适用于异步函数
"""
registry = {}
@functools.wraps(func)
async def wrapper(arg0, *args, **kwargs):
"""
arg0: 即是调用函数时,传递的那个 选择分组的参数
"""
try:
delegate = registry[arg0]
except KeyError:
pass
else:
# 这里传入arg0参数,需要在定义分组函数时,接收该参数,尽管有时并不会使用该参数
return await delegate(arg0, *args, **kwargs)
return await func(arg0, *args, **kwargs)
__register_tools(wrapper, registry)
return wrapper
def value_dispatch_class(func):
"""
适用于同步的实例方法
"""
registry = {}
@functools.wraps(func)
def wrapper(self, arg0, *args, **kwargs):
"""
arg0: 即是调用函数时,传递的那个 选择分组的参数
"""
try:
delegate = registry[arg0]
except KeyError:
pass
else:
# 这里传入arg0参数,需要在定义分组函数时,接收该参数,尽管有时并不会使用该参数
return delegate(self, arg0, *args, **kwargs)
return func(self, arg0, *args, **kwargs)
__register_tools(wrapper, registry)
return wrapper
def value_dispatch_class_async(func):
"""
适用于异步的实例方法
"""
registry = {}
@functools.wraps(func)
async def wrapper(self, arg0, *args, **kwargs):
"""
arg0: 即是调用函数时,传递的那个 选择分组的参数
"""
try:
delegate = registry[arg0]
except KeyError:
pass
else:
# 这里传入arg0参数,需要在定义分组函数时,接收该参数,尽管有时并不会使用该参数
return await delegate(self, arg0, *args, **kwargs)
return await func(self, arg0, *args, **kwargs)
__register_tools(wrapper, registry)
return wrapper
def __register_tools(wrapper, registry):
def register(value):
def wrap(func):
if value in registry:
raise ValueError(
f'@value_dispatch: there is already a handler '
f'registered for {value!r}'
)
registry[value] = func
return func
return wrap
def register_for_all(values):
def wrap(func):
for value in values:
if value in registry:
raise ValueError(
f'@value_dispatch: there is already a handler '
f'registered for {value!r}'
)
registry[value] = func
return func
return wrap
wrapper.register = register
wrapper.register_for_all = register_for_all
用装饰器简化大量if-elif代码的更多相关文章
- react-native 中使用redux 优化 Connect 使用装饰器简化代码报错
报错信息 error: bundling failed: Error: The 'decorators' plugin requires a 'decoratorsBeforeExport' opti ...
- 装饰器,栈 ,asyncio 代码
装饰器目的: 不改变原来代码的基础上. 给函数添加新功能动态代理. 拦截器 通用装饰器的写法def wrapper(fn): def inner(*args, **kwargs): '''之前''' ...
- Python迭代器,生成器,装饰器
迭代器 通常来讲从一个对象中依次取出数据,这个过程叫做遍历,这个手段称为迭代(重复执行某一段代码块,并将每一次迭代得到的结果作为下一次迭代的初始值). 可迭代对象(iterable):是指该对象可以被 ...
- python 函数之装饰器,迭代器,生成器
装饰器 了解一点:写代码要遵循开发封闭原则,虽然这个原则是面向对象开发,但也适用于函数式编程,简单的来说,就是已经实现的功能代码不允许被修改但 可以被扩展即: 封闭:已实现功能的代码块 开发:对扩张开 ...
- Python自动化运维之6、函数装饰器
装饰器: 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大.装饰器一般接受一个函数对象作为参数, ...
- Python进阶内容(二)--- 装饰器
谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...
- python函数式编程之装饰器(一)
1.开放封闭原则 简单来说,就是对扩展开放,对修改封闭 在面向对象的编程方式中,经常会定义各种函数. 一个函数的使用分为定义阶段和使用阶段,一个函数定义完成以后,可能会在很多位置被调用 这意味着如果函 ...
- 理解 Python 装饰器看这一篇就够了
讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...
- Python面试题之装饰器漫谈
讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...
- selenium 中装饰器作用
前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间.于是就想是不是可以只打开一 ...
随机推荐
- API方式开发AI应用的三点总结
1. 编程式prompt 让 AI 具备类似程序的运行逻辑.把大模型当CLR使用.与传统的角色扮演提示prompt相比,此方式所需的tokens数量更少,且输出结果的准确性更高 .示例如下: 2. 语 ...
- deepseek:封装一个axios
封装一个包含上传和下载文件功能的 axios 实例,可以提高代码的复用性和可维护性.以下是一个完整的封装示例,支持文件上传.下载.以及常规的 GET/POST 请求. 封装代码 import axio ...
- Mpmath库-学习笔记
目录 mpmath库学习 1. Introduction 1.2 Basic usage of mpmath 1.3 输出格式化 1.4 输出的小数点位数 2. BASIC FEATURES 2.1 ...
- Jupyter Notebook的所有文件ipynb保存下来
前言 如果你想要保存整个 Jupyter Notebook 工作目录,包括所有笔记本和其他相关文件,最直接的方法是将整个文件夹压缩为一个 ZIP 或 TAR 文件. 下载单个文件 压缩文件夹下载 在 ...
- centos 防火墙配置,并限制端口
查看防火墙状态 systemctl status firewalld 如果防火墙处于停止状态,则启动它: systemctl start firewalld 并设置防火墙开机自启: systemctl ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(10)
1.问题描述: 离线推送,锁屏的时候没有弹出消息,只有下拉在通知中心里面显示.请问是否是正常的? 解决方案: 检查一下是否存在图片风控:https://developer.huawei.com/con ...
- 堆排序(内置模块 heapq )(NB)
博客地址:https://www.cnblogs.com/zylyehuo/ # _*_coding:utf-8_*_ import heapq # q->queue 优先队列 import r ...
- SpringBoot前后端接口加解密--解决方案
开放接口 - 通信方式采用HTTP+JSON或消息中间件进行通信. - 调用接口之前需要使用登录鉴权接口获得token. - 当鉴权成功之后才能调用其他接口(携带Token). 登录接口: Code ...
- MySQL-排序相关原理分析
全字段排序和rowId排序 建表语句如下: CREATE TABLE `t` ( `id` int(11) NOT NULL, `city` varchar(16) NOT NULL, `name` ...
- S7.Net与西门子PLC通讯——纯新手必看
前言 本文档适合从未接触过PLC的.NET开发程序员入门查看.(其实看完了之后,PLC开发也就那样) PLC通讯入门比较难,需要关注的细节比较多.一边学习一边举一反三多思考,一定要自己创建Demo跟 ...