一篇文章让你明白Python中的函数

       函数是具有特定功能的、可重用的代码块。它将一系列相关的操作封装起来,通过一个简单的函数名来调用,从而实现代码的模块化和复用。例如,在数学计算中,计算一个数的平方根是一个常见的操作。我们可以定义一个名为“sqrt”的函数,当需要计算平方根时,直接调用这个函数,而不需要每次都重新编写计算平方根的代码。

一、函数基础

(一)定义

  在Python中,定义函数使用def关键字,其基本语法格式如下:

def 函数名(形参列表):
    """文档字符串"""  # 可选,用于描述函数的功能和参数等信息
    # 函数体
    # 这里是一系列的代码,实现函数的功能
    return 返回值  # 返回值是可选的
  • 函数名:函数的名称,用于标识函数。函数名的命名规则遵循Python的标识符规则,通常使用小写字母和下划线组合,如calculate_areaprint_message等。函数名应该具有描述性,能够清晰地表达函数的功能。

  • 形参列表:形参是函数定义时在括号中声明的参数,用于接收外部传入的数据。它可以有多个形参,形参之间用逗号分隔。例如,def add(a, b):中的ab就是形参,它们在函数内部代表两个将要进行加法运算的数。形参可以有默认值,如def greet(name, age=18):,这里的age形参有一个默认值18。

(二)调用

  函数调用是通过函数名和实际参数来执行函数的过程。其基本语法格式如下:

函数名(实参列表)
  • 实参列表:实参是在调用函数时传递给函数的实际数据。实参的数量和类型需要与形参匹配。实参可以是具体的值,如数字、字符串等,也可以是变量、表达式等。

(三)形参

形参(形式参数)是在函数定义时声明的参数。它就像是函数内部的一个“占位符”,用于接收外部传入的数据。形参在函数被调用时才被赋予具体的值,这个值就是实参。

类型

  • 位置形参:按照位置顺序接收实参的形参。这是最常见的形参类型。例如,在def add(a, b):中,ab都是位置形参,它们按照实参的传递顺序接收数据。

  • 关键字形参:通过关键字(形参名)来接收实参的形参。这种方式使得调用函数时可以不按照形参的顺序传递实参。例如,在def greet(name, age):中,调用greet(age=25, name="Alice")时,nameage就是关键字形参。

  • 默认形参:在函数定义时给形参赋予一个默认值的形参。当调用函数时,如果没有传递对应的实参,就使用这个默认值。例如,在def greet(name, age=18):中,age是一个默认形参,如果调用greet("Bob")时没有传递age的实参,那么age的值就是默认的18。

  • 可变长形参:可以接收任意数量实参的形参。有两种可变长形参:

    • 可变长位置形参:使用*形参名来定义,可以接收任意数量的位置实参,并将它们存储在一个元组中。例如,def sum_numbers(*nums):,调用sum_numbers(1, 2, 3, 4)时,nums形参会接收到一个元组(1, 2, 3, 4)

    • 可变长关键字形参:使用**形参名来定义,可以接收任意数量的关键字实参,并将它们存储在一个字典中。例如,def print_info(**info):,调用print_info(name="Alice", age=25)时,info形参会接收到一个字典{"name": "Alice", "age": 25}

作用

  • 数据传递:形参是函数与外部进行数据交互的桥梁。通过形参,函数可以获取外部传入的数据,从而根据这些数据执行相应的操作。例如,在一个计算圆面积的函数中,半径作为形参传入,函数根据这个半径值来计算面积。

  • 代码复用:形参使得函数具有通用性。同一个函数可以接收不同的实参,执行相同的操作逻辑,但得到不同的结果。比如一个排序函数,它可以对不同的数组进行排序,只需要将不同的数组作为实参传入即可。

(四)实参

实参(实际参数)是在函数调用时传递给函数的实际数据。它是具体的数据值,用于给函数的形参赋值。

