Python 函数学习笔记

是什么

函数 = 一段被命名的、可重复使用的代码块

函数(function)是带名字的代码块,用于完成具体的工作。要执行函数定义的特定任务,可调用(call)该函数。当需要在程序中多次执行同一项任务时,无须反复编写完成该任务的代码,只需要调用执行该任务的函数。它接收输入(参数),执行特定操作,返回结果(或无结果)。

定义一个函数

  1. 函数代码块以 def 关键词开头,后接函数标识符名称圆括号 ()
  2. 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  4. 函数内容以 冒号(:) 起始,并且缩进。
  5. return [表达式]结束函数,选择性地返回一个值给调用方,不带return 表达式的函数相当于返回 None

def functionname( parameters ):
   "函数_文档字符串"
   function_suite
   return [expression]

定义一个空函数

如果想定义一个什么事也不做的空函数,可以用pass语句,缺少了pass,代码运行就会有语法错误,空函数无法被调用。

pass语句什么都不做,那有什么用?

实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。

def empty_fun():
    pass

返回多个值

Python 的函数可以返回多个值。

比如在游戏中经常需要从一个点移动到另一个点,给出坐标、位移和角度,就可以计算出新的坐标:

import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

import math语句表示导入math包,并允许后续代码引用math包里的sincos等函数。

然后,我们就可以同时获得返回值:

>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0

但其实这只是一种假象,Python函数返回的仍然是单一值:

>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)

原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。

Python类型注解(Type Hints)

类型注解是 Python 从动态语言向‘可维护性语言’进化的重要一步。在 Python 中,类型注解不是必须的,但它是让代码清晰的‘最佳实践’。

类型注解 = 在函数、变量上添加类型信息,但 改变运行时行为
只用于静态类型检查(如 PyCharm、mypy),不用于运行时类型检查

基本语法

def add(a: int, b: int) -> int:
    """添加两个整数"""
    return a + b

# 调用
print(add(3, 4))  # 7

变量类型注解

name: str = "Alice"
age: int = 30
is_active: bool = True

高级类型注解

集合类型(List, Dict, Tuple)

from typing import List, Dict, Tuple

# 列表:整数列表
numbers: List[int] = [1, 2, 3]

# 字典:字符串->整数映射
user: Dict[str, int] = {"id": 123, "age": 30}

# 元组:固定长度的元组
point: Tuple[float, float] = (3.5, 4.2)

可选类型(Optional)

from typing import Optional

def get_user(id: int) -> Optional[Dict[str, int]]:
    """可能返回用户,也可能返回 None"""
    if id == 0:
        return None
    return {"id": id, "name": "Alice"}

user = get_user(123)
if user is not None:
    print(user["name"])
    

参数传递

在 python 中,类型属于对象,对象有不同类型的区分,变量是没有类型的:

a = [1, 2, 3]
a = "Runoob"

以上代码中,[1,2,3] 是 List 类型,“Runoob” 是 String 类型,而变量 a 是没有类型,它仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。

Python中一切都是对象,所以严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

可更改(mutable)对象

类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响。

'''
在Python函数的参数传递,主要分参数类型:
- 不可变类型
- 可变类型

**不可变对象类型**:
也即值传递,如整数、浮点数、字符串、元组等。
比如如def fun(a):,传递的只是a的值,没有传递a对象的地址。比如在fun(a)内部修改a的值,
只是修改另一个复制的对象的值,不会影响 a 本身对象的值。

**可变对象类型**:
也即引用(地址)传递,如列表,集合、字典等。如def fun(la):,则是将 la对象地址传过去,修改后fun外部的la也会受影响。
'''

# 传入可变(mutable)对象类型参数案例

def change_list(my_list) :
    my_list[1] = 50
    print("函数内的值",my_list)
    print("函数内列表的内存",id(my_list))

m_list = [1,2,3]
change_list(m_list)
print("函数外的值",m_list)
print("函数外列表的内存",id(m_list))
# 输出结果
#函数内的值 [1, 50, 3]
#函数内列表的内存 1149137300800
#函数外的值 [1, 50, 3]
#函数外列表的内存 1149137300800
 

不可更改(immutable)对象

类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。

'''
在Python函数的参数传递,主要分参数类型:
- 不可变类型
- 可变类型

**不可变对象类型**:
也即值传递,如整数、浮点数、字符串、元组等。
比如如def fun(a):,传递的只是a的值,没有传递a对象的地址。比如在fun(a)内部修改a的值,
只是修改另一个复制的对象的值,不会影响 a 本身对象的值。

**可变对象类型**:
也即引用(地址)传递,如列表,集合、字典等。如def fun(la):,则是将 la对象地址传过去,修改后fun外部的la也会受影响。
'''

# 传入不可变(immutable)对象类型参数案例

