源码地址: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代码的更多相关文章

  1. react-native 中使用redux 优化 Connect 使用装饰器简化代码报错

    报错信息 error: bundling failed: Error: The 'decorators' plugin requires a 'decoratorsBeforeExport' opti ...

  2. 装饰器,栈 ,asyncio 代码

    装饰器目的: 不改变原来代码的基础上. 给函数添加新功能动态代理. 拦截器 通用装饰器的写法def wrapper(fn): def inner(*args, **kwargs): '''之前''' ...

  3. Python迭代器,生成器,装饰器

    迭代器 通常来讲从一个对象中依次取出数据,这个过程叫做遍历,这个手段称为迭代(重复执行某一段代码块,并将每一次迭代得到的结果作为下一次迭代的初始值). 可迭代对象(iterable):是指该对象可以被 ...

  4. python 函数之装饰器,迭代器,生成器

    装饰器 了解一点:写代码要遵循开发封闭原则,虽然这个原则是面向对象开发,但也适用于函数式编程,简单的来说,就是已经实现的功能代码不允许被修改但 可以被扩展即: 封闭:已实现功能的代码块 开发:对扩张开 ...

  5. Python自动化运维之6、函数装饰器

    装饰器: 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大.装饰器一般接受一个函数对象作为参数, ...

  6. Python进阶内容(二)--- 装饰器

    谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...

  7. python函数式编程之装饰器(一)

    1.开放封闭原则 简单来说,就是对扩展开放,对修改封闭 在面向对象的编程方式中,经常会定义各种函数. 一个函数的使用分为定义阶段和使用阶段,一个函数定义完成以后,可能会在很多位置被调用 这意味着如果函 ...

  8. 理解 Python 装饰器看这一篇就够了

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

  9. Python面试题之装饰器漫谈

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

  10. selenium 中装饰器作用

    前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间.于是就想是不是可以只打开一 ...

随机推荐

  1. 使用 kubeadm 创建高可用 Kubernetes 及外部 etcd 集群

    博客链接:使用 kubeadm 创建高可用 Kubernetes 及外部 etcd 集群 前言 Kubernetes 的官方中文文档内容全面,表达清晰,有大量示例和解析 无论任何情况下都推荐先花几个小 ...

  2. Ubuntu 22.04 添加 AppImage 到应用程序

    前言 AppImage 逐渐成为 Linux 常用的一种软件包格式,本文将介绍如何将 AppImage 文件添加到 Ubuntu 的应用程序中. 如下图中的 CAJViewer : 操作过程 设置相关 ...

  3. 【文献阅读】 PVDF &阻尼&有限元建模

    1. 压电Damper原理 Piezoelectric Composite Materials - ScienceDirect 当振动传递到压电材料时,振动能量通过压电效应转化为电能,产生交流电压.所 ...

  4. svn提示Node remains in conflict的解决办法

    svn 更新提示Node remains in conflict 这个时候不管svn up多少次,都无法更新到最新的内容 解决办法: svn revert --depth=infinity * 其中* ...

  5. Web前端入门第 9 问:HTML 块级元素,内联块元素,内联元素三者有什么区别?

    HELLO,这里是大熊学习前端开发的入门笔记. 本系列笔记基于 windows 系统. HTML 中的元素根据其默认的 显示类型 主要分为三类:块级元素.内联元素 和 内联块元素. 它们的核心区别在于 ...

  6. ollama系列1:轻松3步本地部署deepseek,普通电脑可用

    本文主要介绍如何通过ollama快速部署deepseek.qwq.llama3.gemma3等大模型,网速好的小伙伴10分钟就能搞定.让你摆脱GPU焦虑,在普通电脑上面玩转大模型. 安装指南(无废话版 ...

  7. linux ln命令详解

    介绍 ln是linux的一个重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在 ...

  8. CSAPP学习笔记——chapter8 异常控制流

    CSAPP学习笔记--chapter8 异常控制流 简介 异常控制流(Exceptional Control Flow,ECF)是在计算机系统中处理不寻常或异常情况的一种机制.它允许系统跳出正常的顺序 ...

  9. final关键字、Object类--java进阶day01

    1.规则 被final修饰的变量,名称都要大写,多单词的名称则需_来分隔 1.修饰方法 method方法已经不能被重写了,因为修饰该方法的是final 2.修饰类 当一个类中所有的成员方法都不想被重写 ...

  10. ASP.NET 日志路径

    默认路径 protected void Button_StreamWrite_Click(object sender, EventArgs e) {     StreamWriter sw = new ...