类型

  • 位置实参:按照形参的位置顺序传递实参。这是最常见的实参类型。例如,在def greet(name, age):中,调用greet("Alice", 25)时,"Alice"25就是位置实参,它们分别对应形参nameage

  • 关键字实参:通过形参名来传递实参。这种方式可以不按照形参的顺序传递实参。例如,在def greet(name, age):中,调用greet(age=25, name="Alice")时,age=25name="Alice"就是关键字实参。关键字实参使得代码的可读性更好,尤其是在形参较多时。

  • 可变长实参:传递给可变长形参的实参。对于可变长位置形参,可以传递任意数量的位置实参;对于可变长关键字形参,可以传递任意数量的关键字实参。例如,在def sum_numbers(*nums):中,调用sum_numbers(1, 2, 3, 4)时,1, 2, 3, 4就是可变长位置实参;在def print_info(**info):中,调用print_info(name="Alice", age=25)时,name="Alice", age=25就是可变长关键字实参。

注意事项

  • 位置实参和关键字实参混合使用:在调用函数时,位置实参必须在关键字实参之前。例如,greet("Alice", age=25)是正确的,但greet(name="Alice", 25)是错误的。

  • 可变长实参与其他实参混合使用:在定义函数时,可变长位置形参必须位于所有位置形参之后,可变长关键字形参必须位于所有形参(包括位置形参和关键字形参)之后。例如,def func(a, b, *args, c, **kwargs):是正确的形参列表顺序。

(五)返回值

返回值是函数执行完毕后返回给调用处的数据。它是一个函数的输出结果,可以是任何数据类型,如整数、浮点数、字符串、列表、元组、字典等。返回值使得函数可以将处理后的数据反馈给调用者,以便进行后续的操作。

基本语法

在Python中,使用return语句来返回值。其基本语法格式如下:

return 返回值
  • 返回值:可以是单个值,也可以是多个值。如果是多个值,Python会将它们打包成一个元组返回。例如,return 1, 2, 3等价于return (1, 2, 3)

示例

def get_user_info():
    """获取用户信息"""
    name = "Alice"
    age = 25
    return name, age  # 返回多个值

user_name, user_age = get_user_info()
print(user_name, user_age)  # 输出Alice 25

在这个例子中,定义了一个名为get_user_info的函数,它没有形参。函数体中定义了两个变量nameage,并使用return语句返回这两个值。调用函数时,可以使用多个变量来接收返回的元组中的值。

二、函数的类型

(一)内置函数

Python自带的函数,无需用户定义,可以直接使用。例如:

  • print():输出函数,用于在控制台打印信息。

  • len():获取长度函数,用于获取字符串、列表等序列的长度。

  • type():获取类型函数,用于获取变量的数据类型。

  • int()float()str()等:类型转换函数,用于将数据转换为对应的类型。

(二)自定义函数

用户根据自己的需求定义的函数。自定义函数的定义和使用方式在前面的“函数基础”部分已经详细介绍。

三、递归函数

(一)概念

递归函数是指在函数的定义中调用函数自身的一种函数。递归函数通常用于解决具有递归性质的问题,如计算阶乘、斐波那契数列等。

(二)特点

(三)示例

# 定义一个匿名函数,用于计算两个数的和
add = lambda x, y: x + y

# 调用匿名函数
result = add(3, 5)
print(result)  # 输出8

# 使用匿名函数进行排序
numbers = [1, 3, 2, 4, 5]
sorted_numbers = sorted(numbers, key=lambda x: -x)  # 按照降序排序
print(sorted_numbers)  # 输出[5, 4, 3, 2, 1]

在这个例子中,定义了一个匿名函数add,用于计算两个数的和。通过lambda x, y: x + y定义了这个匿名函数,并将其赋值给变量add。调用add(3, 5)时,会返回8。

在排序示例中,使用sorted函数对列表numbers进行排序,并通过key=lambda x: -x指定了排序的关键字函数。这个匿名函数用于计算每个元素的相反数,从而实现降序排序。

五、作用域

(一)概念

作用域是指变量、函数等标识符的有效范围。在Python中,主要有以下几种作用域:

(二)示例

# 全局变量
global_var = "I am global"

def outer_function():
    # 外部函数的局部变量
    outer_var = "I am outer"
    
    def inner_function():
        # 内部函数的局部变量
        inner_var = "I am inner"
        print(inner_var)  # 访问内部函数的局部变量
        print(outer_var)  # 访问外部函数的局部变量
        print(global_var)  # 访问全局变量
    
    inner_function()

outer_function()