def change_int(a:int) ->None:
    print("函数体中未改变前a的内存地址",id(a))
    a = 10
    print("函数体中改变后a的内存地址",id(a))

a = 2
change_int(a)
print(a)
print("函数外b的内存地址",id(a))
# 输出结果
#函数体中未改变前a的内存地址 140733455352776
#函数体中改变后a的内存地址 140733455353032
#2
#函数外b的内存地址 140733455352776

参数

调用函数时可使用的正式参数类型:

  • 必需参数
  • 关键字参数
  • 默认参数
  • 不定长参数

不定长参数

*args:接收任意数量的位置参数

当函数需要处理不确定数量的参数时(如 <font style="color:rgb(6, 10, 38);">sum(1, 2, 3, 4)</font>),Python 用 <font style="color:rgb(6, 10, 38);">*args</font> 解决。

def sum_all(*args):
    """接收任意数量的位置参数"""
    total = 0
    for num in args:
        total += num
    return total

print(sum_all(1, 2, 3))      # 6
print(sum_all(10, 20, 30, 40))  # 100

**kwargs:接收任意数量的关键字参数

当函数需要处理不确定数量的命名参数时(如 <font style="color:rgb(6, 10, 38);">config(key1="value1", key2="value2")</font>),Python 用 <font style="color:rgb(6, 10, 38);">**kwargs</font> 解决。

def print_config(**kwargs):
    """接收任意数量的关键字参数"""
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_config(host="localhost", port=8080, timeout=30)
# 输出:
# host: localhost
# port: 8080
# timeout: 30

lambda(匿名函数)

Python 使用 lambda 来创建匿名函数。
lambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。
匿名函数不需要使用 def 关键字定义完整函数。
lambda 函数通常用于编写简单的、单行的函数,通常在需要函数作为参数传递的情况下使用,
例如在 map()、filter()、reduce() 等函数中。

lambda 函数特点:

  • lambda函数是匿名的,它们没有函数名称,只能通过赋值给变量或作为参数传递给其他函数来使用
  • lambda 函数通常只包含一行代码,适用于编写简单的函数

案例

"""
Python lambda(匿名函数)
Python 使用 lambda 来创建匿名函数。
lambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。
匿名函数不需要使用 def 关键字定义完整函数

公式表达:
lambda arguments: expression
    lambda是 Python 的关键字,用于定义 lambda 函数。
    arguments 是参数列表,可以包含零个或多个参数,但必须在冒号(:)前指定。
    expression 是一个表达式,用于计算并返回函数的结果。
"""


# ab法则
def operator(a, b):
    return a + b


def function(a, b, my_lambda_function):
    return my_lambda_function(a, b)


print("使用普通函数修改:", function(1, 2, operator))
print("*" * 20)

print("=" * 10, "after", "=" * 10)


def function(a, b, lambda_operator):
    return lambda_operator(a, b)


print("使用lambda函数修改:", function(1, 2, lambda a, b: a + b))
print("=" * 20)

# lambda 函数没有参数
f = lambda: "Hello, world!"
print(f())  # 输出: Hello, world!

# lambda 函数1个参数
x = lambda param: param + 10
print(x(5))  # 输出: 15

# lambda 函数多个参数,参数之间使用逗号 , 隔开
x = lambda a, b, c: a + b + c
print(x(5, 6, 2))

# lambda 函数通常与内置函数如 map()、filter() 和 reduce() 一起使用,以便在集合上执行操作
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # 输出:[2, 4, 6, 8]
'''上述代码对标Java:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> evenNumbers = numbers.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers);'''

# 有三名学生的姓名和年龄,按年龄排序
student_list = [{"name": "z3", "age": 36}, {"name": "li4", "age": 14}, {"name": "w5", "age": 27}]
print(sorted(student_list, key=lambda x: x["age"]))

# map() 的主要作用是将给定的函数批量应用到可迭代对象的每个元素上,实现数据转换
map_result = map(lambda x: x * x, [0, 1, 3, 7, 9])
print(list(map_result))  # [0, 1, 9, 49, 81]

# filter() 的主要作用是将给定的函数**批量应用**到可迭代对象的每个元素上,实现数据过滤
filter_result = filter(lambda x: x >= 0, [-0, -1, -3, 7, 9])
print(list(filter_result))  # [0, 7, 9]

解包(unpacking)

解包 = 将序列(列表、元组、字符串等)的元素,一次性分配给多个变量。

核心价值
消除索引依赖,用有意义的变量名代替 **<font style="color:rgb(6, 10, 38);">numbers[0]</font>**,提升可读性+可维护性

