python函数

python中函数的定义和调用

函数的作用

使用函数,把"功能"的相关代码封装起来,当需要使用的时候直接调用函数,实现代码的复用

1.为什么需要函数

目的:让我们的代码已经被重复使用

作用:

1.1代码重复使用

1.2模块化编程(模块化编程核心技术函数,一般是把一个系统分解为若干哥功能,每一个功能技术一个函数

注:在编程领域,可以分为两大类:1.模块话编程 2.面向对象编程

2.什么是函数

是一个被命名的、独立的、完成特定功能的代码段,其可能给调用它的程序一个返回值

被命名的:在python中,大部分函数都是有名函数

def 函数名称():
    return 返回值
返回值:技术函数执行完毕返回的值

完成特定功能的代码段:函数的功能要专一,专门为了完成某个功能而定义

返回值:当函数执行完毕后,其功能会返回一个值给函数调用出

3.函数的定义(def => define缩写形式(定义))

函数的定义:

def 函数名(参数):
    函数体
    ...
    return 返回值

函数的调用:

函数名(参数)

注意:

1.不同的需求,参数可有可无

2.函数必须先定义后使用

4.引入函数

案例:定义一个打招呼程序,用于实现向同事大招呼的功能

以上代码虽然可以完成打招呼的程序,但是‘重复性’代码太多了。

用函数将重复性代码分离处理:

方案一:

def greet():
    print('您好!')



#见到老张调用函数
greet()

#输出
您好!

方案二:升级函数,可以实现向不同人打招呼

def greet(name):
   
    greet('您好,'+ name)

#输入名字实现不同人打招呼
#老张
greet('老张')
#输出
您好,老张

#老王
greet('老王')
#输出
您好,老王
5.函数的返回值

案例三:函数的设计原则 '高内聚,低耦合‘,函数执行完毕之后,应该主动把数据返回给调用处,

#定义一个函数,拥有name参数,同时函数执行完毕后,拥有一个return返回值
def greet(name):
    代码体
    return name + ',您好'

#调用函数
#见到了张老师,打招呼
print(greet('老张'))

#见到了李老师,打招呼
print(greet('老李'))

#见到了王老师,打招呼
print(greet('老王'))
6.return返回值

在 Python 中,return 是函数中用于返回结果的关键字。它的主要作用是终止函数的执行并将指定的值返回给调用者。如果函数中没有 return 语句或 return 后未指定值,函数默认返回 None

基本用法

函数可以通过 return 返回一个值:

def add(a, b):
    return a + b

result = add(3, 5)  # result 的值为 8
 

如果函数中没有 return 语句,或者 return 后没有值,函数返回 None

def no_return():
    pass

print(no_return())  # 输出 None
 
返回多个值

Python 允许通过 return 返回多个值,实际上是返回一个元组:

def get_values():
    return 1, 2, 3

values = get_values()  # values 是一个元组 (1, 2, 3)
 
提前终止函数

return 可以用于提前终止函数的执行:

def check_positive(x):
    if x <= 0:
        return False
    return True

print(check_positive(-1))  # 输出 False
print(check_positive(5))   # 输出 True
 

递归函数中的 return

在递归函数中,return 用于传递递归结果:

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

print(factorial(5))  # 输出 120
 
注意事项
  • return 语句后的代码不会被执行:

    def test():
        return 1
        print("这行代码不会执行")
     
    

  • 在条件分支中,确保所有路径都有返回值或明确返回 None,以避免意外行为。

函数的嵌套

所谓函数嵌套调用指的是一个函数里面又调用了另一个函数

函数嵌套的基础语法

函数嵌套调用是指在一个函数内部调用另一个函数。这是 Python 中组织代码、实现模块化和逻辑分层的重要手段。

基本语法结构:

def outer_function():
    # 外层函数的代码
    print("外层函数开始执行")
def inner_function():
    # 内层函数的代码
    print("内层函数执行")
在外层函数中调用内层函数
inner_function()
print("外层函数执行完毕")
调用外层函数
outer_function()

执行流程:

  1. 定义 outer_function 函数
  2. outer_function 内部定义 inner_function 函数
  3. 调用 outer_function()
  4. outer_function 开始执行,打印"外层函数开始执行"
  5. 调用 inner_function()
  6. inner_function 执行,打印"内层函数执行"
  7. 继续执行 outer_function 剩余代码,打印"外层函数执行完毕"

输出结果:

外层函数开始执行
内层函数执行
外层函数执行完毕

多层嵌套示例:

def function_a():
    print("A函数开始")
def function_b():
    print("B函数开始")
def function_c():
    print("C函数执行")

function_c()
print("B函数结束")
function_b()
print("A函数结束")
function_a()

嵌套调用的特点:

  • 作用域规则:内层函数可以访问外层函数的变量(闭包特性)
  • 执行顺序:从外到内逐层调用,从内到外逐层返回
  • 代码组织:将相关功能封装在一起,提高代码可读性
  • 局部性:内层函数通常只在外层函数内部使用

注意事项:

  • 内层函数在外层函数外部不可直接调用
  • 合理控制嵌套层次,避免过度嵌套影响代码可读性
  • 注意变量作用域,避免意外的变量覆盖
def testB():
    print("----tetsB---")


def testC():
    print("----tetsC---")
    testB()
    

testC()

输出结果:

语法结构:

def outer_function(arg1):  
    # 外层函数代码  
    def inner_function(arg2):  
        # 内层函数代码  
        return arg1 + arg2  # 内层函数可访问外层函数的参数  
    return inner_function  # 返回内层函数对象  
 

嵌套函数的调用方式

直接调用内层函数:外层函数返回内层函数后,通过赋值调用

closure = outer_function(10)  
result = closure(5)  # 结果为15  
 

立即调用嵌套函数

在外层函数中直接调用内层函数。

def outer_function(arg1):  
    def inner_function(arg2):  
        return arg1 * arg2  
    return inner_function(arg1 + 1)  # 立即调用  

result = outer_function(3)  # 结果为12 (3*(3+1))  
 
作用域规则

内层函数可读取外层函数的变量,但修改需使用 nonlocal 关键字(Python)

def counter():  
    count = 0  
    def increment():  
        nonlocal count  
        count += 1  
        return count  
    return increment  

c = counter()  
print(c())  # 输出1  
 

实际应用场景

  1. 闭包:保存外层函数的状态。
  2. 装饰器:通过嵌套函数修改其他函数的行为。
  3. 封装私有逻辑:隐藏内部实现细节。

注意事项

  • 避免过度嵌套,否则会降低代码可读性。
  • 不同语言对嵌套函数的支持可能不同(如C++不支持函数嵌套,但支持Lambda)。

变量的作用域

1.变量:

其作用域可分为局部变量和全局变量。理解它们的区别和使用场景对编写高效、可维护的代码至关重要。

2.局部变量

局部变量是在函数或代码块内部声明的变量,其作用域仅限于声明它的函数或代码块。局部变量在函数调用时创建,在函数执行完毕后销毁。

def example_function():
    local_var = 10  # 局部变量
    print(local_var)

example_function()
# print(local_var)  # 报错,因为 local_var 在函数外不可访问
 

3.全局变量

全局变量是在函数外部声明的变量,其作用域覆盖整个程序。全局变量可以在程序的任何地方访问和修改。

global_var = 20  # 全局变量

def example_function():
    print(global_var)  # 可以访问全局变量

example_function()
print(global_var)  # 可以访问全局变量
 
4.修改全局变量( global 关键字)

在函数内部修改全局变量需要使用 global 关键字,否则会创建一个同名的局部变量。

global_var = 20

def modify_global():
    global global_var  # 声明使用全局变量
    global_var = 30

modify_global()
print(global_var)  # 输出 30
 
局部变量与全局变量的使用建议
  • 优先使用局部变量:减少全局变量的使用可以避免命名冲突和意外的修改。
  • 必要时使用全局变量:当需要在多个函数间共享数据时,可以使用全局变量,但要谨慎管理。
  • 避免滥用全局变量:过多的全局变量会导致代码难以维护和调试。

python with关键字

在 Python 编程中,资源管理是一个重要但容易被忽视的环节。with 关键字为我们提供了一种优雅的方式来处理文件操作、数据库连接等需要明确释放资源的场景。

with 是 Python 中的一个关键字,用于上下文管理协议(Context Management Protocol)。它简化了资源管理代码,特别是那些需要明确释放或清理的资源(如文件、网络连接、数据库连接等)。

为什么需要 with 语句?

传统资源管理的问题

我们先看一个典型的文件操作示例:

file = open('example.txt', 'r')
try:
    content = file.read()
    # 处理文件内容
finally:
    file.close()

这种写法存在以下不足:

  1. 资源管理风险:缺乏自动关闭机制,容易遗漏调用close()方法
  2. 代码冗余:即使是基础的文件操作也需要编写大量模板代码
  3. 异常处理繁琐:开发者需要自行处理各种可能的IO异常情况

with 语句的优势

with 语句基于上下文管理协议(Context Management Protocol)提供了以下优势:

  1. 自动资源释放:自动处理资源的关闭操作
  2. 代码简洁:消除冗余的样板代码
  3. 异常安全:在代码块执行过程中发生异常时仍能保证资源释放
  4. 可读性提升:清晰界定资源的作用范围

文件操作示例

with open('example.txt', 'r') as file:
    content = file.read()
    print(content)
# 文件已自动关闭

这段代码等价于前面的 try-finally 实现,但更加简洁明了。

with 语句的工作原理

上下文管理协议

with 语句背后是 Python 的上下文管理协议,该协议要求对象实现两个方法:

  1. __enter__():进入上下文时调用,返回值赋给 as 后的变量
  2. __exit__():退出上下文时调用,处理清理工作
执行流程

一、正常执行流程(无异常)
  1. 用户发起:进入 with 代码块 用户编写with 上下文管理器实例 as 变量:并执行代码,启动 with 语句逻辑。
  2. with 语句触发上下文初始化:调用__enter__方法 with 语法自动调用ContextManager(上下文管理器对象)的__enter__()方法。
  3. __enter__返回资源对象 __enter__执行资源获取逻辑(比如打开文件、建立数据库连接),将拿到的资源对象返回,赋值给as后面的变量。
  4. 执行 with 缩进内部的业务代码块 把返回的资源交给用户,运行 with 包裹的业务代码。
  5. 内部代码执行结束,触发收尾 代码块正常运行完毕,with 语句自动发起收尾操作,调用ContextManager__exit__()方法。
  6. __exit__执行资源清理__exit__里完成资源释放:关闭文件、断开连接、释放锁等操作,清理完成后把结果回传给 with 语句。
  7. 跳出 with 块,执行后续代码 资源清理完成,流程回到用户代码,继续执行 with 缩进之外的后续程序。
二、代码块抛出异常的特殊逻辑

如果 with 内部代码运行出错抛出异常:

  1. 异常会先被 with 语句捕获,依然会强制调用__exit__(exc_type, exc_val, exc_tb),把异常类型、异常值、堆栈信息三个参数传入__exit__
  2. __exit__先执行资源清理;
  3. __exit__返回True:代表异常已被处理,异常不会向外抛出,程序继续走后续代码;
  4. __exit__返回False(默认情况):资源清理完成后,异常会继续向上抛出,中断后续正常流程。

实际应用场景

1.文件操作

# 同时打开多个文件
with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:
    content = infile.read()
    outfile.write(content.upper())

将input.text内容写入output.text

2.数据库连接

import sqlite3

with sqlite3.connect('database.db') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
    results = cursor.fetchall()
# 连接自动关闭

3.线程锁

import threading

lock = threading.Lock()

with lock:
    # 临界区代码
    print("这段代码是线程安全的")

4.临时修改系统状态

import decimal

with decimal.localcontext() as ctx:
    ctx.prec = 42  # 临时设置高精度
    # 执行高精度计算
# 精度恢复原设置

自定义的上下文管理器

类实现方式

我们可以通过实现 __enter__ 和 __exit__ 方法创建自定义的上下文管理器:

class Timer:
    def __enter__(self):
        import time
        self.start = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.end = time.time()
        print(f"耗时: {self.end - self.start:.2f}秒")
        return False

# 使用示例
with Timer() as t:
    # 执行一些耗时操作
    sum(range(1000000))

代码解读:

1. 进入with Timer() as t阶段
  • 先实例化Timer()得到上下文管理器对象
  • 自动调用对象的__enter__方法:
    1. 导入time模块,记录起始时间self.start
    2. return self,把 Timer 实例本身赋值给变量t
2. 执行with内部代码块

执行sum(range(1000000))做求和运算

3. 代码块结束(正常无异常),自动调用__exit__
  1. 记录结束时间self.end
  2. 计算并打印耗时差值,保留 2 位小数
  3. return False:代表不吞掉内部可能出现的异常,异常会正常向外抛出

补充:如果__exit__返回True,就算内部代码报错,异常也会被上下文管理器拦截,程序不会中断

使用 contextlib 模块

Python 的 contextlib 模块提供了更简单的方式来创建上下文管理器:

from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"<{name}>")
    yield
    print(f"</{name}>")

