学习日志18 python

1 输出的是单个字符or连续的字符串

python 复制代码
在Python3中,有关于break的用法:
for i in 'python':
    if i == 'h':
        break
    print(i)


A 'p','y','t','h','o','n'
B 'p','y','t'
C 'p','y','t','h'
D 'pyt'

正确答案:B
你的答案:D

在 Python 中,当你使用print()函数时,默认情况下每次调用都会在输出后添加一个换行符。

不过,你能够借助end参数对这一行为加以调整。下面通过具体例子来说明

python

运行

python 复制代码
# 示例1:默认情形,每次打印后会换行
for i in 'python':
    if i == 'h':
        break
    print(i)  # 这里会逐个输出,并且每个字符占一行

# 输出结果:
# p
# y
# t

# 示例2:把end参数设置为空字符串,就能让字符在同一行输出
for i in 'python':
    if i == 'h':
        break
    print(i, end='')  # 所有字符会在同一行输出

# 输出结果:
# pyt

题目里的代码使用的是默认的print(i),所以输出结果是:

plaintext

复制代码
p
y
t

这和选项 B 相契合。

选项 D('pyt')要想成立,代码得写成print(i, end='')才行。所以,这道题的正确答案是 B。

2 index()方法

index() 方法是 Python 字符串对象的内置方法,用于查找子字符串在原字符串中首次出现的位置(即起始索引)。它的基本用法和特性如下:

语法

python

运行

python 复制代码
str.index(sub[, start[, end]])
  • sub:必需,要查找的子字符串。
  • start(可选):开始搜索的索引位置,默认为 0。
  • end(可选):结束搜索的索引位置(不包含),默认为字符串长度。

关键点

  1. 索引从 0 开始:字符串的第一个字符索引是 0,第二个是 1,依此类推。
  2. 区分大小写"A""a" 被视为不同字符。
  3. 包含所有字符:空格、标点符号等均参与索引计算。
  4. 仅返回首次出现的位置:若子串多次出现,只返回第一次的索引。
  5. 找不到子串时抛出异常 :与 find() 方法不同,index() 在未找到子串时会抛出 ValueError

示例

python

运行

python 复制代码
s = "Hello,Python!"

# 查找 "Python" 的起始索引(P 在索引 6)
print(s.index("Python"))  # 输出: 6

# 从索引 7 开始查找 "o"
print(s.index("o", 7))    # 输出: 10(第二个 "o" 的位置)

# 在索引 0~5 之间查找 "l"
print(s.index("l", 0, 5)) # 输出: 2(第一个 "l" 的位置)

# 尝试查找不存在的子串(会报错)
print(s.index("Java"))    # 抛出 ValueError: substring not found

常见误区

  • 忽略非字母字符 :例如 "Hello,Python" 中的逗号 , 占索引 5,因此 "Python" 从索引 6 开始。
  • 混淆 index()find()
    • index():找不到子串时抛出异常。
    • find():找不到子串时返回 -1

建议

在使用 index() 时,若不确定子串是否存在,可先通过 sub in str 进行检查,避免程序崩溃:

python

运行

python 复制代码
s = "Hello"
if "World" in s:
    print(s.index("World"))
else:
    print("子串不存在")

理解索引的计算方式和 index() 的特性,就能轻松应对类似题目啦!

3 逗号在字符串中也是一个独立的字符,会占用一个索引位置

在字符串 "Hello,Python" 里,字符的排列和索引对应是这样的:

字符 H e l l o , P y t h o n
索引 0 1 2 3 4 5 6 7 8 9 10 11

可以看到,逗号(,)在索引 5 的位置,它后面的字母 P 才是子串 "Python" 的起始位置,对应索引 6。所以计算子串起始索引时,字符串中所有的字符(包括标点、空格等)都要算在内,一个都不能少

4 闭包(Closure)

闭包(Closure)是编程中的一个重要概念,在 Python、JavaScript、Swift 等许多语言中都存在。简单来说,闭包是一个函数及其捆绑的周边环境状态( lexical environment)的引用的组合

闭包的核心特性

  1. 嵌套函数:闭包必须包含一个嵌套在另一个函数内部的函数。
  2. 捕获变量:内部函数(闭包)会捕获外部函数的局部变量或参数。
  3. 延长作用域:即使外部函数执行结束,闭包仍能访问和修改外部函数的变量。

闭包的本质

