Python基础教程:默认参数和可变参数

枫铃3年前 (2021-10-03)Python243

1. 默认参数

在函数定义时附加一个赋值运算符(=)来为参数指定默认参数值。必选参数在前,默认参数在后,匹配时从左往右进行匹配。

使用默认参数有什么好处?最大的好处是能降低调用函数的难度。

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)
 
enroll('Sarah', 'F')
enroll('Bob', 'M', 7)                 # 从左往右匹配,故是age = 7
enroll('Adam', 'M', city='Tianjin')   # 可以直接指定赋值的参数名

默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下:

# 你使用默认参数调用时出现如下情况
print(add_end())
print(add_end())   # 再次调用时结果就不对了
print(add_end())
 
"""
output:
['END']
['END', 'END']
['END', 'END', 'END']
"""

原因如下:Python 函数在定义的时候,默认参数 L 的值就被计算出来了,即[],因为默认参数 L 也是一个变量,它指向对象[],每次调用该函数,如果改

变了 L 的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。所以,定义默认参数要牢记一点:默认参数必须指向不变对象!

2. 可变参数

可变参数就是传入的参数个数是可变的,可以是 1 个、2 个到任意个,还可以是 0 个。有以下两种参数形式:

1)*args:接收N个位置参数,转换成元组tuple形式。这个*号到底是做什么用的呢?

a. 打包参数:当*号出现在函数参数中时,即遇到带*号的形参,那么就会把还未分配出去的实参以元组形式打包(pack),分配给那个带

'''
Python学习交流,免费公开课,免费资料,
免费答疑,系统学习加QQ群:579817333 
'''
def foo1(*args):
    print(args)      # 输出 (1, 2, 3, 4, 5)
 
def foo2(a, *args):
    print('a:', a) 
    print('args:', args)  # 输出 (2, 3, 4, 5)
 
foo1(1, 2, 3, 4, 5)  # 1,2,3,4,5被打包分配给args
foo2(1, 2, 3, 4, 5)  # 1 配给 a, 2,3,4,5被打包分配给args

这种打包参数的方法也可以使用在不确定元素个数的情况下的赋值,表述有点抽象,直接看个例子:

# 假如 record 后面都是电话号码但是不知道有多少个
record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
name, email, *phone_numbers = record
print(phone_numbers)  # ['773-555-1212', '847-555-1212']
 
# 最后一个元素会被 current 占用,其余均被打包
*trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
print(trailing)  # [10, 8, 7, 1, 9, 5, 10]
 
# 也可用在循环中
records = [('foo', 1, 2), ('bar', 'hello'), ('foo', 3, 4)]
def do_foo(x, y):
    print('foo', x, y)
 
def do_bar(s):
    print('bar', s)
 
for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)   # 这里是拆包,可以看下面理解下
    elif tag == 'bar':
        do_bar(*args)

b. 拆分参数:当*号出现在实参中时,即传入带*号的实参,那么会把打包了的实参(元组或列表),拆分成单个的变量,依次赋值给函数的形参。

'''
Python学习交流,免费公开课,免费资料,
免费答疑,系统学习加QQ群:579817333 
'''
def bar(a, b, c, d=10):
    print(a, b, c, d)
 
bar(*[1, 2, 3])

2)**kwargs:接收N个关键字参数,转换成字典dict形式。**号的作用与*号是一样的,只是打包或拆分的形式不一样:

a. 打包参数:对于带**号的形参,会把多个实参参数打包成字典。

def bar(**kw):
    print(kw)   # 输出:{'a': 1, 'b': 2, 'c': 3}
 
bar(a=1, b=2, c=3)

b. 拆分参数:对于带**号的实参,会把已经打包的字典的键值拆成单个,依次赋值给函数的形参。

def bar(a, b, c):
    print(a,b,c)   # 输出:1,2,3
 
bar(**{'a': 1, 'b': 2, 'c': 3})  # 每个key必须和bar的形参名一致

如果我们希望检查是否有某个参数,如 city 和 job,可以这样:

def person(name, age, **kw):
    if 'city' in kw:
        # 有 city 参数
        pass
    if 'job' in kw:
        # 有 job 参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)
 
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)

注:无论函数参数是怎么样的,我们都可以通过拆分参数这种形式进行赋值,看个例子:

args 和 kw 被拆分后会按顺序赋值给 test 的形参。

'''
Python学习交流,免费公开课,免费资料,
免费答疑,系统学习加QQ群:579817333 
'''
args = (1,2)
kw = {}
 
def test(a,b):
    print(a,b)
 
test(*args, **kw)  # 完全不必去管test的参数有几个或者是怎样的形式

3. 只接受关键字参数

将强制关键字参数放到某个 *参数或者单个*后面就能达到这种效果。比如:

def recv(maxsize, *, block):
    'Receives a message'
    pass
 
recv(1024, True) # TypeError
recv(1024, block=True) # Ok

相关文章

利用python同步windows和linux文件

写python脚本的初衷,每次在windows编辑完文件后,想同步到linux上去,只能够登录服务器,...

爬虫基本原理

爬虫基本原理 一、爬虫是什么? 百度百科和维基百科对网络爬虫的定义:简单来说爬虫就是抓取目标网站内容的工具,一般是根据定义的行...

Django 函数和方法的区别

函数和方法的区别 1、函数要手动传self,方法不用传 2、如果是一个函数,用类名去调用,如果是一个方法...

Django 知识补漏单例模式

单例模式:(说白了就是)创建一个类的实例。在 Python 中,我们可以用多种方法来实现单例模式&#x...

Django基础知识MTV

Django简介 Django是使用Python编写的一个开源Web框架。可以用它来快速搭建一个高性能的网站。 Django也是一个MVC框架。但是在Dj...

Python mysql 索引原理与慢查询优化

一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。