# 使用示例
with tag("h1"):
    print("这是一个标题")

常见问题与最佳实践

常见错误

1、错误地认为 with 只能用于文件

2、忽略__exit__的返回值

总结要点

关键点说明
自动资源管理with 语句确保资源被正确释放
上下文协议需要实现 __enter__ 和 __exit__ 方法
异常安全即使代码块中出现异常,资源也会被释放
常见应用文件操作、数据库连接、线程锁等
自定义实现可以通过类或 contextlib 创建自定义上下文管理器

python装饰器

装饰器(decorator)是 Python 中的一种高级功能,用于在不修改原函数代码的前提下,动态扩展函数或类的功能

本质上,装饰器是一个函数:它接收一个函数作为参数,并返回一个新的函数(通常是对原函数的增强版本)。

装饰器采用 @decorator_name 的语法形式,可直接作用于函数或方法定义上方。

Python 内置了多个实用装饰器,包括 @staticmethod@classmethod

主要应用场景:

  • 日志记录:追踪函数调用过程,记录参数与返回值
  • 性能分析:测量函数执行耗时
  • 权限验证:控制函数访问权限
  • 结果缓存:存储计算结果以提升运行效率

基本语法

装饰器的核心思想是:用一个函数"包装"另一个函数

装饰器的核心思想

