本篇文章详细介绍了 Python 中的函数,包括函数的参数类型、9 种可调用对象、函数的属性,以及 Python 当中支持函数式编程的包。
相信看完这篇文章,你一定对 Python 函数有了更深入的掌握和理解。
觉得有帮助,可以关注我的博客,聚焦全栈开发、人工智能,每周分享 2-4 篇技术博客。
文章目录
1 | 函数的参数类型
根据函数参数的不同,可将函数分为不同的类型,包含位置参数、默认参数、可变参数和关键字参数。
- 位置参数
def greet(name, message):
"""最基本的参数类型,按照位置顺序传递"""
print(f"{message}, {name}!")
# 调用示例
greet("小明", "你好") # 输出: 你好, 小明!
- 默认参数
def greet_with_default(name, message="你好"):
"""参数可以设置默认值,调用时可以不传递"""
print(f"{message}, {name}!")
# 调用示例
greet_with_default("小红") # 输出: 你好, 小红!
greet_with_default("小红", "早上好") # 输出: 早上好, 小红!
- 可变参数
def sum_numbers(*numbers):
"""使用*args接收任意数量的位置参数"""
total = 0
for num in numbers:
total += num
return total
# 调用示例
print(sum_numbers(1, 2, 3)) # 输出: 6
print(sum_numbers(1, 2, 3, 4, 5)) # 输出: 15
- 关键字参数
def print_info(**info):
"""使用**kwargs接收任意数量的关键字参数"""
for key, value in info.items():
print(f"{key}: {value}")
# 调用示例
print_info(name="张三", age=25, city="北京")
# 输出:
# name: 张三
# age: 25
# city: 北京
- 混合使用
def complex_function(a, b=2, *args, **kwargs):
"""混合使用各种参数类型"""
print(f"a: {a}")
print(f"b: {b}")
print(f"args: {args}")
print(f"kwargs: {kwargs}")
# 调用示例
complex_function(1, 3, 4, 5, x=10, y=20)
# 输出:
# a: 1
# b: 3
# args: (4, 5)
# kwargs: {'x': 10, 'y': 20}
- 仅关键字参数
def keyword_only(*, name, age):
"""使用*来强制后面的参数必须使用关键字参数"""
print(f"姓名: {name}, 年龄: {age}")
# 调用示例
keyword_only(name="李四", age=30) # 正确
# keyword_only("李四", 30) # 错误,必须使用关键字参数
- 仅位置参数
def position_only(a, b, /, c, d, *, e, f):
"""
使用/来强制前面的参数必须使用位置参数
/ 之前的参数必须是位置参数
/ 和 * 之间的参数可以是位置参数或关键字参数
* 之后的参数必须是关键字参数
"""
print(f"a: {a}, b: {b}, c: {c}, d: {d}, e: {e}, f: {f}")
# 调用示例
position_only(1, 2, 3, d=4, e=5, f=6) # 正确
position_only(1, 2, c=3, d=4, e=5, f=6) # 正确
# position_only(a=1, b=2, c=3, d=4, e=5, f=6) # 错误,a和b必须是位置参数
# position_only(1, 2, 3, 4, 5, 6) # 错误,e和f必须是关键字参数
- 参数解包
def unpack_example(a, b, c):
print(f"a: {a}, b: {b}, c: {c}")
# 使用列表解包
numbers = [1, 2, 3]
unpack_example(*numbers) # 输出: a: 1, b: 2, c: 3
# 使用字典解包
params = {'a': 1, 'b': 2, 'c': 3}
unpack_example(**params) # 输出: a: 1, b: 2, c: 3
2 | 9 种可调用对象
在 Python 中,可调用对象是指任何可以使用括号 <font style="color:rgba(0, 0, 0, 0.6);">()</font>
进行调用的对象。
Python的九种可调用对象包括:
- 用户定义的函数。使用
def
语句创建的函数 - 内置函数。 Python内置的函数如
len()
,print()
等 - 内置方法。Python对象的内置方法如
list.append()
- 类方法。使用
@classmethod
装饰器定义的方法 - 静态方法。使用
@staticmethod
装饰器定义的方法 - 类实例方法。类的实例方法
- 类实例。实现了
__call__
方法的类实例 - 生成器函数。包含
yield
语句的函数 - lambda表达式。使用
lambda
创建的匿名函数
可以使用 callable()
函数检查一个对象是否可调用。
下面是一些例子。
- 用户定义的函数。
print("1. 用户定义的函数")
def user_function(x, y):
"""使用def语句创建的普通函数"""
return x + y
print(f"调用结果: {user_function(3, 4)}") # 输出: 7
print(f"是否可调用: {callable(user_function)}") # 输出: True
print()
- 内置函数
print("Python内置的函数,如len(), sum(), print()等")
print(f"调用结果: {len([1, 2, 3])}") # 输出: 3
print(f"是否可调用: {callable(len)}") # 输出: True
- 内置方法。
print("Python对象的内置方法")
my_list = [1, 2, 3]
print(f"调用结果: {my_list.append(4)}") # 输出: None (但列表已修改)
print(f"修改后的列表: {my_list}") # 输出: [1, 2, 3, 4]
print(f"是否可调用: {callable(my_list.append)}") # 输出: True
- 类方法
class ClassMethodExample:
@classmethod
def class_method(cls, x):
"""使用@classmethod装饰器定义的方法,第一个参数是类本身"""
print(f"类名: {cls.__name__}")
return x * 2
print(f"调用结果: {ClassMethodExample.class_method(5)}") # 输出: 10
print(f"是否可调用: {callable(ClassMethodExample.class_method)}") # 输出: True
- 静态方法
class StaticMethodExample:
@staticmethod
def static_method(x, y):
"""使用@staticmethod装饰器定义的方法,不自动接收self或cls参数"""
return x + y
print(f"调用结果: {StaticMethodExample.static_method(3, 4)}") # 输出: 7
print(f"是否可调用: {callable(StaticMethodExample.static_method)}") # 输出: True
- 类实例方法
class InstanceMethodExample:
def instance_method(self, x):
"""类实例的方法,第一个参数是实例本身(self)"""
return x * 2
instance = InstanceMethodExample()
print(f"调用结果: {instance.instance_method(5)}") # 输出: 10
print(f"是否可调用: {callable(instance.instance_method)}") # 输出: True
- 类实例
class CallableClass:
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
"""实现__call__方法使实例可调用"""
return x * self.factor
callable_instance = CallableClass(3)
print(f"调用结果: {callable_instance(4)}") # 输出: 12
print(f"是否可调用: {callable(callable_instance)}") # 输出: True
non_callable_instance = InstanceMethodExample() # 没有实现__call__方法的类实例
print(f"是否可调用: {callable(non_callable_instance)}") # 输出: False
- 生成器函数
def generator_function():
"""包含yield语句的函数,每次调用返回一个生成器对象"""
yield 1
yield 2
yield 3
gen = generator_function()
print(f"生成器对象: {gen}")
print(f"第一个值: {next(gen)}") # 输出: 1
print(f"是否可调用: {callable(generator_function)}") # 输出: True
print(f"生成器对象是否可调用: {callable(gen)}") # 输出: False
- lambda 表达式
lambda_func = lambda x, y: x * y
print(f"调用结果: {lambda_func(3, 4)}") # 输出: 12
print(f"是否可调用: {callable(lambda_func)}") # 输出: True
3 | 函数的属性
在 Python 当中,函数是一等对象, 对于对象而言,其内部一般会有各种属性。下面是函数的常用属性:
-
__name__
: 函数的名称。提供函数的名称,用于调试、日志记录和元编程。当使用装饰器或高阶函数时,可以保留原始函数名称。 -
__doc__
: 函数的文档字符串。 存储文档字符串,支持自动生成文档、帮助系统和IDE中的代码提示功能。 -
__module__
: 函数所属的模块名。标识函数所属的模块,有助于确定函数的来源,特别是在导入场景下。 -
__defaults__
: 位置参数的默认值元组。保存位置参数的默认值,对于反射和函数检查功能非常有用,可以在运行时了解函数的默认行为。 -
__code__
: 编译后的函数元数据。包含函数编译后的元数据,如参数数量、局部变量、源文件位置等,常用于高级的调试和代码分析工具。 -
__annotations__
: 函数参数和返回值的类型注解。存储类型注解信息,支持类型检查、静态分析工具和IDE智能提示。 -
__kwdefaults__
:** 仅关键字参数的默认值字典**。记录仅关键字参数的默认值,在检查函数签名时很有用。 -
__closure__
: 闭包函数捕获的变量。保存闭包变量,使闭包函数能够访问其外部作用域中的变量,是函数式编程的核心机制。 -
__call__
: 使函数成为可调用对象的方法。使函数成为可调用对象,是函数本质的体现。 -
自定义属性: 可以给函数添加任意自定义属性。允许给函数添加额外信息,如缓存结果、元数据或配置信息。
下面是一些代码例子:
# 1. __name__ 属性
def example_function():
"""这是一个示例函数"""
pass
print("1. __name__ 属性:获取函数名称")
print(f"函数名称: {example_function.__name__}") # 输出: 函数名称: example_function
print()
# 2. __doc__ 属性
def doc_example():
"""这是函数的文档字符串,用于描述函数的功能和用法。
这部分内容可以包含多行。
"""
pass
print("2. __doc__ 属性:获取函数的文档字符串")
print(f"文档字符串:\n{doc_example.__doc__}")
print()
# 3. __module__ 属性
print("3. __module__ 属性:获取函数所属的模块名")
print(f"所属模块: {example_function.__module__}") # 通常是 '__main__' 或模块名
print()
# 4. __defaults__ 属性
def default_args_example(a, b=1, c=2):
return a + b + c
print("4. __defaults__ 属性:获取默认参数的值")
print(f"默认参数值: {default_args_example.__defaults__}") # 输出: 默认参数值: (1, 2)
print()
# 5. __code__ 属性
def code_example(a, b):
x = a + b
return x
print("5. __code__ 属性:获取编译后的函数元数据")
print(f"函数参数个数: {code_example.__code__.co_argcount}") # 输出参数个数:2
print(f"函数名称: {code_example.__code__.co_name}") # 输出: code_example
print(f"函数定义所在文件: {code_example.__code__.co_filename}")
print(f"函数第一行代码的行号: {code_example.__code__.co_firstlineno}")
print()
# 6. __annotations__ 属性
def annotated_function(a: int, b: str) -> bool:
return len(b) == a
print("6. __annotations__ 属性:获取函数参数和返回值的类型注解")
print(f"类型注解: {annotated_function.__annotations__}") # 输出: {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'bool'>}
print()
# 7. __kwdefaults__ 属性
def keyword_only_example(*, a=1, b=2):
return a + b
print("7. __kwdefaults__ 属性:获取仅关键字参数的默认值")
print(f"仅关键字参数默认值: {keyword_only_example.__kwdefaults__}") # 输出: {'a': 1, 'b': 2}
print()
# 8. __closure__ 属性
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure_func = outer_function(10)
print("8. __closure__ 属性:获取闭包函数的变量")
print(f"闭包是否存在: {closure_func.__closure__ is not None}")
if closure_func.__closure__:
print(f"闭包变量值: {closure_func.__closure__[0].cell_contents}") # 输出: 10
print()
# 9. __call__ 方法
print("9. __call__ 方法:使函数成为可调用对象")
print(f"调用函数: {example_function.__call__() is None}") # 等同于直接调用 example_function()
print()
# 10. 自定义属性
def custom_attr_example():
pass
custom_attr_example.custom_value = 42
print("10. 自定义属性:可以给函数添加任意自定义属性")
print(f"自定义属性值: {custom_attr_example.custom_value}") # 输出: 42
print()
4 | 支持函数式编程的包
4.1 函数式编程概述
在函数式编程中,经常需要把算数运算符当做函数来使用。
什么是函数式编程?函数式编程是一种编程范式,强调函数的应用而非状态的改变。下面是其主要特点:
- 纯函数:相同输入总是产生相同输出,无副作用
- 高阶函数:可接收/返回函数
- 递归作为主要控制结构
- 不可变数据:避免状态修改
函数式编程和命令式编程的主要区别如下:
举一些代码例子直观感受一下两者的区别:
- 基本范式对比
命令式编程(关注步骤):
# 计算列表元素的平方和
numbers = [1, 2, 3, 4]
result = 0
for num in numbers: # 显式循环控制
result += num ** 2 # 修改外部状态
函数式编程(关注结果):
from functools import reduce
numbers = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y**2, numbers, 0) # 无状态修改
- 数据处理对比
命令式过滤与转换:
# 筛选偶数并加倍
data = [1, 2, 3, 4, 5]
output = []
for x in data:
if x % 2 == 0: # 显式条件判断
output.append(x * 2) # 逐步构建结果
函数式编程实现:
# 使用列表推导式
[x*2 for x in data if x%2==0]
4.2 operator 模块
operator模块提供了一系列对应Python内置运算符的函数,使Python支持函数式编程风格。
operator 模块中的基本运算符
例如 operator 包含的基本算数运算符:
import operator
# 算术运算函数
print("基本算术运算:")
print(f"加法: 5 + 3 = {operator.add(5, 3)}")
print(f"减法: 5 - 3 = {operator.sub(5, 3)}")
print(f"乘法: 5 * 3 = {operator.mul(5, 3)}")
print(f"除法: 5 / 3 = {operator.truediv(5, 3)}")
print(f"整除: 5 // 3 = {operator.floordiv(5, 3)}")
print(f"取模: 5 % 3 = {operator.mod(5, 3)}")
print(f"幂运算: 5 ** 3 = {operator.pow(5, 3)}")
print(f"负数: -5 = {operator.neg(5)}")
# 2. 比较运算
print("\n比较运算:")
print(f"等于: 5 == 3 结果是 {operator.eq(5, 3)}")
print(f"不等于: 5 != 3 结果是 {operator.ne(5, 3)}")
print(f"大于: 5 > 3 结果是 {operator.gt(5, 3)}")
print(f"大于等于: 5 >= 3 结果是 {operator.ge(5, 3)}")
print(f"小于: 5 < 3 结果是 {operator.lt(5, 3)}")
print(f"小于等于: 5 <= 3 结果是 {operator.le(5, 3)}")
# 3. 逻辑运算
print("\n逻辑运算:")
print(f"与操作: True and False = {operator.and_(True, False)}")
print(f"或操作: True or False = {operator.or_(True, False)}")
print(f"非操作: not True = {operator.not_(True)}")
# 4. 位运算
print("\n位运算:")
print(f"按位与: 5 & 3 = {operator.and_(5, 3)}")
print(f"按位或: 5 | 3 = {operator.or_(5, 3)}")
print(f"按位异或: 5 ^ 3 = {operator.xor(5, 3)}")
print(f"按位取反: ~5 = {operator.invert(5)}")
print(f"左移: 5 << 2 = {operator.lshift(5, 2)}")
print(f"右移: 5 >> 2 = {operator.rshift(5, 2)}")
operator模块中还有一类函数,即工厂函数itemgetter和attrgetter,能替代从序列中取出项或读取对象属性的lambda表达式。
itemgetter 提取元组或字典中的名字
itemgetter 的一种常见用法是根据元组的某个字段对元组列表进行排序。来看一个例子:
# 基本用法 - 从字典中获取特定键的值
user = {'name': '张三', 'age': 30, 'city': '北京'}
get_name = operator.itemgetter('name') # get_name是一个可调用对象
print(f"获取单个键: get_name(user) = {get_name(user)}")
# 获取多个键
get_info = operator.itemgetter('name', 'city')
print(f"获取多个键: get_info(user) = {get_info(user)}")
# 用于列表/元组的索引获取
numbers = [10, 20, 30, 40, 50]
get_second = operator.itemgetter(1)
print(f"获取列表索引1的值: get_second(numbers) = {get_second(numbers)}")
# 获取多个索引
get_multiple = operator.itemgetter(0, 2, 4)
print(f"获取多个索引的值: get_multiple(numbers) = {get_multiple(numbers)}")
# 在排序中使用itemgetter - 对元组列表排序
data = [('张三', 85), ('李四', 92), ('王五', 78)]
# 按姓名排序(索引0)
sorted_by_name = sorted(data, key=operator.itemgetter(0))
print("按姓名排序:")
for item in sorted_by_name:
print(f" {item[0]}: {item[1]}分")
attrgetter 根据名称来提取对象的属性
attrgetter 创建的函数会根据名称来提取对象的属性。如果传递给 attrgetter 多个属性名,那么它会返回由提取值构成的元组。此外,如果参数名中包含.(点号),那么attrgetter就会深入嵌套对象,检索属性。
看一些例子:
class Student:
def __init__(self, name, score, grade):
self.name = name
self.score = score
self.grade = grade
def __repr__(self):
return f"Student(name='{self.name}', score={self.score}, grade='{self.grade}')"
# 创建学生对象
students = [
Student('张三', 85, 'A'),
Student('李四', 92, 'A+'),
Student('王五', 78, 'B')
]
# 获取单个属性
get_name = operator.attrgetter('name')
print(f"获取第一个学生的name属性: {get_name(students[0])}")
# 获取多个属性
get_info = operator.attrgetter('name', 'score')
print(f"获取第一个学生的多个属性: {get_info(students[0])}")
# 在排序中使用attrgetter
# 按分数排序
sorted_by_score = sorted(students, key=operator.attrgetter('score'))
print("按分数排序学生列表:")
for student in sorted_by_score:
print(f" {student.name}: {student.score}分 ({student.grade})")
# 按名字排序
sorted_by_name = sorted(students, key=operator.attrgetter('name'))
print("按名字排序学生列表:")
for student in sorted_by_name:
print(f" {student.name}: {student.score}分 ({student.grade})")
# 获取嵌套属性
class Person:
def __init__(self):
self.name = "李四"
self.age = 25
class Department:
def __init__(self, name, manager):
self.name = name
self.manager = manager
class Company:
def __init__(self, name, department):
self.name = name
self.department = department
# 创建嵌套对象
manager = Person() # 使用前面定义的Person类
dept = Department("研发部", manager)
company = Company("ABC科技", dept)
# 获取嵌套属性
get_manager_name = operator.attrgetter('department.manager.name')
print(f"获取公司部门经理名字: {get_manager_name(company)}")
4.3 functools.partial 固定函数的部分参数
functools.partial
是 Python 中一个强大的工具,它允许我们固定函数的部分参数,创建新的可调用对象。这在函数式编程和简化代码方面非常有用。
下面是functools.partial
的常见使用场景。
- 基本用法:固定参数。
def power(base, exponent):
"""计算base的exponent次方"""
return base ** exponent
# 创建平方函数
square = partial(power, exponent=2)
# 创建立方函数
cube = partial(power, exponent=3)
print(f"2的平方: {square(2)}") # 输出: 4
print(f"2的立方: {cube(2)}") # 输出: 8
- 在数据处理中应用
# 示例:处理学生成绩
students = [
{'name': '张三', 'math': 85, 'english': 90},
{'name': '李四', 'math': 92, 'english': 88},
{'name': '王五', 'math': 78, 'english': 95}
]
# 创建获取特定科目成绩的函数
get_math_score = partial(lambda student: student['math'])
# 按数学成绩排序
sorted_by_math = sorted(students, key=get_math_score, reverse=True)
print("按数学成绩排序:")
for student in sorted_by_math:
print(f" {student['name']}: {student['math']}分")
- 在 API 调用中的应用
class API:
def __init__(self, base_url):
self.base_url = base_url
def get(self, endpoint, params=None):
print(f"调用API: {self.base_url}/{endpoint}")
print(f"参数: {params}")
# 这里应该是实际的API调用代码
return f"响应数据: {endpoint}"
# 创建特定API实例
api = API("https://api.example.com")
# 创建特定端点的API调用函数
get_users = partial(api.get, "users")
get_products = partial(api.get, "products")
# 调用API
print(get_users(params={"page": 1}))
print(get_products(params={"category": "electronics"}))
输出:
调用API: https://api.example.com/users
参数: {'page': 1}
响应数据: users
调用API: https://api.example.com/products
参数: {'category': 'electronics'}
响应数据: products
- 在配置管理中的应用
class Config:
def __init__(self, default_timeout=30, default_retries=3):
self.default_timeout = default_timeout
self.default_retries = default_retries
def make_request(self, url, timeout=None, retries=None):
timeout = timeout or self.default_timeout
retries = retries or self.default_retries
print(f"请求URL: {url}")
print(f"超时设置: {timeout}秒")
print(f"重试次数: {retries}次")
# 创建不同配置的请求函数
config = Config()
fast_request = partial(config.make_request, timeout=5, retries=1)
reliable_request = partial(config.make_request, timeout=60, retries=5)
# 使用不同配置的请求
fast_request("https://example.com/fast")
reliable_request("https://example.com/important")
全文完。如有帮助,不介意一键三连吧?