一、说明

1.1 关于注解

关于注解这个东西,最早是在大学学java的时候经常会看到某些方法上边@override之类的东西,一方面不知道其作用但另一方面似乎去掉也没什么影响,所以一直都不怎么在意。

今年去看开发的代码也看到很多注解,问其用途基本都和网上类似“为了开启XXX功能我们需要添加@XXX注解的”/”添加@XXX注解是为了开启XXX功能“,不知其原理感觉颇为难受所以自己来研究了一翻。

1.2 关于可变参数

所谓可变参数,最主要就是指传递给被调用函数的参数的个数是不定的。

可变参数应该来说是很常见的,比如C的标准main函数就写成int main(int argc, ** char argv),再比如很常用的print()函数就是最典型的可变参数函数。

但一方面在很长一段时间内并不能理解main函数其实和普通函数没什么区别,另一方面觉得print()是系统函数实现很复杂,所以一直都没弄懂如何实现可变参数应该传递。

1.3 关于注解和可变参数有什么关系

注解和可变参数,在感觉上是没什么关系的。但当我去实现注解,发现要让注解可作用于不同参数个数的函数时需要解决可变参数问题。

而且应当来讲注解作用于不同参数个数的函数是个普遍的需求,所以注解和可变参数关系还是关联很大的。

二、注解代码实现

2.1 被注解函数无参数

# 一个用于进行修饰的函数
# 关键一:外层函数有且只有一个参数,该参数用于承接被修饰函数本身
def decorate_function(need_decorate_function_name):
def decorated_function():
# 关键二:在调用被修饰函数前/后做些其他事情
print("calc staring...")
# 关键点三:原封不动地调用被修饰函数
need_decorate_function_name()
print("calc finished...")
# 关键点四:在最后把修饰完后的函数return回去
return decorated_function # 一个简单的求合函数
@decorate_function
def calc_sum():
a = 1
b = 2
sum_value = a + b
print(f"{a} + {b} = {sum_value}") if __name__ == "__main__":
calc_sum()

最终执行结果如下:

2.2 被注解函数有参数

# 一个用于进行修饰的函数
def decorate_function(need_decorate_function_name):
# 关键点一:内层函数使用和被修饰函数完全一样的参数去承接即可
# 当然参数名一不一样本来其实无所谓,但为了省事全都一样即可
def decorated_function(a, b):
print("calc staring...")
# 关键点二:内层函数将接收到的参数又再原封不动地传给被修饰函数即可
need_decorate_function_name(a, b)
print("calc finished...")
return decorated_function # 一个简单的求合函数
@decorate_function
def calc_sum(a, b):
sum_value = a + b
print(f"{a} + {b} = {sum_value}") if __name__ == "__main__":
calc_sum(1, 2)

最终执行结果如下:

2.3 被注解函数有返回值

# 一个用于进行修饰的函数
def decorate_function(need_decorate_function_name):
def decorated_function(a, b):
print("calc staring...")
# 关键点一:承接好被修饰函数的返回值
result = need_decorate_function_name(a, b)
print("calc finished...")
# 关键点二:在末尾将被修饰函数的返回值原封不动地向上层返回
return result
return decorated_function # 一个简单的求合函数
@decorate_function
def calc_sum(a, b):
sum_value = a + b
return sum_value if __name__ == "__main__":
a = 1
b = 2
sum_value = calc_sum(a, b)
print(f"{a} + {b} = {sum_value}")

执行结果如下:

2.4 被注解函数有多个且它们的参数个数不一致

# 一个用于进行修饰的函数
def decorate_function(need_decorate_function_name):
# 关键点一:使用*args, **kwargs承接所有参数
def decorated_function(*args, **kwargs):
print("calc staring...")
# 关键点二:一模一样地直接把*args, **kwargs传给被修饰函数即可
result = need_decorate_function_name(*args, **kwargs)
print("calc finished...")
return result
return decorated_function # 一个简单的求合函数
@decorate_function
def calc_sum_2(a, b):
sum_value = a + b
return sum_value # 一个简单的求合函数
@decorate_function
def calc_sum_3(a, b, c):
sum_value = a + b + c
return sum_value if __name__ == "__main__":
a = 1
b = 2
c = 3
sum_value_2 = calc_sum_2(a, b)
print(f"{a} + {b} = {sum_value_2}")
sum_value_3 = calc_sum_3(a, b, c)
print(f"{a} + {b} + {c} = {sum_value_3}")

