当前位置: 首页 > news >正文

14、Python之super star:一颗星、两颗星,满天都是小星星

引言

关于Python系列的文章,已经通过两篇文章,介绍了Python中关于函数的简单使用,包括为什么要使用函数,以及函数中带默认值参数的使用注意事项。

之后,岔开函数的主题,通过几篇番外篇,重点谈了一下Python中一切皆对象、Python的数据模型,以及Python中函数和类也是一等公民的理念。其实,关于函数也一直有所涉及。

今天,准备结合Python中星号(star)操作符的用法,继续展开Python中函数的介绍。

关于星号(*)的使用,主要内容有:

1、基础的乘法运算
2、字符串的重复
3、列表的扩充
4、定义不定长的函数位置参数
5、函数调用时,将列表拆包为位置参数进行传递
6、定义不定长的函数关键字参数
7、函数调用时,将字典拆包为关键字参数进行传递

python中的乘法运算

*号是所有编程语言中,比较常见的操作符,首先能够想到的就是四则运算中的乘法运算。没错,首次接触编程语言的新手,可能在键盘上找不到表示乘法运算的x。在所有编程语言中,都是使用*的。

# *的基础作用,乘法运算:
a = 10
b = 5
print(f"{a}乘以{b}的结果是:{a * b}")# Python中也支持复合赋值的操作:
# 等价于 b = b * 100
b *= 100
print(b)

执行结果:

除了数字可以进行乘法运算,Python中将乘法运算进行了扩充:
字符串可以与数字进行相乘,进行字符串的重复

# 书读百遍,其义自现;百遍太多,十遍也行
s1 = 'Life is short, I use Pyton\n'
s2 = s1 * 10
print(s2)

执行结果:

列表也可以进行乘法运算的扩充:

# 一个不过瘾,我要打十个
enemies = ['小喽啰']
print(id(enemies))
enemies *= 10
print(id(enemies))
print(enemies)

执行结果:


这里的复合赋值操作,实际上进行了原列表对象的扩充,而非重新构造一个列表对象。

自动打包

在前面的文章中,介绍Python的unpacking机制时,已经用到过*号,将不需要的元素进行装包,这里简单回顾一下:

# * unpacking
persons = [('张三', 18, 190, '女'), ('小红', 23, 165, '男')]
for p in persons:name, *others, gender = pprint(others)print(type(others))print(f"姓名: {name}, 性别: {gender}")

还是,之前的代码,我们通过*号修饰others变量,从而将除了name、gender接收的元素外,统一打包为一个列表,交由others变量来进行接收:

函数中的位置参数

相较于其他编程语言中的函数调用,形参列表和实参列表必须一一对应,按顺序传递(默认值参数除外)。Python中将必须按照顺序一一传递的函数调用传参,称为位置参数:

a = 10
b = 5
result = divide(a, b)
print(f"{a}除以{b}的结果是:{result}")

我们很容易碰到的一个问题是,如何定义一个函数,可以接收不定数目的参数,比如以下实际场景:
我们如何定义一个函数,可以计算若干个数字的和?
在其他编程语言中,可以通过函数重载来实现,可以定义多个同名的函数,但是参数列表的参数类型、个数不同。
但是,Python中似乎不支持函数重载。

很容易想到的一个变通的解法是我们定义一个接收一个列表作为参数的函数,列表中有几个元素,就是对几个元素求和:

# 定义一个接收列表的函数,从而变通地实现不定数目的参数求和
def my_sum(args):result = 0for num in args:result += numreturn resultnums = [1, 2, 3, 4, 5]
print(my_sum(nums))

这样,确实能够实现不定长参数的问题,但是,每次调用函数,都必须包装成一个列表,然后才能进行传参,似乎有点不太方便。

不定长位置参数

其实,Python中有更好的解决方案:通过*号,让函数接收数量可变的位置参数,可以让函数的设计、使用更加清晰。

这些位置参数通常简称为varargs,或者叫作star args,因为在Python中,我们习惯于使用*args来进行数量可变的位置参数的定义(注意,只是习惯,可以是别的命名方式)。

接下来,还是关于数字求和的问题,我们看star args的函数定义方式:

