【新人系列】Python 入门(十三):函数进阶
✍ 个人博客:https://blog.csdn.net/Newin2020?type=blog
📝 专栏地址:https://blog.csdn.net/newin2020/category_12801353.html
📣 专栏定位:为 0 基础刚入门 Python 的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚 专栏简介:在这个专栏,我将带着大家从 0 开始入门 Python 的学习。在这个 Python 的新人系列专栏下,将会总结 Python 入门基础的一些知识点,方便大家快速入门学习~
❤️ 如果有收获的话,欢迎点赞 👍 收藏 📁 关注,您的支持就是我创作的最大动力 💪
1. 匿名函数
在定义函数的时候,不想给函数起一个名字,这个时候就可以用 lambda 来定义一个匿名函数,lambda 表达式,又称匿名函数。
语法:
- 参数:可选,通常以逗号分隔的变量表达式形式,也就是位置参数
- 表达式:不能包含循环、return,可以包含 if…else…
变量名 = lambda 参数: 表达式
可以理解 lambda 表达式,就是简单函数(函数体仅是单行的表达式)的简写版本。相比函数,lamba 表达式具有以下 2 个优势:
- 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁。
- 对于不需要多次复用的函数,使用 lambda 表达可以在用完之后立即释放,提高程序执行的性能。
# 示例 1:使用函数,不带参数
def func():
return 1 == 2
result = func()
print(result) # False
# 使用匿名函数
result = lambda: 1 == 2
print(result) # <function <lambda> at 0x10444cea0>
print(result()) # False
# 实例 2:使用函数,带有参数
def func_a(x, y, z):
return x + y + z
result = func_a(1, 2, 3)
print(result) # 6
# 使用匿名函数
a = lambda x, y, z: x + y + z
print(a(1, 2, 3)) # 6
# 示例 3:使用函数,含有if...else语法
def func(x, y):
if x > y:
return x
else:
return y
result = func(2, 6)
print(result) # 6
# 使用匿名函数
func = lambda x, y: x if x > y else y
print(func(2, 6)) # 6
2. 高阶函数
函数式编程
函数式编程将一个问题分解成一系列函数,通过把大段代码拆成函数,通过一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程还具有一个特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
高阶函数
在 Python 中,高阶函数是指可以接受一个或多个函数作为参数,或者能够返回一个函数的函数。比如 map( )、filter( ) 和 reduce( ) 这些内置函数就是常见的高阶函数。
2.1 内置高阶函数
map( ) 函数
map( ) 函数接受一个函数和一个可迭代对象作为参数,然后将这个函数应用到可迭代对象的每个元素上,并返回一个新的可迭代对象。
下面这个例子就可以看出方法二中使用了 map( ) 函数之后,操作就方便简洁了许多,不用再自己去手动遍历列表。
# 对传入参数的值进行翻倍
def double_num(x):
return x * 2
numbers = [1, 2, 3, 4, 5]
number_new = []
# 方法一
for i in numbers:
number_new.append(double_num(i))
print(number_new) # [2, 4, 6, 8, 10]
# 方法二
print(list(map(double_num, numbers))) # [2, 4, 6, 8, 10]
filter( ) 函数
filter( ) 函数的基本语法格式:
filter(function, iterable)
funcition 参数表示要传入一个函数,iterable表示一个可选代对象。
filter( ) 函数的功能是对 iterable 中的每个元素,都使用 function 函数判断,并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。
例如,我想过滤一个字符串列表,筛选出以 “A” 开头的字符串,则可以先自定义一个判断规则,然后传入 filter 函数并过滤指定的列表。
# 打印列表中以"A"开头的名字
def first_name(x):
if x.startswith("A"):
return True
else:
return False
name_list = ["Alex", "Hana", "Anny", "Sunny"]
f = filter(first_name, name_list)
print(list(f)) # ['Alex', 'Anny']
2.2 functools 模块
各个函数应用场景:
- 偏函数:传入的参数存在大量重复的值
- cmp_to_key:需要自定义排序规则的场景
- singledispatch:判断分支比较多的场景
- wraps:需要用到装饰器的场景
- lru_cache:涉及到大量计算的场景
偏函数
在 Python 中,偏函数(Partial Function)是通过 functools 模块的 partial 函数创建。偏函数允许固定一个函数的某些参数,从而创建一个新的函数,这个新函数只需要接收剩余未固定的参数。
偏函数可以使代码更简洁、更具可读性,并且在某些情况下可以避免重复编写相似的函数调用代码。
格式:int1 = functools.partial(参数1, 参数2)
- 参数 1:需要转变的函数名
- 参数 2:需要设置的默认参数
举个例子,我在某个场景中会频繁的调用下面的 add 函数,并且传入的参数中总是有一个为 10,那么就可以创建一个偏函数 add_new1 将 10 这个参数固定下来。之后每次调用 add_new1 就只用传递两个参数,10 这个参数会自动帮我们传入。
import functools
def add(a, b, c):
print(a, b, c)
return a + b + c
add_new1 = functools.partial(add, 10)
result = add_new1(2, 3) # 10 2 3
print(result) # 15
add_new2 = functools.partial(add, 10, 2)
result = add_new2(4) # 10 2 4
print(result) # 16
cmp_to_key
在 python 中我们可以使用 sorted 方法对指定列表进行排序,如果传入的是字符串列表,则会按照 ASCII 码值进行排序,但如果我有其它需求例如按照字符数量大小进行排序,则这种方法就行不通了。
numbers = [1, 200, 33, 4, 50, 10]
print(sorted(numbers))
str_list = ['apple', 'banana', 'test', 'python']
print(sorted(str_list)) # ['apple', 'banana', 'python', 'test']
而 functools 中的 cmp_to_key 函数就可以做到这点,我们只需先定义好一个排序规则。然后通过下面这种传参的方式传入 sorted 函数中即可,这相当于指定了自定义的排序规则。
import functools
# 按照字符数量进行排序
def compare_length(a, b):
if len(a) < len(b):
return -1
elif len(a) > len(b):
return 1
else:
return 0
print(sorted(str_list, key=functools.cmp_to_key(compare_length))) # ['test', 'apple', 'banana', 'python']
singledispatch
singledispatch 是 Python 3.4 引入的标准库,它提供了一种将函数转换为多态函数的方法,可以根据函数的第一个参数类型的不同来调用不同的实现。
具体来说,使用 @singledispatch 装饰器可以定义一个函数,并针对第一个参数的类型定义不同的实现。当函数被调用时,Python 会自动查找对应类型的实现来执行,如果找不到对应类型的实现,则会调用默认实现。
举个例子,下面定义了一个名为 func 的函数,并使用 @singledispatch 装饰器将其转换为多态函数。然后,使用 @func.register 装饰器针对不同的参数类型分别定义了不同的实现。当调用 func 函数时,Python 会自动根据传入参数的类型来选择对应的实现。
from functools import singledispatch
@singledispatch
def func(data):
print("default implementation")
@func.register(int)
def func_int(data):
print(f"arg: {data} is an integer")
@func.register(str)
def func_string(data):
print(f"arg: {data} is a string")
func(1) # arg: 1 is an integer
func("hello") # arg: hello is a string
func([1, 2]) # default implementation
我们通过这种方式可以使得我们的代码更清晰更具有结构化,而不是通过写一堆的 if else 语句来进行分支的判断。
wraps
wraps 用于在装饰器中保留被装饰函数的元数据(如函数名和文档字符串),以避免装饰器对原始函数的影响。
我们来看看不加 wraps 的结果是什么,可以发现原始函数名称被改变了。
def add_exclamation(func):
# @wraps(func)
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
return 'hello ' + name
print(greet.__name__) # 输出:wrapper
下面我们在装饰器中使用 @wraps(func),它就会用 func 的元数据覆盖了 wrapper 函数的元数据,这样就可以保留原始函数的元数据,不会因为装饰器的存在而改变原始函数的名字和文档字符串。
from functools import wraps
def add_exclamation(func):
@wraps(func)
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
return 'hello ' + name
print(greet.__name__) # 输出:greet
lru_cache
lru_cache 用于实现缓存功能,即 “Least Recently Used”(最近最少使用)缓存策略。
当使用 lru_cache 装饰一个函数时,它会自动缓存该函数的调用结果。对于相同的参数组合,如果函数已经被调用过,那么下次调用时会直接返回缓存的结果,而无需重新计算,从而提高函数的执行效率。
举个例子,下面 fibonacci 函数计算斐波那契数列,如果没有 lru_cache 装饰器,对于每个 n 的值都会重新计算。有了 lru_cache 装饰器后,已经计算过的结果会被缓存起来,下次相同参数调用时直接返回缓存结果。
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
lru_cache 函数接受一个可选的参数 maxsize ,用于指定缓存的最大大小。如果 maxsize 设为 None ,则缓存大小没有限制,默认值通常是 128。当缓存达到最大容量时,最近最少使用的缓存结果会被删除,为新的结果腾出空间。
2.3 collections 模块
统计对象个数
在需要统计对象个数的场景下,我们不用自己通过很多代码去实现统计方法,而是可以直接调用 collections 模块中的 Counter 函数,直接通过一行代码就能获取对象的个数。
from collections import Counter
list = [1, 2, 3, 2, 1, 3, 5, 2]
str1 = "hello world"
# 统计每个元素出现的次数
print(Counter(list)) # Counter({2: 3, 1: 2, 3: 2, 5: 1})
print(Counter(str1)) # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
# 统计出现次数最多的前x个元素
print(Counter(list).most_common(3)) # [(2, 3), (1, 2), (3, 2)]
print(Counter(str1).most_common(2)) # [('l', 3), ('o', 2)]
合并多个字典
在需要处理多个字典的场景下,可以通过 collections 中的 ChainMap 函数先将多个字典合并成一个字典,然后就能很方便的进行后续处理操作。
from collections import ChainMap
dict1 = {'python': 100}
dict2 = {'java': 90}
dict3 = ChainMap(dict1, dict2) # 合并dict1和dcit2为一个字典
for key, value in dict3.items():
print(key, value)