装饰器的核心在于通过一个函数动态扩展另一个函数的功能,而无需修改原函数的代码。这种模式属于高阶函数的应用,即函数可以作为参数传递或作为返回值返回。

装饰器的基本结构

装饰器通常是一个接受函数作为参数的函数,并返回一个新的函数(或可调用对象)。以下是简单装饰器的代码示例:

def decorator(func):
    def wrapper(*args, **kwargs):
        # 扩展功能代码
        result = func(*args, **kwargs)  # 调用原函数
        # 扩展功能代码
        return result
    return wrapper

装饰器的典型用途

  1. 日志记录:自动记录函数的调用信息或执行时间。
  2. 权限校验:在函数执行前检查用户权限。
  3. 性能分析:统计函数执行耗时。
  4. 缓存:存储函数计算结果避免重复计算。

装饰器的实际示例

以下是一个记录函数执行时间的装饰器实现:

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@timing_decorator
def example_function(n):
    return sum(i * i for i in range(n))

装饰器的语法糖

使用 @decorator_name 语法可以简化装饰器的调用,例如:

@decorator
def target_function():
    pass

等价于:

def target_function():
    pass
target_function = decorator(target_function)

类装饰器

装饰器也可以是类,通过实现 __call__ 方法使实例可调用:

class ClassDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Class decorator action before call")
        result = self.func(*args, **kwargs)
        print("Class decorator action after call")
        return result