# 定义star args的函数,实现不定数目的参数求和
def my_sum(*args):result = 0for num in args:result += numreturn result# 函数调用
print(my_sum(1, 2, 3, 4, 5))

可以发现,只需要将原来的函数定义的地方,参数前面加个*就可以解决这个问题了。

函数的调用方式,变成了直接传具体的数字就行了,有多少传多少,不需要包装成列表对象了。

如果由于历史原因,已经将参数包装成列表了,怎么办呢,也可以通过调用时加*进行unpacking处理:

# 定义star args的函数,实现不定数目的参数求和
def my_sum(*args):result = 0for num in args:result += numreturn result# 列表调用
nums = [1, 2, 3, 4, 5]
print(my_sum(*nums))

不定长的star args,也可以跟固定的位置参数混合使用:

# 定义star args的函数,实现不定数目的参数求和
def my_sum(initial_value, *args):result = initial_valuefor num in args:result += numreturn result# 列表调用
nums = [1, 2, 3, 4, 5]
print(my_sum(100, *nums))

有些教材中,描述说固定的位置参数必须放在star args之前进行定义,这种说法其实是不严谨的。

这两种参数,可以理解为一个是必选的参数,另一个是可选的参数。由于都是按照位置来传,就没法区分,哪个是由必选参数接收,哪个又是由可选参数接收了。

Python中,通过关键字参数机制来避免位置参数传参的二义性。

函数中的关键字参数

由于通过位置参数进行传参,必须顺序一一对应,稍不留神,可能就会导致错误,而且后续函数扩展,增加新的位置参数,所有调用的地方都要按照顺序调整……

好在Python中除了位置参数的函数调用方式,还可以通过关键字进行传参,可以避免位置错乱的问题,回到前面的除法的函数:

# 简单的关键字参数调用演示
def divide(a, b):return a / b# 我们都一样
print(divide(10, 5))
print(divide(10, b=5))
print(divide(a=10, b=5))
print(divide(b=5, a=10))

回到上面关于数量不定的位置参数,并不是star args后面,就不能定义必选参数了,只是调用时需要使用关键字参数进行调用而已。

所以,Python中关于函数参数定义的规定是:在star args之后定义的参数,都是关键字参数,调用的时候,只能使用关键字参数的方式进行参数传递:

# 定义star args的函数,实现不定数目的参数求和
def my_sum(*args, initial_value):result = initial_valuefor num in args:result += numreturn result# 列表调用
nums = [1, 2, 3, 4, 5]
# 这种定义方式会报错,可以清晰地看到不定长位置参数与关键字参数混用的限制
print(my_sum(*nums, 100))

运行报错:


必须以关键字参数的形式传递initial_value这个参数。正确的调用方式:

print(my_sum(*nums, initial_value=100))
print(my_sum(initial_value=100, *nums))

从Python 3.8开始,哪怕函数不需要不定长的位置参数,也可以在函数定义中,使用*号,来强制要求之后的参数必须作为关键字参数进行传参:

# Python3.8开始的,*使用def star_func(a, b, *, c, d):print(f"a: {a}, b: {b}, c: {c}, d:{d}")# 报错 TypeError: star_func() takes 2 positional arguments but 4 were given
# star_func(1, 2, 3, 4)# 报错 TypeError: star_func() missing 2 required keyword-only arguments: 'c' and 'd'
# star_func(1, 2)# 正确调用方式:
star_func(1, 2, d=10, c=5)

不定长关键字参数

位置参数可以不定数目,实现函数的一次定义,灵活调用。关键字参数,其实也是可以的。这就是要介绍的两颗星的使用了(**)

在Python函数定义中的两个习惯:

  • *args:进行不定长的位置参数的定义
  • **kwargs:进行不定长的关键字参数的定义

再次说明,只是习惯,args、kwargs的参数名是可以随意的。

以一个简单的示例说明下不定长关键字参数的定义:

def double_star_func(**kwargs):print("传递的关键字参数有:")for key, value in kwargs.items():print(f"key: {key}, value: {value}")double_star_func(a=1, b=2, c='hello python')
double_star_func(host='127.0.0.1', port=3306, user='deploy', database='test')