在这个例子中,定义了一个全局变量global_var,一个外部函数outer_function和一个内部函数inner_function。在inner_function中,可以访问内部函数的局部变量inner_var、外部函数的局部变量outer_var和全局变量global_var。这展示了不同作用域之间的访问关系。

六、闭包

(一)概念

闭包是指一个函数对象,它记录了创建它的作用域链。闭包可以捕获并记住外部函数的局部变量,即使外部函数已经执行完毕,这些变量仍然可以被内部函数访问。闭包在Python中通常通过嵌套函数实现。

(二)特点

在这个例子中,定义了一个性能测试装饰器performance_decoratorperformance_decorator在调用被装饰的函数之前记录开始时间,在调用之后记录结束时间,并计算函数执行所花费的时间。通过@performance_decorator语法,将装饰器应用到compute_sum函数上。调用compute_sum(1000000)时,会输出函数执行所花费的时间。

(三)权限校验

# 定义权限校验装饰器
def auth_decorator(func):
    def wrapper(*args, **kwargs):
        # 假设这里进行权限校验
        authorized = True  # 模拟权限校验结果
        if not authorized:
            raise Exception("Unauthorized access")
        return func(*args, **kwargs)
    return wrapper

# 使用权限校验装饰器
@auth_decorator
def admin_only_function():
    print("This is an admin-only function.")

# 调用被装饰的函数
try:
    admin_only_function()
except Exception as e:
    print(e)

在这个例子中,定义了一个权限校验装饰器auth_decoratorauth_decorator在调用被装饰的函数之前进行权限校验,如果用户没有权限,则抛出异常。通过@auth_decorator语法,将装饰器应用到admin_only_function函数上。调用admin_only_function()时,会先进行权限校验,如果校验不通过,则抛出“Unauthorized access”异常。

(三)示例

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

# 创建闭包
closure = outer_function(10)

# 调用闭包
result = closure(5)
print(result)  # 输出15

在这个例子中,outer_function是一个外部函数,它接受一个参数x。内部函数inner_function接受一个参数y,并返回x + y。当调用outer_function(10)时,返回了inner_function函数对象,这个函数对象就是一个闭包。闭包closure捕获了外部函数的局部变量x(值为10),即使outer_function已经执行完毕,x仍然可以被closure访问。调用closure(5)时,会返回15。

七、装饰器

(一)概念

装饰器是一种设计模式,用于在不修改原有函数代码的情况下,给函数添加新的功能。在Python中,装饰器通常是一个函数或类,它接受一个函数作为参数,并返回一个新的函数。装饰器可以用于日志记录、性能测试、事务处理、权限校验等场景。

(二)语法

函数装饰器

@装饰器函数名
def 被装饰的函数名():
    # 函数体

类装饰器

@装饰器类名
class 被装饰的类名:
    # 类体

(三)示例

函数装饰器示例

# 定义一个装饰器函数
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

# 使用装饰器
@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")

# 调用被装饰的函数
say_hello("Alice")

在这个例子中,定义了一个装饰器函数my_decoratormy_decorator接受一个函数func作为参数,并返回一个新的函数wrapperwrapper函数在调用func之前和之后分别打印了一些信息。通过@my_decorator语法,将装饰器应用到say_hello函数上。调用say_hello("Alice")时,会先执行wrapper函数中的代码,然后调用say_hello函数,最后再次执行wrapper函数中的代码。

类装饰器示例

# 定义一个类装饰器
class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Something is happening before the function is called.")
        result = self.func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result

# 使用类装饰器
@MyDecorator
def say_hello(name):
    print(f"Hello, {name}!")

# 调用被装饰的函数
say_hello("Alice")

在这个例子中,定义了一个类装饰器MyDecoratorMyDecorator类的__init__方法接受一个函数func作为参数,并将其存储在实例变量中。__call__方法使得类实例可以像函数一样被调用,在调用时执行一些额外的操作。通过@MyDecorator语法,将类装饰器应用到say_hello函数上。调用say_hello("Alice")时,会先执行MyDecorator实例的__call__方法中的代码,然后调用say_hello函数,最后再次执行__call__方法中的代码。

八、装饰器案例

(一)日志记录

import logging

# 配置日志
logging.basicConfig(level=logging.INFO)