@ClassDecorator
def another_function():
    pass

多层装饰器

多个装饰器可以堆叠使用,执行顺序从下往上:

@decorator1
@decorator2
def stacked_function():
    pass

等价于:

stacked_function = decorator1(decorator2(stacked_function))

保留原函数元信息

装饰器可能导致原函数的 __name____doc__ 被覆盖,可通过 functools.wraps 保留元信息:

from functools import wraps

def preserving_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

内置装饰器

class MyClass:
    @staticmethod
    def static_method():
        print("静态方法")

    @classmethod
    def class_method(cls):
        print(cls.__name__)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

常用内置装饰器:

  1. @staticmethod:定义静态方法
  2. @classmethod:定义类方法
  3. @property:将方法变为属性

核心总结

装饰器 = 函数包装函数 + 不修改原代码扩展功能
  • @ 语法本质是函数替换
  • wrapper 才是真正执行的函数
  • 推荐使用 *args, **kwargs 提高通用性
  • 支持函数、类、甚至带参数的装饰器

python数据结构

列表

Python中列表是可变的,这是它区别于字符串和元组的最重要的特点,一句话概括即:列表可以修改,而字符串和元组不能。

以下是 Python 中列表的方法:

方法描述
list.append(x)把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。
list.extend(L)通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。
list.insert(i, x)在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。
list.remove(x)删除列表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。
list.pop([i])从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
list.clear()移除列表中的所有项,等于del a[:]。
list.index(x)返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
list.count(x)返回 x 在列表中出现的次数。
list.sort()对列表中的元素进行排序。
list.reverse()倒排列表中的元素。
list.copy()返回列表的浅复制,等于a[:]。

