|
文章目录

再学函数
1. 变量在函数中的作用域
变量的作用域是指变量的作用范围.
局部变量: 在函数体或局部范围内声明的变量称为局部变量.局部变量仅在局部作用域内有效.
全局变量: 在函数之外或在全局范围内声明的变量称为全局变量.全局变量允许在函数内部和外部访问,但是不允许在函数内部修改.
golbal: 用于在函数内部访问和修改全局作用域中的变量
.通过在函数内部使用 global
关键字声明变量,该变量就可以在函数内部被修改,并且修改后的值也会影响到全局作用域中的变量.
x = 10
def modify_global_variable():
global x
x = 20
print(x)
modify_global_variable()
print(x)
nonlocal: 用于在嵌套函数中修改嵌套作用域外的变量
。它允许你在内部函数中访问并修改外部函数的局部变量,而不是创建一个新的同名局部变量
.并且会影响到外部函数中的同名变量.这样可以实现在嵌套函数中共享和修改变量的目的.(如果不加nonlocal, 也是可以在嵌套函数中访问到外层的变量的,只是无法修改)
def outer():
x = "local"
def inner():
nonlocal x
x = "nonlocal"
print(x)
inner()
print("outer:", x)
x = 'global'
outer()
print('global:', x)
2. 函数的参数传递.
函数参数的传递可以分为4类:
位置参数: 调用函数时根据函数定义的参数的位置来传递参数.
关键字参数: 函数调用通过key=value的形式来传递参数, 可以让函数更加的清晰, 同时也不需要强调顺序.
def self_introduction(name, age, gender):
print(f"你好, 我的名字叫{name}")
print(f"今年{age}岁, 性别{gender}")
self_introduction(name="zhangsan", age=25, gender='男')
需要注意的是: 位置参数和关键字参数可以混用, 但是位置参数必须在前面, 且需要匹配顺序.
但是关键字参数之间, 不存在先后顺序.
# 强制关键字参数.
def self_introduction(name, age, *, gender):
print(f"你好, 我的名字叫{name}")
print(f"今年{age}岁, 性别{gender}")
self_introduction("lisi", '男', gender=25)
- 缺省参数: 也叫做默认参数, 用于在定义函数的时候为参数提供默认值, 调用函数时可以不传递该参数的值, 所有的位置参数必须出现在默认参数前, 包括函数定义和调用.
def self_introduction(name, age, come_from='中国'):
print(f"你好, 我的名字叫{name}, 今年{age}岁, 我来自{come_from}")
self_introduction(name="zhangsan", age=25)
不定长参数: 也叫做可变参数, 用于不确定调用的时候会传递多少参数.
a.
位置传递
: *args. 传递给函数的所有参数, 都会被args所接受,会根据传进去的参数的位置组成一个元组
.- *args允许函数接受任意数量的位置参数.
它以元组的形式传递参数,函数内部可以使用args变量来获取到参数.
b.
关键字传递
: **kwargs, 在参数是key=value的情况下, 所有的key-value都会被kwargs接收, 同时会将key-value组成一个字典.- **kwargs允许函数接收任意数量的关键字参数.
它以字典的形式传递参数, 函数内部可以使用kwargs变量来引用这个字典.
def average(a, b, c):
print((a + b + c) / 3)
1.通过位置传递不定常参数
# 通过位置传递不定长参数
def average(*args):
# print(type(args)) -> 元组
# print(args) -> (1,2,3,4,5,6)
print(sum(args) / len(args))
average(1, 2, 3, 4, 5 ,6)
2.通过关键字传递不定长参数
def self_introduction(**kwargs):
print(kwargs)
# print(f"你好, 我的名字叫{name}, 今年{age}岁, 我来自{come_from}")
self_introduction(name="zhangsan", age=25, come_from='北京')
3.不定长参数在Python当中的应用
def self_introduction(*args, **kw):
# print(args)
# print(kw)
# print(type(kwargs))
# print(kwargs)
print(f"你好, 我的名字叫{args[0]}, 今年{kw['age']}岁, 我来自{kw['come_from']}")
self_introduction("zhangsan", age=25, come_from='北京')
补充学习: 不定长参数*args和**kwargs
请看这块代码:
def wrapper(*args, **kwargs):
# 这里直接使用args和kwargs,并没有加上*号
cache_key = (args, tuple(sorted(kwargs.items())))
if cache_key in cache:
return cache.get(cache_key)
# 这里使用的是加上*号的*args和**args
result = func(*args, **kwargs)
cache[cache_key] = result
return result
注意我在上面代码中注释部分的内容, 现在请思考不加 * 号的不定长参数和加上 * 号的不定长参数在用法上有什么区别?
首先再次说明一下Python当中的*args和**kwargs的用法:
*args
- 作用:*args 用于将多个位置参数(非关键字参数)收集为一个
元组
- 语法:
在函数定义中,*args 放在形参列表的最后,表示接收所有未被其他形参捕获的位置参数
- 作用:*args 用于将多个位置参数(非关键字参数)收集为一个
**kwargs
- 作用:**kwargs 用于将多个关键字参数收集为一个
字典
- 语法:
在函数定义中,**kwargs 放在形参列表的最后,表示接收所有未被其他形参捕获的关键字参数
- 作用:**kwargs 用于将多个关键字参数收集为一个
结合使用 *args 和 **kwargs
在函数定义中,*args 和 **kwargs 可以同时使用,但 *args 必须在 **kwargs 之前
在上面的代码中:
def wrapper(*args, **kwargs):
# 这里直接使用args和kwargs,并没有加上*号
cache_key = (args, tuple(sorted(kwargs.items())))
if cache_key in cache:
return cache.get(cache_key)
# 这里使用的是加上*号的*args和**args
result = func(*args, **kwargs)
cache[cache_key] = result
return result
函数定义当中的形参: def wrapper(*args, **kwargs):
- *args 和 **kwargs 的接收:
- *args 接收所有位置参数, 存储为元组
- **kwargs 接收所有关键字参数, 存储为字典
下面在函数体中使用不定长参数:
# 这里直接使用args和kwargs,并没有加上*号
cache_key = (args, tuple(sorted(kwargs.items())))
print(type(args)) # tuple
print(type(kwargs)) # dict
args 和 kwargs 是作为普通变量使用的,而不是作为参数传递给函数. 这里的 args 是一个元组,kwargs 是一个字典,它们已经被函数定义中的 *args 和 **kwargs 收集并存储了
如果直接打印这里的args和kwargs的类型会发现分别是tuple()和dict()
但是我们发现在函数调用中使用的是加上*号的不定长参数:
# 这里使用的是加上*号的*args和**args
result = func(*args, **kwargs)
其实, 在函数调用中,*args 和 **kwargs 用于将已有的数据结构(元组或字典)**解包** 为函数的参数
:
- *args:将一个元组
解包
为多个位置参数 - **kwargs:将一个
字典
解包为多个关键字参数
总结:
- 在
函数定义
中,*args 和 **kwargs 用于收集动态参数 - 在
函数调用
中,*args 和 **kwargs 用于解包已有的数据结构(元组或字典)为函数的参数
3. 值传递和引用传递
值传递(Pass by Value)和引用传递(Pass by Reference)是关于函数参数传递方式的两个概念.
值传递是指将实际参数的值复制一份给形式参数,函数中对形式参数的修改不会影响到实际参数的值
。在值传递中,函数中对形式参数的修改只会影响到函数内部,不会影响到函数外部。
# 值传递
def modify_value(x):
x = 10
print(x)
value = 5
modify_value(value)
print(value)
引用传递是指将实际参数的引用(地址)传递给形式参数,形式参数和实际参数指向同一块内存地址,函数中对形式参数的修改会影响到实际参数的值。在引用传递中,函数中对形式参数的修改会反映到函数外部.
# 引用传递
def modify_list(lst):
lst.append(4) # 修改参数(列表)的内容
print(1, id(lst))
# lst += [5] # 不会创建新对象, 所以id值不变, 注意这里不要误解+=
lst = lst + [5] # 创建了新对象
print(2, id(lst))
print(1, lst)
my_list = [1, 2, 3]
print(3, id(my_list))
modify_list(my_list)
print(2, my_list)
补充说明:
- 不可变对象(如字符串、元组、数字等):修改内容时会创建新的对象,id 值会改变
- 可变对象(如列表、字典、集合等):修改内容时不会创建新的对象,id 值保持不变
需要注意的是,虽然在Python中没有严格的引用传递机制,但是对于可变对象(如列表、字典等),它们的传递方式是引用传递,因为它们的值可以在函数内部被修改. 而对于不可变对象(如数字、字符串等),它们的传递方式类是值传递,因为函数内部对形式参数的修改不会影响到实际参数
理解值传递和引用传递的概念对于编写和调试代码非常重要,可以帮助我们更好地理解函数参数传递的机制和代码中的行为.
# 注意比较下面两种调用方式:
def f(x, li=[]):
for i in range(x):
li.append(i)
print(li)
# 1. 方式一
print('当参数为4')
f(4)
print('当参数为5')
# 注意哦, 这里使用的依然是之前的那个默认的li的地址
f(5)
# 2. 方式二
print('当参数为4')
f(4)
print('当参数为5')
f(5, li=[4, 5]) # 这里使用的是指定的列表, 所以地址变了
补充学习: 把函数作为参数传递
# 当把函数作为参数传递.
a_list = [15, 111, 2232, 123123, 324234]
# 将a_List排序, 排序的根据是数字的个位.
def sorted_by(num):
num_string = str(num)[-1]
return int(num_string)
b_list = sorted(a_list, key=sorted_by)
print(b_list)
# 改写之前的计算器:
def add(a, b):
return a + b
def calc(a, b, func):
return func(a, b)
res = calc(6, 6, add)
print(res)
4. 匿名函数
也称为lambda函数,是一种简洁的函数定义方式,用于创建一次性的、简单的函数.
匿名函数的语法形式为:lambda 参数: 表达式
匿名函数的返回值就是 表达式的结果
它由关键字 lambda
开头,后跟一个或多个参数,然后是冒号 :
,最后是一个表达式, 匿名函数执行表达式的计算,并返回结果.
匿名函数通常用于需要简单函数逻辑的地方,尤其是在需要传递函数作为参数的情况下,可以更简洁地定义函数,避免定义命名函数的繁琐
。但需要注意的是,匿名函数通常用于表达式较短、逻辑简单的情况,对于复杂的函数逻辑,仍然建议使用命名函数进行定义
def add(a, b):
return a + b
lambda a, b: a + b
案例: numbers = [5, 2, 7, 1, 9] 对此列表排序, 根据对3取余的 结果进行排序.
# numbers = [5, 2, 7, 1, 9] 对此列表排序, 根据对3取余的 结果进行排序.
# 2 2 1 1 0
numbers = [5, 2, 7, 1, 9]
a_list = sorted(numbers, key=lambda x: x % 3)
print(a_list)
改写前面的计算器程序:
def calculator(a, b, operator):
operator_dict = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
}
return operator_dict.get(operator)(a, b)
res = calculator(6, 6, '*')
print(res)
5. python中内置的常用函数
zip()
zip: zip([iterable, …]),
将多个可迭代对象打包成元组.
它返回一个可迭代的对象,该对象生成元组,每个元组包含来自每个可迭代对象的元素,如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同.
以长的为主使用zip_longest方法
# zip
from itertools import zip_longest
numbers = [1, 2, 3, 4]
letters = ['a', 'b', 'c']
x_list = ['d', 'e']
zipped = list(zip_longest(numbers, letters, x_list, fillvalue=0))
print(zipped)
for item in zipped:
print(item)
map()
map(): map(function, iterable):对可迭代对象中的每个元素应用指定的函数.
# map
def square(x):
return x ** 2
numbers = [1, 2, 3, 4, 5]
# 要将numbers中的数字全部转换为字符串.
a_list = list(map(str, numbers))
print(a_list)
squared_numbers = map(lambda x: x**2, numbers)
print(squared_numbers)
print(list(squared_numbers))
filter()
filter(): filter(function, iterable): 对可迭代对象中的元素进行过滤. 返回值为True时保存
# filter
def is_even(x):
return x % 2 == 0
numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 != 0, numbers)
print(list(even_numbers)) # [1, 3, 5]
all()
all(): all(iterable):判断可迭代对象中的所有元素是否都为真.如果是返回 True,否则返回 False.
# all 判断可迭代对象中的所有元素是否都为真.如果是返回 True,否则返回 False.
print(all(['a', 'b', 'c', ''])) # False
print(all([])) # True 注意哦,Python规定如果可迭代对象为空, all()的结果就是 True
numbers = [1, 2, 3, 4, 5, -1]
# 使用 all() 函数判断是否所有元素都大于0
sign = all(num > 0 for num in numbers)
print(sign) # False
any()
any(): any(iterable): 判断可迭代对象中的任何一个元素是否为真(有一个为真就是真).不是则返回 False,如果有一个为 True,则返回 True.
# any
# print(any(['', 0, '', None]))
# print(any([]))
numbers = [1, 3, 5, -1]
# 使用 any() 函数判断numbers中是否存在偶数元素
sign = any(num % 2 == 0 for num in numbers)
print(sign)
6. 函数练习
1.编写一个函数 get_sum_of_lists,接受多个列表作为参数,并返回它们对应位置元素的和的列表 短列表缺少的用0补齐.
from itertools import zip_longest
# [1, 2, 3, 4, 5, 6]
# [6, 5, 4, 3, 2, 1]
# 拆包
def get_sum_of_lists(*args):
# print(*args)
zipped = list(zip_longest(*args, fillvalue=0))
result = list(map(sum, zipped))
return result
lst1 = [1, 2, 3, 4, 5, 6, 7, 8]
lst2 = [6, 5, 4, 3, 2, 1]
lst3 = [6, 5, 4, 3, 2, 1, 3, 6]
res = get_sum_of_lists(lst1, lst2, lst3)
print(res)
2.实现一个函数 get_max_length(words),接收一个字符串列表 words,返回列表中最长的单词的长度.
例如,对于输入列表 [‘apple’, ‘banana’, ‘orange’, ‘watermelon’],函数应该返回 10,因为最长的单词是 ‘watermelon’,它的长度为 10
def get_max_length(words):
# len_list = list(map(len, words))
len_list = [len(word) for word in words]
return max(len_list)
lst = ['apple', 'banana', 'orange', 'watermelon']
print(get_max_length(lst))
3.实现一个函数get_primes
, 接收一个参数n, 函数功能是: 返回n以内(包含n)的所有的质数. get_primes函数体内使用lambda实现.
只能被1跟它本身整除的数. 2, 3, 4, 5 ,6 ,7 ,8 ,9, 10 每一次取余的结果都不是0, 那么它就是质数. 反之, 就不是质数.
# all, all true , false
# filter
# 5 2 3 4
# 6 2 3 4 5
def get_primes(n):
primes_list = list(filter(lambda x: all(x % i != 0 for i in range(2, x)), range(2, n + 1)))
return primes_list
primes = get_primes(20)
print(primes)
|
|