type
status
date
slug
summary
tags
category
icon
password
tips:文章内容均来自对于Python-100-Days的学习
生成式(推导式)
生成式可以用来生成列表、集合、字典,生成式的效率很高,极推荐使用。
注意:嵌套式列表的生成式推导:
如果使用两个
*
来生成scores,第一个*
结束后会生成[None, None, None]
。然后 [...] * len(names)
会将这个 [None, None, None]
复制 5 次。表面上看起来没问题,但实际上这里有一个浅拷贝的问题。*
操作符在复制列表时,只是复制了引用,而不是创建新的列表对象。因此,实际上 scores
中的五个子列表都是同一个列表对象的引用。所有的列表都是同一个对象。进行操作时只会保留最后一个学生的成绩。而使用正确的列表推导式会执行
len(names)
次(这里是 5 次),每次都会创建一个新的 [None] * len(courses)
列表。因此,每个子列表都是独立的对象。
除了列表推导式之外还可以使用:这种显式追加的方式也可以保证每个子列表都是独立创建的。
Q:为什么
[None] * len(courses)
没问题?A:因为
None
是不可变对象(immutable),而子列表是可变对象(mutable)。对于不可变对象,
*
复制的是值;而对于可变对象,*
复制的是引用。
heapq
模块(堆排序)
itertools
模块
迭代工具模块
优势:
- 只在需要的时候生成元素,节省内存
- 底层使用C实现,比纯Python代码快
注意:
- 使用后迭代器会被消耗,需要重新创建
- groupby()函数在使用前需要先排序
collections
模块
collections
模块提供了许多高性能的容器数据类型,可以用来替代Python内置的基本容器,例如:dict,list,set,tuple等.Counter
计数器
Counter
可以用来统计可哈希对象出现的次数most_common()
方法可以帮助我们获取出现频率最高的元素。defaultdict
默认字典
可以在使用字典的时候主动为不存在的键提供默认值
deque
双端队列
高效的首尾插入和删除操作
常用简单算法
- 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
- 贪心法 - 在对问题求解时,总是做出在当前看来最好的选择,不追求最优解,快速找到满意解。
- 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。
- 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。
- 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。
关于函数
- 函数可以赋值给变量
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
高阶函数的用法
map()
是 Python 的一个内置高阶函数,用于将一个函数应用到可迭代对象(如列表、元组等)的每个元素上,并返回一个迭代器,包含所有经过函数处理的结果。
map(function, iterable, ...)
特性 | map+filter 方式 (items1 ) | 列表推导式 ( items2 ) |
可读性 | 较低(嵌套函数调用) | 更高(直观的过滤和转换) |
性能 | 相近(Python 3中两者效率相当) | 相近 |
适用场景 | 函数式编程风格 | Pythonic 的推荐写法 |
扩展性 | 适合已有现成的函数 | 适合简单直接的转换和过滤 |
闭包与作用域问题
- Python搜索变量的LEGB顺序(Local >>> Embedded >>> Global >>> Built-in)
局部作用域(函数内部) L
嵌套函数的外层作用域(闭包) E
模块\全局作用域 G
内置作用域(Python内置的变量和函数) B
global
和nonlocal
关键字的作用global
:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。nonlocal
:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。
延迟绑定
延迟绑定是指在闭包中引用的外部变量不是在函数定义时来确定值,而是在函数调用的时候才进行查找之后再确定的.
为什么会这样?
- 变量绑定时机:Python 的闭包是通过名称引用而非值捕获
- 循环结束后:所有
func
都引用同一个变量i
,而i
的最终值是 2
- 调用时才求值:当实际调用
func()
时才会查找i
的值
方法1:使用默认参数立即绑定
原理:
- 默认参数在函数定义时求值并固定
- 每个
func
都有自己的i
参数副本
方法2:闭包工厂函数
原理:
- 每次循环调用
make_func
都会创建新的作用域
- 每个闭包捕获自己独立的
i
值
方法3:使用 functools.partial
更复杂的版本
延迟绑定的本质原因
- Python 的闭包实现:通过名称引用而非值捕获
- LEGB 查找规则:函数调用时才查找变量值
- 循环变量的作用域:在 Python 中,循环变量不会为每次迭代创建新作用域
总结对比
解决方案 | 适用场景 | 优点 | 缺点 |
默认参数 | 简单场景 | 代码简洁 | 需要修改函数签名 |
工厂函数 | 复杂场景 | 逻辑清晰 | 需要额外函数 |
partial | 函数式编程 | 灵活 | 需要导入 |
如果需要捕获循环变量的当前值,必须通过某种方式立即绑定该值。
装饰器函数的使用
例子:输出函数执行时间的装饰器。
如果装饰器不希望跟
print
函数耦合,可以编写可以参数化的装饰器。说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过 func.__wrapped__方式获得被装饰之前的函数或类来取消装饰器的作用。
例子:用装饰器来实现单例模式。
提示:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
线程安全的单例装饰器。
提示:上面的代码用到了 with上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持 __enter__和 __exit__魔术方法)。在 wrapper函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。