下面示例演示了列表的大部分方法:

list = ['aaa','bbb','ccc',123] #原列表
list2 = ['eee','fff','ggg']
#把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。
list.append('ddd')
print(list)
print("---------------------------")
#通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。
list.extend(list2)
print(list)
print("---------------------------")
#在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,
# 例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。
list.insert(0,'您好')
print(list)
print("---------------------------")
#删除列表中值为 x 的一个元素。如果没有这样的元素,就会返回一个错误。
list.remove(123)
print(list)
print("---------------------------")
#从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。
# (方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
list.pop(0)
print(list)
print("---------------------------")
#返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
print(list.index('aaa'))
print("---------------------------")
#返回 x 在列表中出现的次数。
print(list.count('aaa'))
print("---------------------------")
#倒排列表中的元素。
list.reverse()
print(list)
print("---------------------------")
list3 = list.copy()
print(list3)

输出结果如下:

注意:类似 insert, remove 或 sort 等修改列表的方法没有返回值。

将列表当做栈使用

在 Python 中,可以使用列表(list)来实现栈的功能。栈是一种后进先出(LIFO, Last-In-First-Out)数据结构,意味着最后添加的元素最先被移除。列表提供了一些方法,使其非常适合用于栈操作,特别是 append() 和 pop() 方法。

用 append() 方法可以把一个元素添加到栈顶,用不指定索引的 pop() 方法可以把一个元素从栈顶释放出来。

栈操作

  • 压入(Push): 将一个元素添加到栈的顶端。
  • 弹出(Pop): 移除并返回栈顶元素。
  • 查看栈顶元素(Peek/Top): 返回栈顶元素而不移除它。
  • 检查是否为空(IsEmpty): 检查栈是否为空。
  • 获取栈的大小(Size): 获取栈中元素的数量。