'''
基本定义
把可迭代对象(list/tuple/str/range/生成器等)里的元素“按位置”或“按名字”
批量赋给左侧变量的语法统称“解包”。

1 函数定义时的“收集”
# *args 收集多余位置参数
# **kwargs 收集多余关键字参数
def f(a,*args, **kwargs):
    pass

2 函数调用时的解包
args = (1, 2)
kwargs = {'x': 3, 'y': 4}
func(a,*args, **kwargs)   # 等价于 func(100,1, 2, x=3, y=4)
'''

def func(a, *args, **kwargs):
    print(a)
    print(args)
    print(kwargs)
func(100,1,2,3,k1=20, k2=30)

print("*" * 20)

# 解包传参:函数调用时解包
def add(a, b, c):
    return a + b + c

args = (1, 2)
kwargs = {"c": 3}
print(add(*args, **kwargs))

print("*" * 20)


def func1(seq:int,*number:int) -> int:
    result = 0;
    print(seq)
    for x in number:
        result =  result + (x * x)
    return result

print(func1(1, *(1,2,3)))
print(func1(2, 1,2,3))
#print(func1(3, (1,2,3)))# TypeError: can't multiply sequence by non-int of type 'tuple'

闭包(Closure)

闭包 = 一个函数 + 它"记住"的外部环境(变量)

Python 闭包是指嵌套内部函数引用了其外部(嵌套)函数的变量 / 参数且内部函数被返回或暴露出去,
从而保留了外部函数的作用域(即使外部函数已执行完毕)的一种函数对象。
简单来说,闭包让内部函数 “记住” 了它诞生时的外部环境变量,即使外部函数已经执行结束,这些变量也不会被销毁。

def outer(x):
    def inner(y):
        return x + y  # inner 闭包了 x
    return inner

# 创建闭包
add_5 = outer(5)
print(add_5(3))  # 输出 8

闭包必须同时满足 3 个条件:

  1. 函数嵌套
  2. 内部函数引用外部函数的局部变量
  3. 外部函数把内部函数返回(或当作参数传递出去)

优缺点

优点:封装隐藏数据、持久化状态、简化参数传递,适合实现轻量级的状态保持场景

缺点:内存占用过高、可读性 / 调试性差、循环变量捕获陷阱,使用时需避免持有大数据

价值:闭包是 Python 中实现函数式编程和装饰器的重要基础!

💡 为什么重要
装饰器 = 闭包的典型应用,是 Python 的核心特性(如 <font style="color:rgb(6, 10, 38);">@staticmethod</font><font style="color:rgb(6, 10, 38);">@property</font>)。

"""
简化代码

闭包可以提前捕获外部函数的参数,形成一个参数部分固定的新函数,
简化后续调用,避免重复传入相同参数,提升代码复用性和简洁性。

示例:固化 “问候前缀”,后续只需传入姓名即可。
"""
import random


def create_greeter(prefix):
    def greeter(name):
        return f"{prefix}{name}!"

    return greeter


hello_greeter = create_greeter("你好")
print(hello_greeter)
print(hello_greeter(random.randint(1000, 9999)))  # 你好,随机数!(无需重复传入"你好")


# 最终建议:不要写三层嵌套!用最简单的单层闭包。
# 💡 为什么这是最佳实践?
# 避免嵌套函数(减少代码复杂度)
# 调用方式符合直觉(像普通函数一样)
def create_greeter(prefix):
    def greeter(name):
        def greeter2(age):
            return f"{prefix}{name}{age}!"
        return greeter2  # 1. 返回 greeter2(在 greeter 内部定义后)
    return greeter  # 2. 返回 greeter(外部函数)

# 使用方式:
greet = create_greeter("Hello")
greet_name = greet("Alice")  # 返回 greeter2
print(greet_name(30))  # 输出: Hello,Alice,30!


# summary
'''
闭包是 Python 中实现函数式编程和装饰器的重要基础!
一句话定义
    闭包(closure) =「内部函数」+「定义时所在作用域的变量」——即使外层函数已返回,内部函数仍能记住并访问那些变量。

必须同时满足 3 个条件
    函数嵌套
    内部函数引用外部函数的局部变量
    外部函数把内部函数返回(或当作参数传递出去)

记忆口诀
“外层返回内层,内层抓着外层的局部;
返回的函数带着背包,变量永生不死。”

闭包 = 内部函数 + 引用外部变量 + 外部函数返回内部函数,

'''

def outer(x):               # 外部函数
    def inner(y):           # 内部函数
        return x + y        # 内部函数引用了外部变量 x
    return inner            # 返回的是“带背包”的内部函数对象


add_10Var = outer(10)          # 外部函数返回内部函数,但 x=10 被 inner 背着
print(add_10Var(7))            # 17
print(add_10Var.__closure__)   # 查看闭包保存的变量元组
print(add_10Var.__closure__[0].cell_contents)  # 10

# 即使 outer(10) 已经执行结束,x=10 本应被销毁,但由于 inner 引用了它,Python 会将 x 保留在闭包环境中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值