# 定义日志装饰器
def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function {func.__name__} with args {args} and kwargs {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} returned {result}")
        return result
    return wrapper

# 使用日志装饰器
@log_decorator
def add(x, y):
    return x + y

# 调用被装饰的函数
result = add(3, 5)

在这个例子中,定义了一个日志装饰器log_decoratorlog_decorator在调用被装饰的函数之前和之后分别记录了日志信息,包括函数名、参数和返回值。通过@log_decorator语法,将装饰器应用到add函数上。调用add(3, 5)时,会先记录调用日志,然后执行add函数,最后记录返回值日志。

(二)性能测试

import time

# 定义性能测试装饰器
def performance_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to execute.")
        return result
    return wrapper

# 使用性能测试装饰器
@performance_decorator
def compute_sum(n):
    return sum(range(n))

# 调用被装饰的函数
result = compute_sum(1000000)
  • 递归终止条件:递归函数必须有一个明确的终止条件,否则会导致无限递归,最终引发栈溢出错误。例如,在计算阶乘的递归函数中,当参数为1时,函数应该停止递归。

  • 递归步骤:在每次递归调用中,问题的规模应该逐渐减小,逐步向终止条件靠近。

  • (三)示例

    # 计算阶乘的递归函数
    def factorial(n):
        if n == 1:  # 递归终止条件
            return 1
        else:
            return n * factorial(n - 1)  # 递归步骤
    
    # 调用递归函数
    result = factorial(5)
    print(result)  # 输出120

    在这个例子中,factorial函数用于计算阶乘。当n等于1时,函数返回1,这是递归的终止条件。否则,函数会调用自身,计算n乘以factorial(n - 1),每次调用都会使问题规模减小,直到达到终止条件。

    四、匿名函数

    (一)概念

    匿名函数是一种没有函数名的函数。在Python中,使用lambda关键字来定义匿名函数。匿名函数通常用于简单的操作,如排序、过滤等场景,可以避免定义一个完整的函数。

    (二)语法

    lambda 参数列表: 表达式
  • 参数列表:可以有多个参数,参数之间用逗号分隔。

  • 表达式:是一个简单的表达式,用于计算返回值。匿名函数只能包含一个表达式,不能包含复杂的语句。

  • 局部作用域:在函数内部定义的变量,只能在该函数内部访问。局部变量在函数调用时创建,在函数返回时销毁。

  • 全局作用域:在模块(文件)级别定义的变量,可以在整个模块中访问。全局变量在模块加载时创建,在模块卸载时销毁。

  • 嵌套作用域:当函数嵌套定义时,内部函数可以访问外部函数的变量,但外部函数不能访问内部函数的变量。

  • 内置作用域:Python内置的变量和函数,如printlen等,可以在任何地方访问。

  • 捕获变量:闭包可以捕获外部函数的局部变量,并在内部函数中使用这些变量。

  • 延长变量生命周期:闭包使得外部函数的局部变量的生命周期得以延长,直到闭包对象被销毁。

在这个例子中,定义了一个性能测试装饰器performance_decoratorperformance_decorator在调用被装饰的函数之前记录开始时间,在调用之后记录结束时间,并计算函数执行所花费的时间。通过@performance_decorator语法,将装饰器应用到compute_sum函数上。调用compute_sum(1000000)时,会输出函数执行所花费的时间。

(三)权限校验

# 定义权限校验装饰器
def auth_decorator(func):
    def wrapper(*args, **kwargs):
        # 假设这里进行权限校验
        authorized = True  # 模拟权限校验结果
        if not authorized:
            raise Exception("Unauthorized access")
        return func(*args, **kwargs)
    return wrapper

# 使用权限校验装饰器
@auth_decorator
def admin_only_function():
    print("This is an admin-only function.")

# 调用被装饰的函数
try:
    admin_only_function()
except Exception as e:
    print(e)

在这个例子中,定义了一个权限校验装饰器auth_decoratorauth_decorator在调用被装饰的函数之前进行权限校验,如果用户没有权限,则抛出异常。通过@auth_decorator语法,将装饰器应用到admin_only_function函数上。调用admin_only_function()时,会先进行权限校验,如果校验不通过,则抛出“Unauthorized access”异常。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值