以下是如何在 Python 中使用列表实现这些操作的详细说明:

1、创建一个空栈

stack = []

2、压入(Push)操作

使用 append() 方法将元素添加到栈的顶端:

stack.append(1)
stack.append(2)
stack.append(3)
print(stack)  # 输出: [1, 2, 3]

3、弹出(Pop)操作

使用 pop() 方法移除并返回栈顶元素:

top_element = stack.pop()
print(top_element)  # 输出: 3
print(stack)        # 输出: [1, 2]

4、查看栈顶元素(Peek/Top)

直接访问列表的最后一个元素(不移除):

top_element = stack[-1]
print(top_element)  # 输出: 2

5、检查是否为空(IsEmpty)

检查列表是否为空:

is_empty = len(stack) == 0
print(is_empty)  # 输出: False

6、获取栈的大小(Size)

使用 len() 函数获取栈中元素的数量:

size = len(stack)
print(size)  # 输出: 2

将列表当作队列使用

在 Python 中,列表(list)可以用作队列(queue),但由于列表的特点,直接使用列表来实现队列并不是最优的选择。

队列是一种先进先出(FIFO, First-In-First-Out)的数据结构,意味着最早添加的元素最先被移除。

使用列表时,如果频繁地在列表的开头插入或删除元素,性能会受到影响,因为这些操作的时间复杂度是 O(n)。为了解决这个问题,Python 提供了 collections.deque,它是双端队列,可以在两端高效地添加和删除元素。

使用 collections.deque 实现队列

collections.deque 是 Python 标准库的一部分,非常适合用于实现队列。

以下是使用 deque 实现队列的示例:


from collections import deque


#创建一个空队列
queue = deque()
#向队尾添加元素
queue.append(1)
queue.append(2)
queue.append(3)
queue.append(4)
queue.append(5)
print(f"队列状态:{queue}")
#从队首移除元素
first_element = queue.popleft()
print(f"移除的元素:{first_element}")
print(f"队列状态:{queue}")
#查看队首元素(不移除)
front_element = queue[0]
print(f"队首的元素:{first_element}")
print(f"队列状态:{queue}")

## 检查队列是否为空
is_empty = len(queue) == 0
print("队列是否为空:", is_empty)

# 获取队列大小
size = len(queue)
print("队列大小:", size)       

输出结果:

实例(使用列表实现队列)

class Queue:
    def __init__(self):
        self.queue = []

    def enqueue(self, item):
        self.queue.append(item)

    def dequeue(self):
        if not self.is_empty():
            return self.queue.pop(0)
        else:
            raise IndexError("dequeue from empty queue")

    def peek(self):
        if not self.is_empty():
            return self.queue[0]
        else:
            raise IndexError("peek from empty queue")

    def is_empty(self):
        return len(self.queue) == 0

    def size(self):
        return len(self.queue)

# 使用示例
queue = Queue()
queue.enqueue('a')
queue.enqueue('b')
queue.enqueue('c')

print("队首元素:", queue.peek())    # 输出: 队首元素: a
print("队列大小:", queue.size())    # 输出: 队列大小: 3

print("移除的元素:", queue.dequeue())  # 输出: 移除的元素: a
print("队列是否为空:", queue.is_empty())  # 输出: 队列是否为空: False
print("队列大小:", queue.size())    # 输出: 队列大小: 2

虽然可以使用列表来实现队列,但使用 collections.deque 会更高效和简洁。它提供了 O(1) 时间复杂度的添加和删除操作,非常适合队列这种数据结构。

列表推导式

列表推导式提供了从序列创建列表的简单途径。通常应用程序将一些操作应用于某个序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的判定条件创建子序列。

每个列表推导式都在 for 之后跟一个表达式,然后有零到多个 for 或 if 子句。返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表。如果希望表达式推导出一个元组,就必须使用括号。