此外,如同我们可以通过列表来进行位置参数的传递,关键字参数,我们可以通过字典对象进行传递。

通过将我们已有的存储在字典中的配置信息,以关键字参数的方式进行传递,可以极大地简化函数的调用。

比如,有如下场景,我们需要进行MySQL的数据库连接,我们以字典对象的方式存储了多个数据库的连接配置信息:

第一种连接数据库的函数调用方式:

import pymysqldb_configs = {'dev': {'host': '127.0.0.01','port': 3306,'user': 'dev_user','password': 'dev_password','database': 'db_dev'},'test': {'host': '127.0.0.01','port': 3306,'user': 'test_user','password': 'test_password','database': 'db_test'},'prod': {'host': '127.0.0.01','port': 3306,'user': 'prod_user','password': 'prod_password','database': 'db_prod'}
}dev_db = db_configs['dev']
db_conn = pymysql.connect(host=dev_db['host'], port=dev_db['port'], user=dev_db['user'], password=dev_db['password'], database=dev_db['database'])

有点麻烦……
第二种连接方式:我们看是用字典传参的方式:

import pymysqldb_configs = {'dev': {'host': '127.0.0.01','port': 3306,'user': 'dev_user','password': 'dev_password','database': 'db_dev'},'test': {'host': '127.0.0.01','port': 3306,'user': 'test_user','password': 'test_password','database': 'db_test'},'prod': {'host': '127.0.0.01','port': 3306,'user': 'prod_user','password': 'prod_password','database': 'db_prod'}
}db_conn = pymysql.connect(**db_configs['dev'])

在函数调用时,可以通过两个星号(**)来将字典解析为函数的关键字参数进行传递,从而简化函数的调用。

总结

在Python中,星号(*)的主要用法有:

1、基础的乘法运算
2、字符串的重复
3、列表的扩充
4、定义不定长的函数位置参数
5、函数调用时,将列表拆包为位置参数进行传递
6、定义不定长的函数关键字参数
7、函数调用时,将字典拆包为关键字参数进行传递

只要理解了这几点,在后续的使用中,就能大大简化自己代码的编写,真正提高代码的编写效率,毕竟我们选择用Python的原因在于人生苦短。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Rust 版本升级:rustup update stable 报错
  • 2300. 咒语和药水的成功对数
  • BUUCTF逆向wp [MRCTF2020]Transform
  • 【Linux】多线程_7
  • Spring解决循环依赖:三级缓存
  • 17-3 向量数据库之野望3 - SingleStoreDB 实践教程
  • MongoDB教程(六):mongoDB复制副本集
  • ant design form动态增减表单项Form.List如何进行动态校验规则
  • AI安全系列——[第五空间 2022]AI(持续更新)
  • 使用 Apache Pulsar 构建弹性可扩展的事件驱动应用
  • 【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十)-服务体验保证的用例
  • 用虚拟机,可以在x86的电脑上虚拟出arm的电脑吗
  • 【轻松拿捏】Java-final关键字(面试)
  • Jmeter-单用户单表查询千条以上数据,前端页面分页怎么做
  • 【Git 学习笔记】第四章 git rebase 变基操作与相关示例(上)
  • JavaScript 如何正确处理 Unicode 编码问题!
  • Create React App 使用
  • echarts花样作死的坑
  • ESLint简单操作
  • Java基本数据类型之Number
  • js操作时间(持续更新)
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • Python3爬取英雄联盟英雄皮肤大图
  • Vue UI框架库开发介绍
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • Webpack 4 学习01(基础配置)
  • 分布式任务队列Celery
  • 关于Java中分层中遇到的一些问题
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 前端面试总结(at, md)
  • 区块链共识机制优缺点对比都是什么
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 用Python写一份独特的元宵节祝福
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • Mac 上flink的安装与启动
  • 积累各种好的链接
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #、%和$符号在OGNL表达式中经常出现
  • #HarmonyOS:基础语法
  • #pragma pack(1)
  • $jQuery 重写Alert样式方法
  • (bean配置类的注解开发)学习Spring的第十三天
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (floyd+补集) poj 3275
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (二)PySpark3:SparkSQL编程
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (四)软件性能测试