执行结果如下:

2.5 在类内使用注解

class Test:
# 一个用于进行修饰的函数
# 关键点一:坚定不移地认同,外层函数有且只有一个参数,该参数用于承接被修饰函数
def decorate_function(need_decorate_function_name):
# 关键点二:坚定不移地认同*args, **kwargs可以承接所有参数,包括self在内
def decorated_function(*args, **kwargs):
print("calc staring...")
# 关键点三:坚定不移地认同*args, **kwargs可以把所有参数传给被修饰函数,包括self在内
result = need_decorate_function_name(*args, **kwargs)
print("calc finished...")
return result
return decorated_function # 一个简单的求合函数
@decorate_function
def calc_sum_2(self, a, b):
sum_value = a + b
return sum_value # 一个简单的求合函数
@decorate_function
def calc_sum_3(self, a, b, c):
sum_value = a + b + c
return sum_value if __name__ == "__main__":
obj = Test()
a = 1
b = 2
c = 3
sum_value_2 = obj.calc_sum_2(a, b)
print(f"{a} + {b} = {sum_value_2}")
sum_value_3 = obj.calc_sum_3(a, b, c)
print(f"{a} + {b} + {c} = {sum_value_3}")

执行结果如下:

三、可变参数实现本质

python中调用函数时,传递参数有两种方式,一种是以位置形式进行传递(如test(a)),一种是以“k=v”的的形式进行传递(如test(a=1))。同样的“k=v”形式必须位于位置参数之后。

(另外,python中定义一个函数其参数有类似的两种形式,一种是没有默认值的参数(位置参数,如def test(a)),一种是有默认值的参数(默认参数,def test(a=1))。另外默认参数必须处于位置参数之后。但一是我们这里参数传递并不需要关心函数定义时参数的形式)

使用的演示程序如下:

# 一个简单的求合函数
def calc_sum(a, b, c, d, e):
sum_value = a + b + c + d + e
return sum_value # 此函数只单纯调用calc_sum()
def call_calc_sum(a,*args,**kwargs):
sum_value = calc_sum(a,*args,**kwargs)
return sum_value call_calc_sum(1, 2, 3, e=4, d=5)

3.1 从参数变为*args, **kwargs的过程

被调用函数通过以下步骤提取参数:

第一步,如果前面有非*args, **kwargs的参数,则在传来的参数中先分配给他。比如这里在*args前面有a,所以就把第一个参数值1赋给a。

第二步,将其他非k=v形式的参数,组成元组赋值为args。比如这是把下来的2,3组成(2,3)。

第三步,将其他的k=v形式的参数,组成字典赋值给kwargs。比如这里把e=4,d=4组成['e': 4, 'd': 5]。

3.2 从*args, **kwargs变回具体参数的过程

被调用函数通过以下步骤提取参数:

第一步,如果开头有非*args, **kwargs的参数,则将按正常参数解析。如1赋给第一个参数a。

第二步,将元组参数按顺序分发给接下来的参数。如将2赋给下来的第二个参数b,再将3赋给下来的第三个参数c。

第三步,将字典参数,按设置的k/v分发给对应的参数。如按e=4赋给第五个参数e,按d=5赋值给第四个参数d。