这里我们将列表中每个数值乘三,获得一个新的列表:

>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]

这里我们对序列里每一个元素逐个调用某方法:

>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']

我们可以用 if 子句作为过滤器:

>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]

嵌套列表解析

Python的列表还可以嵌套。

以下实例展示了3X4的矩阵列表:

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

以下实例将3X4的矩阵列表转换为4X3列表:

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

以上实例也可以使用以下方法来实现:

>>> transposed = []
>>> for i in range(4):
...     transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

另外一种实现方法:

>>> transposed = []
>>> for i in range(4):
...     # the following 3 lines implement the nested listcomp
...     transposed_row = []
...     for row in matrix:
...         transposed_row.append(row[i])
...     transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

代码理解:

内层列表推导式 [row[i] for row in matrix] 分步演示

i=0(取第 0 列)举例:

  1. row = [1,2,3,4]row[0] = 1
  2. row = [5,6,7,8]row[0] = 5
  3. row = [9,10,11,12]row[0] = 9 拼接得到:[1, 5, 9],作为转置矩阵第 1 行。

同理:

  • i=1 → 取每行第 1 列 → [2, 6, 10]
  • i=2 → 取每行第 2 列 → [3, 7, 11]
  • i=3 → 取每行第 3 列 → [4, 8, 12]

def 语句

使用 del 语句可以从一个列表中根据索引来删除一个元素,而不是值来删除元素。这与使用 pop() 返回一个值不同。可以用 del 语句从列表中删除一个切割,或清空整个列表(我们以前介绍的方法是给该切割赋一个空列表)。

元组和序列

元组由若干逗号分隔的值组成,例如:

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

如你所见,元组在输出时总是有括号的,以便于正确表达嵌套结构。在输入时可能有或没有括号, 不过括号通常是必须的(如果元组是更大的表达式的一部分)。

集合

集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。

可以用大括号({})创建集合。注意:如果要创建一个空集合,你必须用 set() 而不是 {} ;后者创建一个空的字典,下一节我们会介绍这个数据结构。

以下是一个简单的演示:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # 删除重复的
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # 检测成员
True
>>> 'crabgrass' in basket
False

>>> # 以下演示了两个集合的操作
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # a 中唯一的字母
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # 在 a 中的字母,但不在 b 中
{'r', 'd', 'b'}
>>> a | b                              # 在 a 或 b 中的字母
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # 在 a 和 b 中都有的字母
{'a', 'c'}
>>> a ^ b                              # 在 a 或 b 中的字母,但不同时在 a 和 b 中
{'r', 'd', 'b', 'm', 'z', 'l'}

集合也支持推导式:

>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

字典

另一个非常有用的 Python 内建数据类型是字典。

序列是以连续的整数为索引,与此不同的是,字典以关键字为索引,关键字可以是任意不可变类型,通常用字符串或数值。

理解字典的最佳方式是把它看做无序的键=>值对集合。在同一个字典之内,关键字必须是互不相同。

一对大括号创建一个空的字典:{}

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> list(tel.keys())
['irv', 'guido', 'jack']
>>> sorted(tel.keys())
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

造函数 dict() 直接从键值对元组列表中构建字典。如果有固定的模式,列表推导式指定特定的键值对:

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}

此外,字典推导可以用来创建任意键和值的表达式词典:

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

如果关键字只是简单的字符串,使用关键字参数指定键值对有时候更方便:

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}

遍历技巧

在字典中遍历时,关键字和对应的值可以使用 items() 方法同时解读出来:

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

在序列中遍历时,索引位置和对应值可以使用 enumerate() 函数同时得到:

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

同时遍历两个或更多的序列,可以使用 zip() 组合:

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

要反向遍历一个序列,首先指定这个序列,然后调用 reversed() 函数:

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

要按顺序遍历一个序列,使用 sorted() 函数返回一个已排序的序列,并不修改原值:

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