学习日志33 python
1 mean()
方法
在 Python 中,mean()
是一个用于计算平均值的方法,常见于数据分析库(如 pandas 和 numpy)中,也可以通过 Python 标准库的 statistics
模块使用。
1. pandas 中的 mean ()
在 pandas 中,mean()
方法可以用于 Series(一维数据)或 DataFrame(二维数据),计算数值的算术平均值。
示例:
python
运行
import pandas as pd# 对 Series 使用
s = pd.Series([1, 2, 3, 4, 5])
print(s.mean()) # 输出:3.0# 对 DataFrame 使用
df = pd.DataFrame({'A': [1, 2, 3],'B': [4, 5, 6]
})
print(df.mean()) # 按列计算平均值,输出 A:2.0, B:5.0
print(df.mean(axis=1)) # 按行计算平均值,输出 0:2.5, 1:3.5, 2:4.5
2. numpy 中的 mean ()
numpy 的 mean()
函数用于计算数组元素的平均值。
示例:
python
运行
import numpy as nparr = np.array([1, 2, 3, 4, 5])
print(np.mean(arr)) # 输出:3.0
3. Python 标准库 statistics 中的 mean ()
标准库 statistics
中的 mean()
函数可计算列表等可迭代对象的平均值。
示例:
python
运行
from statistics import meandata = [1, 2, 3, 4, 5]
print(mean(data)) # 输出:3.0
注意:
mean()
方法默认忽略NaN
(缺失值),这在处理实际数据时非常实用。- 它仅适用于数值型数据,对字符串等非数值类型会报错。
2 线程和协程
线程和协程都是实现程序并发执行的机制,但它们的本质和工作方式有显著区别:
1. 线程(Thread)
线程是操作系统能够进行调度的最小单位,隶属于进程,多个线程共享进程的资源(如内存空间、文件描述符等)。
- 特点:
- 由操作系统内核调度,切换时需要从用户态切换到内核态,开销较大(保存 / 恢复寄存器、栈等状态)。
- 每个线程有独立的栈空间(通常为几 MB),内存占用较高,系统能创建的线程数量有限(一般数千个)。
- 支持真正的并行:多线程可在多核 CPU 上同时运行。
- 若一个线程阻塞(如等待 I/O),操作系统会调度其他线程执行,不影响整体流程。
2. 协程(Coroutine)
协程是用户态的轻量级 “线程”,本质是程序自身控制的代码执行单元,完全运行在单个线程内。
- 特点:
- 由程序(用户代码)自主调度,无需操作系统介入,切换成本极低(仅需保存少量状态)。
- 多个协程共享所在线程的栈空间,内存占用极小(KB 级别),理论上可创建数百万个。
- 不支持并行:同一时间只有一个协程在运行(依赖单线程),无法利用多核 CPU。
- 若一个协程阻塞,会导致所在线程内的所有协程被阻塞,因此需配合非阻塞 I/O 使用。
核心区别总结
维度 | 线程 | 协程 |
---|---|---|
调度者 | 操作系统内核 | 程序自身(用户态) |
切换开销 | 大(内核态切换) | 极小(用户态切换) |
资源占用 | 高(独立栈,MB 级) | 极低(共享栈,KB 级) |
并行能力 | 支持多核并行 | 不支持(单线程内并发) |
适用场景 | CPU 密集型任务 | I/O 密集型任务(如网络请求) |
简单说:线程是 “操作系统管的并发单元”,适合利用多核;协程是 “程序自己管的轻量单元”,适合高并发 I/O 场景,两者常结合使用(如多线程 + 协程)以发挥最大效率。
3 query()
方法通过字符串表达式筛选数据(加@)
query()方法是pandas中用于数据筛选的高级方法,当需要引用DataFrame外部的变量时,必须使用@符号作为前缀。
这种设计可以明确区分DataFrame内的列名和外部变量,避免命名冲突。
在 pandas 中,query()
方法通过字符串表达式筛选数据时,若要引用 DataFrame 外部的变量,必须用 @
符号前缀,这是一个很重要的细节。
举例来说:
python
运行
import pandas as pddf = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [10, 20, 30, 40]})
threshold = 2 # 外部变量# 正确:用@引用外部变量threshold
result = df.query('A > @threshold')# 错误:不使用@会被误认为是DataFrame的列名
# result = df.query('A > threshold') # 会报错,因为df中没有名为threshold的列
这种设计的核心作用就是区分:
- 不带
@
的标识符:被解析为 DataFrame 中的列名 - 带
@
的标识符:被解析为当前作用域中的外部变量
这一机制有效避免了列名与外部变量同名时的冲突,让表达式更清晰、不易出错。
【为什么要特别设置一个pd模块里面的query方法?】
pandas 设计 query()
方法主要是为了提供一种更简洁、直观且可读性更强的数据筛选方式,尤其在处理复杂条件时优势明显。其核心价值体现在以下几个方面:
语法更接近自然语言,可读性更高
传统的筛选方式(如df[df['score'] > 80 & df['age'] < 20]
)需要嵌套多层索引和逻辑运算符,当条件复杂时容易变得冗长且易错。
而query()
允许直接用字符串表达式书写条件(如df.query('score > 80 and age < 20')
),结构更清晰,接近人类自然的表达习惯,降低了理解和维护成本。减少代码冗余,简化复杂条件
对于涉及多列、多逻辑的筛选(如包含and
/or
/in
/not in
等),query()
能显著减少重复书写的df['列名']
部分。
例如,筛选 “分数在 80-90 之间且年龄大于平均值” 的数据集:- 传统方式:
df[(df['score'] >= 80) & (df['score'] <= 90) & (df['age'] > df['age'].mean())]
query()
方式:df.query('80 <= score <= 90 and age > age.mean()')
后者更简洁,且避免了括号匹配错误的风险。
- 传统方式:
支持动态生成条件,灵活性更高
query()
的条件是字符串,这意味着可以通过字符串拼接动态生成筛选逻辑,尤其适合需要根据外部参数动态调整条件的场景。
例如:python
运行
condition = "score > 80" if need_filter_age:condition += " and age < 20" result = df.query(condition)
与外部变量交互更便捷
通过@
符号引用外部变量(如df.query('score > @threshold')
),既明确区分了 DataFrame 列名和外部变量,又避免了传统方式中需要嵌套df[]
和变量的繁琐(如df[df['score'] > threshold]
)。潜在的性能优化
在某些场景下(如处理大型 DataFrame 或复杂条件时),query()
会通过底层优化(如借助numexpr
库)提升计算效率,减少中间变量的创建,从而比传统索引方式更快。
总结来说,query()
方法的设计初衷是为了让数据筛选逻辑更直观、代码更简洁,同时兼顾灵活性和潜在的性能优势,尤其适合处理中等复杂度以上的筛选需求。
对于简单筛选,传统索引方式可能更直接;但当条件变得复杂时,query()
的优势会逐渐显现。
4 字符与 Unicode 编码值之间的转换
Python 中的 ord()
和 chr()
是一对互补的函数,用于字符与 Unicode 编码值之间的转换:
1. ord(c)
- 作用:返回单个字符
c
对应的 Unicode 编码值(整数)。 - 参数:必须是长度为 1 的字符串(即单个字符)。
- 示例:
python
运行
print(ord('A')) # 输出:65(大写字母A的Unicode编码) print(ord('中')) # 输出:20013(汉字"中"的Unicode编码) print(ord('😊')) # 输出:128522(表情符号的Unicode编码)
2. chr(i)
- 作用:返回 Unicode 编码值
i
对应的字符(字符串)。 - 参数:必须是有效的 Unicode 编码(整数,范围通常为 0 到 1,114,111)。
- 示例:
python
运行
print(chr(65)) # 输出:'A'(编码65对应的字符) print(chr(20013)) # 输出:'中'(编码20013对应的字符) print(chr(128522))# 输出:'😊'(编码128522对应的表情)
总结
ord(c)
:字符 → 编码(字符转数字)chr(i)
:编码 → 字符(数字转字符)
这两个函数在处理字符编码转换、密码学、文本处理等场景中非常常用,比如大小写字母转换(ord('a') - ord('A')
可得到差值 32)。
5 Python 解释器的核心实现语言是 C
“Python 是用 C 语言写成的” 这句话主要指的是 Python 解释器的核心实现语言是 C。
具体来说:
- Python 本身是一种编程语言(高级语言),有自己的语法规则和特性(如动态类型、缩进语法等)。
- 但要让计算机能理解并执行 Python 代码,需要一个 “翻译官”—— 这就是 Python 解释器。
- 目前最广泛使用的官方 Python 解释器(CPython),其底层核心代码是用 C 语言编写的。当你运行
python script.py
时,实际是启动了这个用 C 语言编译出的程序,由它来解析和执行你的 Python 代码。
这种设计的原因:
- 执行效率:C 语言是接近硬件的低级语言,执行速度快,用它实现解释器可以提高 Python 代码的运行效率。
- 系统交互:C 语言能直接操作操作系统接口和硬件资源,让 Python 可以方便地调用系统功能。
- 历史选择:Python 创始人 Guido van Rossum 在 1989 年设计 Python 时,选择用 C 实现解释器,这一传统延续至今。
补充:除了 CPython,还有其他语言实现的 Python 解释器(如用 Java 实现的 Jython、用 Python 实现的 PyPy 等),但 CPython 是官方默认且最常用的版本,所以通常说 “Python 用 C 写成” 指的就是这个语境。
6 PyCodeObject
以及代码编译后生成的对象数量
python是用C语言写成的,根据名字空间特性,以下代码经过python编译器编译后,一共得到()个PyCodeObject对象。
class A:pass
def Fun():pass
a = A()
Fun()A 1
B 2
C 3
D 4正确答案:C
什么是 PyCodeObject
?
PyCodeObject
是 Python 解释器(CPython)在编译代码时生成的一种底层数据结构,它本质上是代码的 “编译后表示”。简单说,Python 代码(文本形式)会先被编译成 PyCodeObject
(字节码和相关元信息),然后解释器再执行这个对象。
每个独立的作用域(名字空间) 都会会对应一个 PyCodeObject
,因为不同作用域的变量、逻辑是相互隔离的,需要单独编译。
题目代码的作用域分析
题目中的代码如下:
python
运行
class A:passdef Fun():passa = A()
Fun()
我们逐个分析其中的独立作用域,每个作用域对应一个 PyCodeObject
:
模块作用域(整个
.py
文件)
整个 Python 文件本身就是一个 “模块”,模块级别的代码(不在任何类或函数内部的代码)构成一个独立作用域。
题目中,a = A()
、Fun()
这两行代码属于模块级,同时类A
和函数Fun
的定义语句(class A:
、def Fun():
)本身也属于模块级代码(只是它们内部的内容属于其他作用域)。
这个作用域会生成 1 个PyCodeObject
。类作用域(
class A
的内部)
类的定义体(class A: ...
冒号后的代码块)是一个独立的作用域,用于存放类的属性、方法等。
题目中class A: pass
的pass
虽然是空语句,但类体本身仍是独立作用域,需要单独编译。
这个作用域会生成 第 2 个PyCodeObject
。函数作用域(
def Fun
的内部)
函数的定义体(def Fun(): ...
冒号后的代码块)也是独立作用域,用于存放函数内的变量、逻辑等。
题目中def Fun(): pass
的pass
同样是空语句,但函数体仍是独立作用域,需要单独编译。
这个作用域会生成 第 3 个PyCodeObject
。
为什么没有更多的 PyCodeObject
?
代码中没有其他独立作用域:
- 没有嵌套类(如
class A: class B: ...
) - 没有嵌套函数(如
def Fun(): def inner(): ...
) - 没有其他代码块(如
if
、for
等,这些属于所在作用域内部,不生成独立的PyCodeObject
)
结论
题目中的代码共有 3 个独立作用域,因此编译后会生成 3 个 PyCodeObject
,答案是 C。
这个概念的核心是:“独立作用域” 的数量 = PyCodeObject
的数量,而类、函数、模块是最典型的独立作用域。