闭包让函数可以 "记住" 它创建时的环境,即使该环境已经不存在(例如外部函数已返回)。闭包保存的是变量的引用,而非值的副本。

Python 中的闭包示例

python

运行

python 复制代码
def outer(x):
    def inner(y):
        return x + y  # 闭包捕获了外部变量x
    return inner  # 返回闭包

# 创建闭包实例
closure = outer(10)

# 调用闭包,此时outer函数已执行完毕,但闭包仍记得x=10
print(closure(5))  # 输出: 15
关键点解析
  • inner 是闭包 :它嵌套在 outer 内部,并引用了外部变量 x
  • x 被捕获 :当 outer(10) 执行时,x=10 被闭包捕获。
  • 闭包独立保存状态 :即使 outer 执行结束,闭包仍保留对 x 的引用。

闭包的常见场景

  1. 函数工厂:动态创建函数。

    python

    运行

    python 复制代码
    def multiplier(factor):
        return lambda x: x * factor  # 返回闭包
    
    double = multiplier(2)
    triple = multiplier(3)
    print(double(5))  # 输出: 10
    print(triple(5))  # 输出: 15
  2. 数据封装:模拟私有变量。

    python

    运行

    python 复制代码
    def counter():
        count = 0
        def increment():
            nonlocal count  # 声明使用外部变量
            count += 1
            return count
        return increment
    
    c = counter()
    print(c())  # 输出: 1
    print(c())  # 输出: 2

闭包的注意事项

1. 闭包捕获变量引用

闭包保存的是变量的引用,而非创建时的值。这可能导致意外结果:

python

运行

python 复制代码
def create_closures():
    closures = []
    for i in range(3):
        closures.append(lambda: i)  # 闭包捕获的是i的引用
    return closures

# 所有闭包共享同一个i,而i的最终值为2
for c in create_closures():
    print(c())  # 输出: 2, 2, 2
2. 解决方法:绑定变量值

通过默认参数将当前值绑定到闭包:

python

运行

python 复制代码
def create_closures():
    closures = []
    for i in range(3):
        closures.append(lambda x=i: x)  # 将i的值绑定到默认参数x
    return closures

# 每个闭包保存了自己的x值
for c in create_closures():
    print(c())  # 输出: 0, 1, 2

闭包 vs. 普通函数

特性 普通函数 闭包
捕获外部变量 无法直接访问外部函数变量 可以捕获并保留外部变量的引用
状态保存 不保存调用状态 保存创建时的环境状态
独立性 所有调用共享同一逻辑 每个闭包实例独立保存状态

闭包的应用场景

  • 事件处理:在 GUI 或前端框架中保存事件上下文。
  • 装饰器:在不修改原函数的情况下扩展功能。
  • 延迟计算:保存计算所需的参数,在需要时执行。

理解闭包的核心是**"捕获并保留变量引用"**,这是掌握闭包行为的关键。在循环中创建闭包时,尤其要注意变量绑定的时机。

5 闭包捕获的是变量的引用,而不是变量的值

python 复制代码
执行下列程序,输出结果为()

def fn():

    t = []

    i = 0

    while i < 2:

        t.append(lambda x: print(i*x,end=","))

        i += 1

    return t

for f in fn():

    f(2)

输出结果:4,4,

代码分析

这段代码的核心在于闭包对变量的引用方式,特别是在循环中创建闭包时容易出现的陷阱。我们分步来看:

  1. 函数 fn() 的执行过程

    • 创建一个空列表 t 和计数器 i(初始值为 0)。
    • 进入 while 循环(条件 i < 2):
      • 第一次循环:i=0,向列表 t 中添加一个匿名函数(闭包),该函数接受参数 x,打印 i*x 的值。
      • 计数器 i 增加到 1。
      • 第二次循环:i=1,再次向列表 t 中添加一个相同结构的匿名函数。
      • 计数器 i 增加到 2,循环结束。
    • 返回列表 t,此时列表中包含两个闭包函数。
  2. 主程序的执行过程

    • 调用 fn() 得到包含两个闭包的列表 t
    • 遍历这个列表,依次调用每个闭包函数,并传入参数 2(即执行 f(2))。

闭包的陷阱

关键问题在于:闭包捕获的是变量的引用,而不是变量的值

当闭包在后续被调用时,它们并不会使用创建时的 i 值(0 和 1),而是使用当前 i 的值。由于循环结束后 i 的最终值为 2,因此两次调用闭包时,i 的值都是 2,导致输出结果为:

  • 第一次调用:2 * 2 = 4
  • 第二次调用:2 * 2 = 4

