函数参数
1 形参与实参简介
形参指的是函数定义阶段的函数名后小括号中的变量(形式上的参数)。
实参指的是函数调用阶段传递给函数的实际的值。
形参相当于变量名,实参相当于变量值。
形参与实参的关系:
- 在函数调用阶段,实参被绑定给形参;
- 形参与实参的绑定关系只能存在于函数体内部,在调用时生效,在调用结束后解除。
def func(a, b):
pass
# 形式1
func(1, 2)
# 形式2
a = 1
b = 2
func(a, b)
# 形式3
func(int(a), b)
2 形参与实参的具体使用
2.1 位置参数
按照从左到右的顺序依次定义的参数称为位置参数,包括位置形参和位置实参。
位置形参
处于函数定义阶段
特点:
位置形参必须被传值。
位置实参
处于函数调用阶段
特点:
按照从左到右的顺序依次传入的值,且传值的数量和顺序必须与位置形参一致。
2.2 关键字参数(实参)
在函数调用阶段,按照 key=value的形式传入的值。
特点:
指名道姓地给某个形参传值,可以不参照形参的顺序。
2.3 位置实参与关键字实参混合使用
- 位置实参必须放在关键字实参前面;
- 不能对同一个形参重复传值。
def func(x, y):
pass
func(1, y=2)
func(x=1, 2) # 报错
func(1, x=1, y=2) # 报错 相当于对形参x传两次值。
2.3 默认参数(形参)
在函数定义阶段,就已经被赋值的形参称为默认参数。
特点:
在定义阶段已经为默认形参赋值,在调用阶段可以不需要为默认形参赋值。
2.4 位置形参与默认形参混合使用
- 位置形参必须在默认形参之前;
- 默认形参的值是在函数定义阶段被赋值的,准确地说,被赋予的是值的内存地址;
- 默认值可以被指定为任意类型,但不推荐被指定为可变数据类型。
原因:函数的理想状态是被设计成根据输入值可以预知执行结果的状态,如果默认值被指定为可变数据类型,则意味着在函数定义和调用阶段外可以去修改函数的执行结果,这是不被推荐的。
num = 12
def func(x, y=num):
print(y)
num = 123
func(1) # 12
上面的例子中在函数定义阶段默认形参y被赋值,即形参y存储了num的内存地址,指向了存储值12的内存空间,之后修改变量num并不影响形参y。
lst = [12]
def func(x, y=lst):
print(y)
lst.append(123)
func(1) # [12, 123]
上面的例子中在函数定义阶段默认形参y存储了lst的内存地址,指向了存储列表的内存空间,列表是可变数据类型。变量lst和形参y指向的内存空间中存储的是元素的内存地址。当通过变量lst修改列表时,内存地址不会发生变化,因此形参y可以感知到列表的变化。
改进
def func(x, lst=None): # 保证形参均为不可变数据类型
if lst is None: # 在函数内部将参数初始化为列表
lst = []
lst1 = [1, 2, 3]
func(1, lst1)
3 可变长度参数
在调用函数时传入的实参个数不固定,实参个数大于形参个数。
实参的作用是给形参传值,这意味着需要一种形参来接受多余的实参,包括位置实参和关键字实参。
3.1 用于形参
- 用来接收多余的位置实参
* + 形参名(args)
多余的位置实参被 * 保存为元组,然后将元组赋值给 * 后面的变量。
def add(start, *args):
res = start
for each_arg in args:
res += each_arg
return res
print(add(0, 1, 4, 5, 22, 666)) # 698
- 用来接收多余的关键字实参
** + 形参名(kwargs)
多余的关键字实参被 ** 保存为字典,然后将字典赋值给 ** 后面的变量 (kwargs)。
3.2 用于实参
实参前带 *,意味着先将 * 后面的参数打散,再按顺序传递给位置实参。
实参前带 **,意味着先将 ** 后面的参数打散,再传递给关键字实参。
def func(a, b, c):
print(a, b, c)
func([1, 2, 3]) # 报错,形参个数与实参个数不一致
func(*[1, 2, 3])
str = 'abc'
func(*str) # a b c
def func(a, b, c):
print(a, b, c)
dic = {'a': 1, 'b': 2, 'c': 3}
func(*dic) # a b c key
func(**dic) # 1 2 3 key=value
3.3 混合
*args 必须位于 **kwargs 之前
def index(x, y, z):
print(x, y, z)
def wrapper(*args, **kwargs): # 形参带*/**,用于接收多余的实参
index(*args, **kwargs) # 实参带*/**,将接受到的实参打散,再分配给对应的形参
wrapper(1, 2, 3) # 1 2 3
wrapper(y=2, z=3, x=1) # 1 2 3
wrapper(1, z=3, y=2) # 1 2 3
内部相当于
=> wrapper(*(1, ), **{'y': 2, 'z': 3})
=> index(*(1, ), **{'y': 2, 'z': 3})
=> index(1, y=2, z=3)
wrapper()将接收到的参数原封不动地交给了其内部函数index(),因此为wrapper()传递的参数格式(位置,数量,关键字等)必须遵循其内部函数index()。
修改内部函数index,为其增加新的形参,此时外部函数wrapper无需修改。
def index(x, y, z, w):
print(x, y, z, w)
def wrapper(*args, **kwargs):
index(*args, **kwargs)
wrapper(1, 2, 3, 4) # 1 2 3 4
wrapper(w=4, y=2, z=3, x=1) # 1 2 3 4
wrapper(1, z=3, w=4, y=2) # 1 2 3 4
4 练习
4.1 函数1
用户传入修改的文件名和要修改的内容,执行函数,完成修改操作。
def modify(filepath, raw_txt, new_txt):
"""
用于修改文件
:param filepath: 待修改的文件路径
:param raw_txt: 待修改的字符串
:param new_txt: 新字符串
:return:
"""
with open(filepath, mode='rt', encoding='utf-8') as f1:
old_txt = f1.read()
new_txt = old_txt.replace(raw_txt, new_txt)
with open(filepath, mode='wt', encoding='utf-8') as f2:
f2.write(new_txt)
4.2 函数2
统计传入字符串中【数字】、【字母】、【空格】以及 【其它】的个数
def count_str(str):
"""
计算传入字符串中【数字】、【字母】、【空格】以及 【其它】的个数。
:param str: 待统计的字符串
:return:
"""
if len(str) == 0:
print('输入字符串为空。')
return
count_lst = ['【数字】', '【字母】', '【空格】', '【其它】']
count_dict = dict.fromkeys(count_lst, 0)
for each_str in str:
if each_str.isdigit():
count_dict['【数字】'] += 1
elif each_str.isalpha():
count_dict['【字母】'] += 1
elif each_str == ' ': # 这里不能使用isspace(), isspace()会判断空格、制表符和换行等
count_dict['【空格】'] += 1
else:
count_dict['【其它】'] += 1
res = ''
for each_key in count_dict:
res += '{key}的数量为{value}\n'.format(key=each_key, value=count_dict[each_key])
print(res)
4.3 函数3
判断用户传入的对象(字符串、列表、元组)长度是否大于5。
def check_len(obj):
"""
判断用户传入的对象(字符串、列表、元组)长度是否大于5
:param obj: 用户传入的对象(字符串、列表、元组)
:return: True-长度大于5
False-长度不大于5
"""
if len(obj) > 5:
print('传入的{}的长度大于5'.format(type(obj)))
return True
else:
print('传入的{}的长度不大于5'.format(type(obj)))
return False
4.4 函数4
检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
def get_first_two(str):
"""
检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
:param str: 待处理的字符串
:return: 处理后的字符串
"""
if len(str) > 2:
str = str[:2]
return str
4.5 函数5
检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
def get_odd_ele(obj):
"""
检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者
:param obj: 待处理的列表或元组
:return: 新列表
"""
res = list(obj)
return res[1::2]
4.6 函数6
检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新字典返回给调用者。
dic = {“k1”: “v1v1”, “k2”: [11, 22, 33, 44]}
PS: 字典中的value只能是字符串或列表
def get_first_two(dic):
"""
检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新字典返回给调用者。
:param dic: 待处理的字典
:return: 新字典
"""
for each_key, each_value in dic.items():
if len(each_value) > 2:
dic[each_key] = each_value[:2]
return dic