【python小技巧2】星号(*)和斜杠(/)的妙用
小倪同学0v0
编辑于 2023年01月05日 15:48
收录于文集
共6篇

先来自测一下:

代码块
text/x-python
自动换行
复制代码
a = 3 * 2          # 太简单了
b = 3 ** 2         # 还是太简单了
c = 2 * 2 ** 2 ** 3  # 注意细节!极易出错!


def func(x, /, y, *, z, **kwargs):  # 这个函数的参数什么意思?
  pass


d, *e, f = range(10)  # 问 e 的类型是什么?值是什么?
g = *range(10), 50  # g 的值是什么?

h = {'age': 18,   'name': 'XN'}
i = {'job': 'stu', 'net_name': '小倪同学0v0'}
j = {**h, **i}  # 这里在干嘛?

*k, (l, m) = (1, 2, (3, 4))
*k, l, m = (1, 2, (3, 4))  # 这两个语句中 k, l, m 的值分别是什么?
复制成功

答案:

代码块
text/x-python
自动换行
复制代码
a == 6
b == 9
c == 512

d == 0
e == [1, 2, 3, 4, 5, 6, 7, 8]  # e 的类型是列表
f == 9
g == (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 50)

j == {'age': 18, 'name': 'XN', 'job': 'stu', 'net_name': '小倪同学0v0'}  # 在做字典合并

# 倒数第二行
k == [1, 2]
l == 3
m == 4
# 最后一行
k == [1]
l == 2
m == (3, 4)
复制成功

如果你感到犹豫,请接着往下看吧。

一、星号的用途

  1. a * b 代表乘法,如 3 * 2 得到 6

  2. a ** b 代表乘方,如 3 ** 2 得到 9

    x ** y <==> pow(x, y)        x ** y % z <==> pow(x, y, z)

    细节:先算乘方后算乘法,且同为乘法从左向右,同为乘方从右向左。所以开头的 c 的值是 512 而不是 128,更不是 65536

  3. 函数定义时:

    1. *args 这种写法,称为可变长位置参数。

      额外的位置参数将“吸入”这个args,args是一个元组。

    2. **kwargs 这种写法,称为可变长关键字参数。

      额外的关键字参数将以键值对的形式“吸入”这个kwargs中,kwargs是一个字典。

      注意:可变长关键字参数 和 可变长位置参数都至多存在一个

      以上两种参数,统称可变长参数

      且 *args 必须在 **kwargs 前面

    3. def f(a, *, b): pass这种写法

      将后面的所有参数设置为 keyword only argument,即只能用关键字传参。

      这里的 a 两种传参都可以,而 b 只能使用关键字传参。为什么?因为这个 * 虽然没有参数名字,但它将所有额外的位置参数都“吸入”这个 * 里,所以 b 只能用关键字传参

  4. 序列解包 / 拆包

    1. *args 将一个序列展开

    2. **kwargs 将一个字典以键值对形式展开(其实不止字典,任何 mapping 都可以,常见的还有 collections.defaultdict, collections.OrderedDict 以及 collections.Counter 等)

      e.g.

代码块
text/x-python
自动换行
复制代码
def add(x, y):
  return x + y


nums_tuple = (2, 3)
add(nums_tuple[0], nums_tuple[1])  # 传统做法,返回 5
add(*nums_tuple)                   # 一个星号序列解包,返回 5

nums_dict = {'x': 2, 'y': 3}
add(x=nums_dict['x'], y=nums_dict['y'])  # 传统做法,返回 5
add(**nums_dict)                         # 两个星号关键字解包,返回 5
复制成功

            不仅可以在函数调用时解包传参,也可用在赋值语句等。e.g.

代码块
text/x-python
自动换行
复制代码
a = *range(10), 50
# 把 range(10) 看成列表的话是 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 把它解包,在合并上后面的 50
# 得到 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 50)
# 注意,因为没加括号,默认是元组

a = [*range(10), 50]
# 但要是写成这样,就成了 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 50]
复制成功

            也可写在左值,用于拆包哦

代码块
text/x-python
自动换行
复制代码
a, *b, c = range(5)
# 这个例子中,range(5) 先解包成 (0, 1, 2, 3, 4)
# a 获取到一个 0
# 这个 b “能屈能伸”,先让 c 得到一个 4
# b 再把剩下的都吸入囊中,即 [1, 2, 3]
# (其实储存顺序是从前向后,这样说方便理解)
复制成功

            注意: 这样写出来的 b 是一个列表,不是元组

    5. 正则表达式

        匹配前面的子表达式 0 次或多次

二、斜杠的用途

  1. a / b 代表 truediv,emmm...将其译为真除吧(网上没找到更好的翻译),返回浮点数,如 11 / 2 得到 5.5

  2. a // b 代表floordiv,即整除,返回整数,如 11 // 2 得到 5

  3. 函数定义时,斜杠前面的参数成为 positional only argument,只能使用位置传参,不能使用关键字。e.g.

代码块
text/x-python
自动换行
复制代码
def add(a, b=0, /):
  return a + b

# 可以这样调用
add(2)     # 2
add(2, 1)  # 3

# 不能这样调用
add(a=2)     # TypeError
add(2, b=1)  # TypeError
复制成功

总结:

  1. * 用于乘法、乘方等;也用于序列解包拆包;函数定义时作可变长参数标志,将其后面的参数设置为仅关键字参数

  2. / 用于 2 种除法;函数定义时将其前面的参数设置为仅位置参数

END

参考资料:

https://docs.python.org/3.8/tutorial/controlflow.html

以上内容如有错误,欢迎指出!