如何修正

如果希望闭包捕获循环变量的当前值,可以通过函数参数来传递:

python

运行

python 复制代码
def fn():
    t = []
    i = 0
    while i < 2:
        # 使用默认参数捕获当前i的值
        t.append(lambda x, i=i: print(i*x, end=","))
        i += 1
    return t

for f in fn():
    f(2)  # 输出:0,2,

这里通过 i=i 将当前 i 的值绑定到闭包的默认参数中,使得每次创建的闭包都保存了当时的 i 值(0 和 1)。

总结

这个例子展示了 Python 闭包的一个重要特性:闭包捕获的是变量的引用,而不是变量的值。在循环中创建闭包时,需要特别注意这一点,避免所有闭包共享同一个变量的最终值。

逐行注释详解:

python

运行

python 复制代码
def fn():
    # 创建空列表t,用于存储后续生成的闭包函数
    t = []
    # 初始化计数器i,用于控制循环次数
    i = 0
    
    # 循环2次(i=0和i=1时满足条件)
    while i < 2:
        # 向列表t中添加一个匿名函数(闭包)
        # 该闭包接受参数x,打印i*x的结果(注意:闭包捕获的是i的引用,而非值)
        t.append(lambda x: print(i*x, end=","))
        # 计数器i加1
        i += 1
    
    # 循环结束后,i的值为2(不满足循环条件i<2)
    # 返回包含两个闭包函数的列表t
    return t

# 调用fn()获取闭包列表,并遍历调用每个闭包
for f in fn():
    # 调用闭包函数,传入参数2
    # 由于闭包捕获的i最终值为2,因此两次调用都输出2*2=4
    f(2)  # 输出结果:4,4,

关键解释

  1. 闭包捕获变量引用

    • 闭包内部的 i 是对外部变量 i 的引用,而非创建时的值。
    • 当闭包被调用时,Python 会查找当前 i 的值(此时循环已结束,i=2)。
  2. 循环执行过程

    • 第一次循环(i=0):添加闭包 lambda x: print(i*x, end=",")
    • 第二次循环(i=1):添加另一个相同结构的闭包
    • 循环结束后,i 最终值为 2
  3. 闭包调用结果

    • 每个闭包在调用时,i 的值都是 2,因此:
      • 第一次调用 f(2) 输出 2*2=4
      • 第二次调用 f(2) 同样输出 2*2=4

修正方案(保存变量值)

python

运行

python 复制代码
def fn():
    t = []
    i = 0
    while i < 2:
        # 使用默认参数i=i将当前i的值绑定到闭包
        t.append(lambda x, i=i: print(i*x, end=","))
        i += 1
    return t

for f in fn():
    f(2)  # 输出:0,2,(保存了创建时的i值)

通过默认参数 i=i,将当前 i 的值(0 和 1)保存到闭包内部,避免后续 i 的变化影响闭包结果。

相关推荐
武子康9 分钟前
Java-82 深入浅出 MySQL 内部架构:服务层、存储引擎与文件系统全覆盖
java·开发语言·数据库·学习·mysql·spring·微服务
倒悬于世9 分钟前
开源的语音合成大模型-Cosyvoice使用介绍
人工智能·python·语音识别
惜.己37 分钟前
pytest中使用skip跳过某个函数
开发语言·python·测试工具·pytest
挽风8212 小时前
Excel file format cannot be determined, you must specify an engine manually.
python
叫我:松哥3 小时前
基于网络爬虫的在线医疗咨询数据爬取与医疗服务分析系统,技术采用django+朴素贝叶斯算法+boostrap+echart可视化
人工智能·爬虫·python·算法·django·数据可视化·朴素贝叶斯
zylyehuo4 小时前
AirSim基础使用【Python】
python·drone
霜绛4 小时前
机器学习笔记(三)——决策树、随机森林
人工智能·笔记·学习·决策树·随机森林·机器学习
东方佑5 小时前
高效序列建模新突破:SamOut模型解读与21.79%损失改进
开发语言·python
站住前面的二哈5 小时前
Cartographer安装测试与模块开发(三)--Cartographer在Gazebo仿真环境下的建图以及建图与定位阶段问题(实车也可参考)
学习·ubuntu
ahauedu5 小时前
用Java 代码实现一个简单的负载均衡逻辑
java·python·负载均衡