Python3注解+可变参数实现的更多相关文章

  1. JAVA基础学习之IP简述使用、反射、正则表达式操作、网络爬虫、可变参数、了解和入门注解的应用、使用Eclipse的Debug功能(7)

    1.IP简述使用//获取本地主机ip地址对象.InetAddress ip = InetAddress.getLocalHost();//获取其他主机的ip地址对象.ip = InetAddress. ...

  2. Python3 系列之 可变参数和关键字参数

    刚开始接触 python 的时候,对 python 中的 *wargs (可变参数) 和 **kwargs (关键字参数)的理解不是很透彻,看了一下 <Explore Python>一书, ...

  3. python3 关键字和可变参数笔记

    """普及一下字典的知识""" # dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First' ...

  4. python 可变参数

    原文地址:http://docs.pythontab.com/python/python3.4/controlflow.html#tut-functions 一个最不常用的选择是可以让函数调用可变个数 ...

  5. Effective Java 第三版——32.合理地结合泛型和可变参数

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. 关于Retrofit网络请求URL中含有可变参数的处理

    开题:在此默认各位看官对Retrofit.以及Okhttp已经有过一定的了解及应用,所以今天我们不谈基础入门的东西,今天我们谈在Retrofit请求接口管理类中URL参数含有动态参数的处理方式.一般我 ...

  7. 第六天python3 函数、参数及参数解构(一)

    函数 作用:结构话编程对代码的最基本的封装,一般按照功能组织一段代码,封装的目的是为了复用,减少冗余代码,代码更加简洁.美观,可读易懂: 分类: 内建函数,如max() reversed()等 库函数 ...

  8. C可变参数的函数

    我们实现一个简单的printf函数(可变参数) #include <stdio.h> #include <stdarg.h> void myprintf(const char ...

  9. c#编程基础之函数可变参数

    可变参数:int sum (params int[] values)int sum (string name,params int[] values) 注意:params参数必须是形参表中的最后一个参 ...

随机推荐

  1. tengine 基于权重负载均衡的简单配置

    环境如下: 资源服务器_1:192.168.10.10  centos 7  tengine 2.3.0 资源服务器_2:192.168.10.129  centos 7  tengine  2.3. ...

  2. Java 之 Stack 集合

    一.Stack:栈 概述 栈是一种先进后出(FILO)或后进先出(LIFO:Last in first out)的数据结构. Stack是Vector的子类,比Vector多了几个方法,它的后进先出的 ...

  3. 英语cowbezoar牛黄cowbezoar单词

    牛黄(cowbezoar)是脊索动物门哺乳纲牛科动物牛胆囊的胆结石.在胆囊中产生的称“胆黄”或“蛋黄”,在胆管中产生的称“管黄”,在肝管中产生的称“肝黄”. 中文学名牛黄 拉丁学名Bos taurus ...

  4. cs1.6 8倍镜

    地图名字包含awp的是狙击场 打开游戏 搜索方法 开镜搜减小,关镜搜增加 分析数据 开关镜,观察数据变动 修改测试 测试结果 类似的:鼠标XY坐标,也是上减下加,左加右减

  5. 第一篇 Scrum 冲刺博客

    一.各个成员在 Alpha 阶段认领的任务 姓名 Alpha 阶段认领的任务 林剑峰 用户信息页面:完成用户信息的上传 石竞贤 发布信息页面:完成用户图片上传云存储的功能,并且把发布信息上传到云数据库 ...

  6. OL8.0静默安装Oracle 19C

    首先在edelivery中下载Oracle Linux 8.0 然后就默认安装系统 环境准备工具目前不支持OL8,所以需要手动安装,首先设置内核参数,在/etc/sysctl.conf追加 [root ...

  7. "轻"量级 Java Web 服务框架漫谈

    博文太长了, 还是先说下概要: 框架"轻量"与否可以从两方面来看待: 1) 框架本身的体量 - 例如小 jar 无依赖的苗条框架; 2) 用户使用框架是否获得各种便利而无阻隔(&q ...

  8. Nginx 高级配置-状态页配置

    Nginx 高级配置-状态页配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 建议将nginx的监控状态的值通过zabbix或者Open-Falcon之类的监控工具来监控状态,并 ...

  9. Python 和 R 中的一数多图

    R # 一数多图 x <- 2:6 y <- 7:3 y1 <- y +2 opar <- par(no.readonly = TRUE) par(mfrow=c(2, 3)) ...

  10. Java实现Redis的消息订阅和发布

    1.  首先需要一个消息监听器类 package com.sogou.baike.testimport.testSubscribe; import redis.clients.jedis.JedisP ...