社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【译】Python初学者应该了解的星号(*)

老齐Py • 6 年前 • 497 次点击  
阅读 21

【译】Python初学者应该了解的星号(*)

作者:mingrammer

翻译:老齐

与本文相关的图书推荐:《Python大学实用教程》

本书各大电商平台有售


与其他编程语言相比,Python语言的操作类型更多样化。

特别是星号(*),在Python中是一个用途广泛的操作符,而不仅仅用于两个数字相乘的运算之中。在本文中,我们将讨论星号的多种用途。

这里总结了4种星号的应用场景:

  • 作为乘法和乘方的运算符
  • 表示序列中元素的重复
  • 用于收集参数(可以称之为“打包”)
  • 用于容器类对象的解包

下面逐一进行说明。

乘法或乘方的运算符

对此你一定不陌生,像乘法一样,Python中也内置了乘方运算符。

>>> 2 * 3 
6 
>>> 2 ** 3 
8 
>>> 1.414 * 1.414 
1.9993959999999997
>>> 1.414 ** 1.414
1.6320575353248798
复制代码

重复类列表的容器元素

Python也支持类列表的容器类对象(即序列)与整数相乘,即为按照整数实现重复其中的元素数量。

# Initialize the zero-valued list with 100 length
zeros_list = [0] * 100 

# Declare the zero-valued tuple with 100 length
zeros_tuple = (0,) * 100  

# Extending the "vector_list" by 3 times
vector_list = [[1, 2, 3]] 
for i, vector in enumerate(vector_list * 3):     
    print("{0} scalar product of vector: {1}".format((i + 1), [(i + 1) * e for e in vector]))
# 1 scalar product of vector: [1, 2, 3]
# 2 scalar product of vector: [2, 4, 6]
# 3 scalar product of vector: [3, 6, 9]
复制代码

参数收集

很多函数中,都会有不确定个数的参数。例如,如果我们不知道要提供多少个参数,或者因为什么原因必须传任意个参数等。

在Python中有两类参数,一类是位置参数,另外一类是关键词参数,前者根据位置确定相应值,后者则是依据参数名称确定。

在研究任意个位置/关键词参数之前,先讨论确定数量的位置参数和关键词参数。

# A function that shows the results of running competitions consisting of 2 to 4 runners.
def save_ranking(first, second, third=None, fourth=None):
    rank = {}
    rank[1], rank[2] = first, second
    rank[3] = third if third is not None else 'Nobody'
    rank[4] = fourth if fourth is not None else 'Nobody'
    print(rank)    

# Pass the 2 positional arguments
save_ranking('ming', 'alice')
# Pass the 2 positional arguments and 1 keyword argument
save_ranking('alice', 'ming', third='mike')
# Pass the 2 positional arguments and 2 keyword arguments (But, one of them was passed as like positional argument)
save_ranking('alice', 'ming', 'mike', fourth='jim')
复制代码

上述代码中的函数有2个位置参数:firstsecond,2个关键词参数:thirdfourth。位置参数不能省略,必须给所有的位置参数按照其正确的位置传值。然而,对于关键词参数,在定义函数的时候你可以设置默认值,如果调用函数的时候省略了相应的实参,会以默认值作为实参,即关键词参数可以省略。

如你所见,关键词参数可以省略,所以,它们就不能在未知参数前面进行声明,如果按照下面的方式声明参数,就必然抛出异常。

def save_ranking(first, second=None, third, fourth=None):
    ...
复制代码

但是,在save_ranking('alice', 'ming', 'mike', fourth='jim')调用中,提供了3个位置实参和一个关键词参数。是的,对于关键词参数,你也可以按照位置参数的方式传值,所对应的关键词能够接受依据位置所传的数据。按照此处的调用方法,'mike'就自动传给了third

以上我们已经讨论了参数的基本含义,从上述示例中,我们也能看出来,上面所定义的函数不能接收任意个数的参数,因为该函数的参数是固定数量的。因此,需要对该函数进行改造,让它能够接收任意个参数,不论是位置参数还是关键词参数。看下面的示例:

收集位置参数

def save_ranking(*args):
    print(args) 
save_ranking('ming', 'alice', 'tom', 'wilson', 'roy')
# ('ming', 'alice', 'tom', 'wilson', 'roy')
复制代码

收集位置参数和关键词参数

def save_ranking(*args, **kwargs):
    print(args)     
    print(kwargs)
save_ranking('ming', 'alice', 'tom', fourth='wilson', fifth='roy')
# ('ming', 'alice', 'tom')
# {'fourth': 'wilson', 'fifth': 'roy'}
复制代码

在上面的示例中,*args意味着收集任意个数的位置参数,**kwargs意味着收集任意个数的关键词参数。这里的*args**kwargs可以称之为打包。

如你所见,在上面我们按照位置或关键词传了任意个数的参数。按照位置传的参数被收集到元组中,并用变量args引用;以关键词传的参数则用变量kwargs引用为字典类型。

前面提到过,关键词参数不能写在位置参数前面,所以,下面的定义方式是错误的:

def save_ranking(**kwargs, *args):
    ...
复制代码

任意个数的参数很有价值,在很多开源项目中都能够看到,一般都是用*args或者**kwargs作为收集任意参数的名称,当然,你可以用其他名称,比如*requeired或者**optional等,都可以。只是对于开源项目而言,我们习惯使用*args**kwargs罢了。

解包

星号还可以用于对容器的解包,这与前面的参数收集类似,比如,有一个包含数据的列表、元组或者字典,还有一个收集任意参数的函数:

from functools import reduce

primes = [2, 3, 5, 7, 11, 13]

def product(*numbers):
    p = reduce(lambda x, y: x * y, numbers)
    return p 

product(*primes)
# 30030

product(primes)
# [2, 3, 5, 7, 11, 13]
复制代码

因为product()能接收任意参数,我们本来需要将列表中的元素取出来,然后传给此函数。但在这里,如果以*primes的方式向函数提供primes列表数据,则primes所引用的列表会被解包,其中的每个素数都被传给函数,并被收集后用变量numbers引用。如果传该列表primes给函数,就不能解包,numbers所引用的元组中只有一个primes列表。

对于元组也如此,对于字典,需要用**代替*

headers = {
    'Accept': 'text/plain',
    'Content-Length': 348, 
    'Host': 'http://mingrammer.com' 
}  

def pre_process(**headers): 
    content_length = headers['Content-Length'] 
    print('content length: ', content_length) 
    
    host = headers['Host']
    if 'https' not in host: 
        raise ValueError('You must use SSL for http communication'


    
)  
        
pre_process(**headers)
# content length:  348
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 7, in pre_process
# ValueError: You must use SSL for http communication
复制代码

还有很多种解包的实现方式,甚至于不针对函数,只是从列表、元组中取出数据,并用动态变量引用。

numbers = [1, 2, 3, 4, 5, 6]

# The left side of unpacking should be list or tuple.
*a, = numbers
# a = [1, 2, 3, 4, 5, 6]

*a, b = numbers
# a = [1, 2, 3, 4, 5]
# b = 6

a, *b, = numbers
# a = 1 
# b = [2, 3, 4, 5, 6]

a, *b, c = numbers
# a = 1
# b = [2, 3, 4, 5]
# c = 6
复制代码

上述操作中说明,可以分别从列表或元组中解包,得到相应值,然后用*a*b引用解包所得数据,并将其打包为列表。这与前述任意个数的参数是同样的概念。

结论

以上简要介绍了Python语言中的星号(*),作为一个操作符,它有很多用途,特别是在“收集参数”中,显得很重要。但是,初学者容易在此处迷惑,所以,如果你是初学者,上面的内容要认真看一看。

原文链接:medium.com/understand-…

搜索技术问答的公众号:老齐教室

在公众号中回复:老齐,可查看所有文章、书籍、课程。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/56636