filter()
函数filter(function, iterable)
filter函数是python中的高阶函数, 第一个参数是一个筛选函数, 第二个参数是一个可迭代对象, 返回的是一个生成器类型, 可以通过next获取值。
filter()
函数是 Python 内置的高阶函数,其主要功能是对可迭代对象中的每个元素运用筛选函数进行判断,然后把符合条件的元素以生成器的形式返回。下面为你详细介绍它的用法和特性:
python
运行
filter(function, iterable)
这里的参数 function
是一个用于筛选元素的函数,它会返回布尔值;iterable
则是一个可迭代对象,像列表、元组、集合等都属于此类。
filter()
返回的是一个生成器对象,这意味着它是惰性求值的,只有在需要的时候才会生成值,这样能有效节省内存。function
返回 True
时,对应的元素会被保留;若返回 False
,则该元素会被过滤掉。要是 function
为 None
,filter()
会保留 iterable
中所有布尔值为 True
的元素。python
运行
numbers = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens)) # 输出: [2, 4, 6]
python
运行
words = ["hello", "", "world", " ", None, "python"]
valid_words = filter(None, words) # 不传筛选函数时,会过滤掉布尔值为 False 的元素
print(list(valid_words)) # 输出: ['hello', 'world', 'python']
python
运行
numbers = [1, 2, 3, 4, 5]
filtered = filter(lambda x: x > 3, numbers)
print(next(filtered)) # 输出: 4
print(next(filtered)) # 输出: 5
你也可以通过列表推导式或者生成器表达式来实现相同的筛选功能:
python
运行
# 列表推导式
evens = [x for x in numbers if x % 2 == 0]
# 生成器表达式(更节省内存)
evens_gen = (x for x in numbers if x % 2 == 0)
filter()
函数特别适合用于数据筛选场景,它返回的生成器能高效处理大规模数据。不过在 Python 3 中,需要注意 filter()
返回的不再是列表,而是生成器,这一点和 Python 2 有所不同。
filter()
函数会保留 function
返回值为 True
的元素,过滤掉返回 False
的元素。具体规则如下:
当 function
是自定义函数或 lambda 时:
只有当函数对元素的计算结果为 True
时,该元素才会被保留。
python
运行
numbers = [1, 2, 3, 4]
result = filter(lambda x: x > 2, numbers) # 保留大于 2 的数
print(list(result)) # 输出: [3, 4]
当 function
为 None
时:
会自动过滤掉任何布尔值为 False
的元素(如 None
、0
、空字符串 ""
、空列表 []
等)。
python
运行
values = [0, "", None, "hello", 5]
result = filter(None, values) # 保留布尔值为 True 的元素
print(list(result)) # 输出: ['hello', 5]
可以将 filter()
函数等价理解为一个生成器表达式:
python
运行
# 以下两种写法等价
filter(function, iterable)
(x for x in iterable if function(x)) # 当 function 不为 None 时
(x for x in iterable if x) # 当 function 为 None 时
返回值是生成器:需要用 list()
转换为列表(或用 next()
逐个获取值)。
函数参数的顺序:function
在前,iterable
在后,不要混淆。
以下代码运行结果为:
func = lambda x:x%2
result = filter(func, [1, 2, 3, 4, 5])
print(list(result))
正确答案:[1,3,5]
char
(字符)类型Python的主要内置数据类型包括:
- 数值类型:int(整数)、float(浮点数)、complex(复数)
- 序列类型:list(列表)、tuple(元组)、range(范围)
- 字符串类型:str - 映射类型:dict(字典)
- 集合类型:set(集合)、frozenset(不可变集合)
- 布尔类型:bool
- 空值类型:None
所以char并不在Python的数据类型体系中。
而是用长度为 1 的字符串 str
来表示单个字符。例如:
python
运行
char = 'A' # 这是一个长度为 1 的字符串,而非 char 类型
print(type(char)) # 输出:
char
类型?str
类型默认支持 Unicode,直接用 str
表示字符更符合国际化需求。char
是基本类型,而 Python 一切皆对象)。虽然没有 char
类型,但可以通过索引访问字符串中的字符:
python
运行
s = "hello"
first_char = s[0] # 'h'
print(type(first_char)) #
语言 | 字符类型 | 字符串类型 |
---|---|---|
Python | str (长度 = 1) |
str |
Java | char |
String |
C/C++ | char |
char[] 或 std::string |
如果你需要处理字符编码(如 ASCII/Unicode 值),可以使用 ord()
和 chr()
函数:
python
运行
char = 'A'
code = ord(char) # 65 (Unicode 码点)
back_to_char = chr(code) # 'A'
这种设计让 Python 在处理文本时更加统一和灵活,无需担心字符与字符串的类型转换问题。
在 Python 中,生成一个 PyCodeObject
对象 意味着将源代码编译为 Python 虚拟机(VM)可以执行的中间表示形式,也称为 字节码(bytecode)。这个过程是 Python 执行代码的关键步骤,理解它有助于掌握 Python 的运行机制。
PyCodeObject
?PyCodeObject
是 CPython 解释器(用 C 语言实现的 Python 官方解释器)中的一个核心数据结构,它存储了编译后的代码信息,包括:
LOAD_CONST
、CALL_FUNCTION
等,类似于汇编语言。可以将 PyCodeObject
看作是源代码的 编译产物,类似于 Java 的 .class
文件或 C 的目标文件(.o
)。
PyCodeObject
?Python 是 解释型语言,但执行前需要先将源代码编译为字节码,原因如下:
.pyc
文件)。PyCodeObject
的过程当你运行 Python 代码时:
def
、class
、变量名)。PyCodeObject
(包含字节码)。这个过程由 Python 解释器自动完成,例如:
python
运行
def add(a, b):
return a + b
# 编译后,add 函数对应一个 PyCodeObject,包含以下字节码:
# 2 0 LOAD_FAST 0 (a)
# 2 LOAD_FAST 1 (b)
# 4 BINARY_ADD
# 6 RETURN_VALUE
PyCodeObject
?以下情况会生成独立的 PyCodeObject
:
.py
文件):每个文件有一个全局的 PyCodeObject
。def
语句生成一个(包括 lambda 函数)。class
语句生成一个(类体中的代码会被编译)。(x for x in range(10))
。可以使用 dis
模块反汇编 PyCodeObject
:
python
运行
import dis
def add(a, b):
return a + b
dis.dis(add) # 打印字节码指令
输出:
plaintext
2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
生成 PyCodeObject
是 Python 执行代码的必经之路,它将源代码转换为可执行的字节码,让 Python 兼具解释型语言的灵活性和编译型语言的部分性能优势。理解这一点有助于解释一些 Python 特性,如:
PyCodeObject
)。.pyc
缓存)。PyCodeObject
保存了闭包信息)。题目
Python 代码经过编译后,一共生成多少个 PyCodeObject 对象?
python
运行
class A:
pass
def Fun():
pass
a = A()
Fun()
选项
A. 1
B. 2
C. 3
D. 4
你的答案
B. 2
正确答案
C. 3
混淆作用域的定义
a = A()
)和函数调用(Fun()
)视为独立作用域。对 PyCodeObject 的生成规则理解不深
python
运行
# 作用域1:模块级别(全局命名空间)
class A: # 作用域2:类 A 的命名空间
pass
def Fun(): # 作用域3:函数 Fun 的命名空间
pass
a = A() # 全局命名空间中的语句
Fun() # 全局命名空间中的语句
PyCodeObject 生成规则
结构 | 是否生成 PyCodeObject | 示例 |
---|---|---|
模块(.py 文件) |
✅ | 整个代码文件 |
类定义(class ) |
✅ | class A: pass |
函数定义(def ) |
✅ | def Fun(): pass |
实例化对象 | ❌ | a = A() |
函数调用 | ❌ | Fun() |
普通语句 | ❌ | 赋值、条件判断、循环等 |
验证方法
通过 __code__
属性查看对象对应的 PyCodeObject(实际是 code
对象):
python
运行
print(A.__code__) # 类体对应的 code 对象
print(Fun.__code__) # 函数体对应的 code 对象
print(__code__) # 当前模块对应的 code 对象
作用域划分口诀
模块类函数,作用域三分;实例与调用,作用域不分。
嵌套作用域示例
python
运行
def outer(): # 作用域1
x = 10
class Inner: # 作用域2
pass
def inner(): # 作用域3
y = 20
return inner
上述代码包含 3 个 PyCodeObject(outer
、Inner
、inner
各一个)。
下次遇到同类题目的思考步骤
执行以下程序,输出结果为()
a = [['1','2'] for i in range(2)]
b = [['1','2']]*2
a[0][1] = '3'
b[0][0] = '4'
print(a,b)
A [['1', '3'], ['1', '3']] [['4', '2'], ['4', '2']]
B [['1', '3'], ['1', '2']] [['4', '2'], ['4', '2']]
C [['1', '3'], ['1', '2']] [['4', '2'], ['1', '2']]
D [['1', '3'], ['1', '3']] [['4', '2'], ['1', '2']]
答案:B
方式 | 语法示例 | 对象关系 | 内存特性 |
---|---|---|---|
列表推导式 | a = [['1','2'] for _ in range(2)] |
创建多个独立对象 | 子列表内存地址不同 |
乘法操作 | b = [['1','2']] * 2 |
复制同一对象的引用 | 子列表内存地址相同 |
浅拷贝:
仅复制容器(如列表)本身,内部元素仍为原对象的引用。
示例:b = [['1','2']] * 2
中,子列表 ['1','2']
被重复引用。
深拷贝:
递归复制容器及其所有嵌套对象,生成完全独立的新对象。
实现方式:使用 copy.deepcopy()
函数。
独立对象(列表推导式):
修改任一子列表仅影响当前对象。
示例:a[0][1] = '3'
仅修改 a[0]
。
共享引用(乘法操作):
修改任一子列表会影响所有引用同一对象的元素。
示例:b[0][0] = '4'
同时修改 b[0]
和 b[1]
。
使用 id()
函数检查内存地址:
python
运行
a = [['1','2'] for _ in range(2)]
b = [['1','2']] * 2
print(id(a[0]) == id(a[1])) # False(独立对象)
print(id(b[0]) == id(b[1])) # True(共享引用)
多维数组初始化:
初始化多维列表时,避免使用 [[val]*n]*m
(如 [[0]*3]*3
),应使用列表推导式 [[val for _ in range(n)] for _ in range(m)]
。
数据处理场景:
在需要独立操作子元素的场景(如矩阵运算、数据分组)中,确保使用独立对象初始化。
id()
或 is
操作符验证对象身份,避免因引用共享导致的意外修改。为了更直观地验证这一行为,我们可以通过以下代码检查对象的内存地址:
python
运行
a = [['1','2'] for i in range(2)]
b = [['1','2']]*2
print(f"a[0] 地址: {id(a[0])}") # 不同地址
print(f"a[1] 地址: {id(a[1])}")
print(f"b[0] 地址: {id(b[0])}") # 相同地址
print(f"b[1] 地址: {id(b[1])}")
a[0][1] = '3'
b[0][0] = '4'
print("修改后 a:", a)
print("修改后 b:", b)
输出结果:
plaintext
a[0] 地址: 140281423443648
a[1] 地址: 140281423443904
b[0] 地址: 140281423443776
b[1] 地址: 140281423443776
修改后 a: [['1', '3'], ['1', '2']]
修改后 b: [['4', '2'], ['4', '2']]
关键点:
a
的子列表内存地址不同,说明它们是独立的对象。b
的子列表内存地址相同,说明它们指向同一个对象。b
的任一子列表的修改会反映到所有引用中,而 a
的子列表相互独立。这一特性在处理嵌套数据结构时尤为重要,例如矩阵操作或多维数组的初始化。理解引用机制可以避免潜在的逻辑错误。
理解这些特性可以有效避免 Python 中常见的引用陷阱,提升代码的健壮性。