跳到主要内容

Python期末复习

Round四舍五入

在Python中,round() 是一个内置函数,用于对浮动数字进行四舍五入。它可以根据指定的小数位数来调整数字。下面是 round() 函数的基本用法和一些详细解释。

基本语法

round(number, ndigits)
  • number:要四舍五入的数字。
  • ndigits:指定要保留的小数位数(默认为0)。

使用示例

  1. 不指定小数位数(四舍五入到最接近的整数)
print(round(3.14159))  # 输出 3
print(round(2.71828)) # 输出 3
  1. 指定保留的小数位数
print(round(3.14159, 2))  # 输出 3.14
print(round(2.71828, 3)) # 输出 2.718
  1. 负数的小数位数(四舍五入到指定的十位、百位等)

如果 ndigits 为负数,那么就会对数字的小数点左侧的数字进行四舍五入。

print(round(12345, -1))  # 输出 12340,四舍五入到十位
print(round(12345, -2)) # 输出 12300,四舍五入到百位

特殊情况

  1. 四舍五入的规则:在Python中,round() 使用的是银行家舍入法(round half to even),即在四舍五入时,如果数字刚好在两个整数的中间,Python会将它四舍五入到最接近的偶数。这样可以减少在大量运算时的累积误差。
print(round(0.5))  # 输出 0(四舍五入到最近的偶数)
print(round(1.5)) # 输出 2(四舍五入到最近的偶数)

小结

  • round() 函数可以四舍五入浮动数字到指定的小数位数。
  • 默认情况下,它四舍五入到整数(ndigits=0)。
  • 可以通过传递负数的 ndigits 来指定四舍五入到小数点左侧。
  • 使用银行家舍入法时,数字恰好为中间值时,Python会选择最接近的偶数。

这种用法在处理财务或科学计算时特别有用,可以避免因单次计算中的四舍五入而导致的累计误差。

round(x,6)和.6f 两种四舍五入的区别

主要区别在于 输出格式(有没有固定显示 6 位小数)以及 Python 对浮点数的默认打印行为:

方式四舍五入精度输出时是否保留多余的"0"例子(假设 s=2.5)
s = round(s,6)``print(f"{s}")6 位2.5
print(f"{s:.6f}")6 位2.500000

细节说明

  1. round(s,6) 只是对浮点数做了"数学上"四舍五入到 6 位
    • 它返回一个 float,值在数学上已经精确到 6 位小数。
    • 但 Python 打印一个 float(即 str(s))时,会采用"最简洁"表示,不会额外补 "0"。
  2. f"{s:.6f}" 是格式控制
    • 它在内部也会做四舍五入(与 round 用同样的"银行家舍入"原则),
    • 然后保证输出时总共 6 位小数,不足的用"0"补齐。

举例

s = 1.2345

# 你的写法
s = round(s, 6)
print(f"{s}") # 输出: 1.2345

# 我的写法
print(f"{s:.6f}") # 输出: 1.234500

当希望结果严格显示 6 位小数(包括末尾的 0)时,就要用 :.6f;如果只是想数学上保留 6 位精度,但不介意少几个 0,就可以用 round(s,6) 然后默认打印。

格式化字符串

在 Python 中,% 操作符就是所谓的"旧式"字符串格式化(也叫 printf 风格格式化),它的基本思路是:

"格式字符串" % 值或元组或字典

——其中,格式字符串里用一系列 % 开头的格式说明符来表示要插入的变量占位,后面的值会依次(或按名字)填入这些占位符。下面分步讲解它的用法。


1. 基本语法

print("Hello, %s! You have %d messages." % ("Alice", 5))
  • %s:字符串占位符
  • %d:十进制整数占位符

占位符的顺序要和后面提供的值一致。如果只有一个值,也可以不写元组,直接写单个值:

name = "Bob"
print("Hi, %s!" % name)

2. 常见格式说明符

占位符类型说明
%s字符串会把任意对象先用 str() 转为字符串再插入
%r原始字符串会把任意对象用 repr() 转为带引号的形式插入
%d十进制整数用于整数
%f浮点数默认保留 6 位小数
%.nf浮点数保留 n 位小数,比如 %.1f 保留 1 位小数
%x/%X十六进制整数小写/大写字母表示
%%百分号输出一个 % 本身

3. 精度与宽度控制

格式说明符可以更进一步指定"宽度"和"对齐":

%[映射键][标志][宽度][.精度]类型
  • 宽度(width):指定该字段至少占多少字符,不够则默认左填空格
  • 精度(.precision):
    • 对浮点数,精度指小数点后保留几位
    • 对字符串,精度指最大取多少个字符
  • 标志(flags):
    • -:左对齐
    • +:正数前显示 +
    • 0:数字左边补 0 (在指定宽度时)
num = 3.14159
print("默认:%f" % num) # 默认 6 位小数
print("两位:%.2f" % num) # 小数点后 2 位
print("宽度8:%8.2f" % num) # 总宽度 8(含小数点和小数位),右对齐
print("宽度8左对齐:%-8.2f" % num)
print("补0:%08.2f" % num)

4. 元组与字典格式化

  • 元组:按位置依次填充

    a, b = 10, 20
    print("a=%d, b=%d" % (a, b))
  • 字典:用 %(key)s%(key)d 等方式,通过键名填充

    data = {"name": "小明", "score": 95}
    print("学生:%(name)s,分数:%(score)d" % data)

5. 结合你给出的例子

print("BMI=%.1f, 您的体重%s!" % (bmi, weight))
  • %.1f:表示将 bmi (假设是浮点数)格式化为保留 1 位小数
  • %s:将 weight (任意类型)转为字符串插入
  • 注意:这里用的是一个元组 (bmi, weight) 来传递两个值

如果想让输出更整齐,还可以加宽度、对齐、符号等:

print("BMI=%+6.1f, 您的体重:%5skg" % (bmi, weight))

6. 小结与进阶

  • 优点:简单直观、借鉴 C 语言的 printf
  • 缺点:当变量较多、嵌套复杂时可读性差
  • 更现代的替代
    • .format() 方法:"BMI={:.1f}, 体重{}kg".format(bmi, weight)
    • f-string(Python 3.6+):f"BMI={bmi:.1f}, 体重{weight}kg"

f-string 不仅语法更简洁,还能直接在花括号里写表达式,更加灵活。


掌握了这些,要点就在于记住常用的格式说明符和可选的宽度/精度/对齐标志,多练习几次,你就能很熟练地用 % 来格式化输出了。

str.join()

在 Python 中,str.join() 是将一个可迭代对象中的多个字符串用指定分隔符连接成一个新字符串的高效方法。下面从基础到进阶、从注意事项到最佳实践,给你一个全方位的讲解。


1. 基本语法

separator.join(iterable)
  • separator:用来分隔各个元素的字符串(可以是空串 ""、空格 " "、逗号 ","、换行符 "\n" 等)。
  • iterable:可迭代对象,其中的每一项都必须是字符串类型str);如果有非字符串类型,会抛出 TypeError

返回值是一个新的字符串,等于将 iterable 中的各元素,以 separator 依次拼接起来。


2. 最简单的例子

words = ["apple", "banana", "cherry"]
result = ",".join(words)
print(result) # apple,banana,cherry
  • 以逗号连结列表中的单词。

如果想在单词之间加一个空格:

" ".join(words)  # "apple banana cherry"

若想把字符反转再连回去(也能用于字符串本身,因为字符串也是可迭代的):

s = "hello"
rev = "".join(reversed(s)) # "olleh"
# 或者直接 s[::-1],但这里是 join 的示例

3. 常见用法及场景

  1. 列表 → 字符串: 把多个单词、数字(先转成字符串)拼成一句话或一个文件路径、URL 参数等。

    parts = ["2025", "04", "29"]
    date_str = "-".join(parts) # "2025-04-29"
  2. 元组、集合

    tpl = ("a", "b", "c")
    result = "|".join(tpl) # "a|b|c"

    st = {"x", "y", "z"}
    result = ":".join(st) # 可能是 "y:z:x"(集合无序)
  3. 生成器表达式: 当元素需要动态计算或类型转换时,常和 map() 或生成器表达式结合:

    nums = [1, 2, 3, 4]
    s = ",".join(str(n) for n in nums)
    # "1,2,3,4"
  4. 文件操作里的换行: 将多行内容一次性写入文件:

    lines = ["第一行", "第二行", "第三行"]
    with open("out.txt", "w", encoding="utf-8") as f:
    f.write("\n".join(lines))
  5. 拆分(Reverse): 虽然 .join() 本身是拼接,但经常和 .split()、切片、reversed() 配合,完成拆分-处理-重拼接的流程。


4. 性能考量

  • 为什么不用简单的 +=

    s = ""
    for w in words:
    s += w + ","

    这种方式会在每次循环时创建新字符串,时间复杂度和空间开销都更高。

  • join() 优势 它在内部先计算出结果长度,只分配一次内存,然后一次性写入,大幅提升效率——尤其当拼接上百、上千个片段时差异尤为明显。


5. 注意事项

  1. 元素必须是 str

    data = ["a", 123, "b"]
    "-".join(data)
    # TypeError: sequence item 1: expected str instance, int found

    解决方法:先把非字符串元素转换好:

    "-".join(map(str, data))
  2. 空可迭代对象

    ",".join([])    # 返回 ""(空字符串)
  3. 在字节序列上 bytesbytearray 类型也有各自的 .join(),但它们要求可迭代对象成员都是 bytesbytearray

    b",".join([b"foo", b"bar"])  # b"foo,bar"
  4. 大规模拼接 如果遇到极大规模的数据流,可以考虑 io.StringIO 或者写文件流中直接拼接,以进一步减少内存峰值。


6. 进阶示例

6.1 只保留奇数位字符并合并

s = "abcdefghijklmnopqrstuvwxyz"
# 取下标为奇数的字符,然后 join
res = "".join(s[i] for i in range(len(s)) if i % 2 == 1)
print(res) # "bdfhjlnprtvxz"

6.2 拼接带 HTML 标签的列表

items = ["apple", "banana", "cherry"]
html = "<li>" + "</li><li>".join(items) + "</li>"
# 结果: "<li>apple</li><li>banana</li><li>cherry</li>"

6.3 用生成器批量拼接日志行

def log_lines():
for i in range(1, 6):
yield f"[{i:03d}] 处理完成"

all_logs = "\n".join(log_lines())
print(all_logs)

7. 小结

  • 核心语义:拿一个"分隔符"去"连接"一系列字符串。
  • 优势:简洁、内存和速度都优于循环累加。
  • 常见陷阱:务必保证可迭代中全是字符串类型;对于大型数据流,注意内存峰值。

掌握了 join(),你就能用最"Pythonic" 的方式,快速高效地完成各种字符串拼接需求!

NumPy基础教程

1. NumPy是什么?

NumPy(Numerical Python)是Python科学计算的核心库,主要用于处理数组和矩阵运算。它提供了比Python列表更高效的数值计算工具。

2. 安装NumPy

pip install numpy

3. 导入NumPy

import numpy as np  # 约定俗成的导入方式

4. 创建NumPy数组

4.1 从列表创建数组

# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # 输出: [1 2 3 4 5]

# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)

4.2 特殊数组创建

# 创建全0数组
zeros = np.zeros(5) # 一维全0数组
zeros_2d = np.zeros((3, 4)) # 3行4列的二维全0数组

# 创建全1数组
ones = np.ones(5) # 一维全1数组
ones_2d = np.ones((2, 3)) # 2行3列的二维全1数组

# 创建特定范围的数组
# start, stop, step
range_arr = np.arange(0, 10, 2) # [0 2 4 6 8]

# 创建均匀分布的数组
# start, stop, num(元素个数)
linspace_arr = np.linspace(0, 10, 5) # 0到10之间均匀分布的5个数

4.3 随机数组

# 设置随机数种子(保证可重复性)
np.random.seed(42)

# 生成0-1均匀分布的随机数
uniform_arr = np.random.uniform(0, 1, 5)

# 生成标准正态分布的随机数
normal_arr = np.random.normal(0, 1, 5)

# 生成随机整数
random_int_arr = np.random.randint(0, 10, 5) # 0-9之间的5个随机整数

5. 数组属性

arr = np.array([[1, 2, 3], [4, 5, 6]])

# 数组维度
print(arr.ndim) # 输出: 2

# 数组形状
print(arr.shape) # 输出: (2, 3)

# 数组元素类型
print(arr.dtype) # 输出: int64

# 数组元素总数
print(arr.size) # 输出: 6

6. 数组运算

6.1 基本运算

# 数组与标量运算
arr = np.array([1, 2, 3, 4])
print(arr * 2) # 每个元素乘2: [2 4 6 8]
print(arr + 10) # 每个元素加10: [11 12 13 14]

# 数组间运算
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
print(arr1 + arr2) # 对应位置相加: [5 7 9]
print(arr1 * arr2) # 对应位置相乘: [4 10 18]

6.2 统计函数

arr = np.array([1, 2, 3, 4, 5])

# 基本统计
print(np.sum(arr)) # 求和:15
print(np.mean(arr)) # 平均值:3.0
print(np.max(arr)) # 最大值:5
print(np.min(arr)) # 最小值:1
print(np.std(arr)) # 标准差

7. 数组切片和索引

# 一维数组
arr1 = np.array([0, 1, 2, 3, 4, 5])
print(arr1[2:5]) # 索引2到4: [2 3 4]
print(arr1[:3]) # 前3个元素: [0 1 2]
print(arr1[::2]) # 每隔2个取一个: [0 2 4]

# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2[1, 2]) # 第2行第3列的元素:6
print(arr2[:, 1]) # 第2列所有元素: [2 5 8]
print(arr2[0:2, 1:3]) # 前2行,第2-3列

8. 数组重塑

# 改变数组形状
arr = np.arange(6)
reshaped = arr.reshape((2, 3)) # 变成2行3列
print(reshaped)

# 展平数组
flatten = reshaped.flatten() # 变回一维数组

9. 常用实用函数

# 生成单位矩阵
identity = np.eye(3)

# 对角矩阵
diag = np.diag([1, 2, 3])

# 矩阵转置
arr = np.array([[1, 2], [3, 4]])
print(arr.T)

总结

NumPy提供了强大的数组操作和数值计算工具,是科学计算、数据分析的基础库。

推荐学习路径

  1. 熟悉基本数组创建
  2. 掌握数组运算
  3. 理解切片和索引
  4. 学习统计函数
  5. 探索更高级的矩阵操作

练习建议

  • 尝试创建不同类型和维度的数组
  • 实践各种数组运算
  • 解决实际的数值计算问题

NumPy知识

在这行代码里:

points_in_quarter_circle = np.sum(distances <= 1)

你看到了一个非常典型的 NumPy 向量化(vectorization)写法,它背后其实做了两件事:

  1. 元素级(element-wise)比较,生成布尔数组 表达式 distances <= 1distances 是一个形状为 (N,) 的 NumPy 数组时,会对数组中每一个元素分别做一次比较,返回一个同样形状的布尔数组:

    # 举例说明
    distances = np.array([0.5, 1.2, 0.8, 1.0, 1.5])
    mask = distances <= 1
    # mask == array([ True, False, True, True, False])

    这一步完全是在 C 级别实现的,非常快,比起 Python 的 for 循环要高效得多。

  2. 对布尔数组求和 NumPy 在对布尔类型数组调用 np.sum() 时,会把 True 当作 1False 当作 0,然后把所有元素加起来。这就恰好给了我们「有多少个点满足条件」的结果:

    np.sum(mask)
    # 等价于 1 + 0 + 1 + 1 + 0 == 3

合起来,np.sum(distances <= 1) 就是:

  • 先判断每个 distance 是否 ≤ 1,得到一个只有 True/False 的数组;
  • 再把这些 True/False 当作 1/0 累加,得到满足条件的点的数量。

为什么要这样写?

  1. 简洁 只用一行就完成了筛选和计数。
  2. 高效 比起 Python 层面的 for 循环和 if 判断,NumPy 的向量化运算是在底层 C/C++ 实现的,速度快、内存访问连续。
  3. 可读 逻辑直观:"把所有 ≤1 的点当作 1,加一加就是总数"。

类似的写法

  • np.count_nonzero(distances <= 1) 功能等价,也是统计 Boolean 数组中 True 的个数。
  • (distances <= 1).sum() 直接调用布尔数组的 .sum() 方法,同样把 True 当成 1。
inside1 = np.sum(distances <= 1)
inside2 = np.count_nonzero(distances <= 1)
inside3 = (distances <= 1).sum()

assert inside1 == inside2 == inside3

扩展:用布尔掩码(mask)做更多事

除了统计个数,你还可以用这个布尔掩码来 选择修改 数组中的元素:

mask = distances <= 1

# 1) 取出所有圆内的距离值
in_distances = distances[mask]

# 2) 把圆外的距离都设为 0
distances_outside_zeroed = distances.copy()
distances_outside_zeroed[~mask] = 0

这样一来,你就可以方便地对符合条件的元素进行各种操作,而不用写任何显式的循环。

列表推导式

当然可以,这一类语法叫做 列表推导式(List Comprehension),它是 Python 中非常常用、简洁且高效的一种写法,用于从一个可迭代对象生成一个新列表。


基本语法结构:

[表达式 for 变量 in 可迭代对象 if 条件]

这等价于下面的传统写法:

result = []
for 变量 in 可迭代对象:
if 条件:
result.append(表达式)

举例说明你提到的代码:

[1 for k in scores if k >= 60]

解释:

  • scores 是一个成绩列表,比如 [85, 92, 78, 54, 96]
  • for k in scores:遍历这个列表中的每个成绩,赋值给变量 k
  • if k >= 60:只保留那些及格的成绩(大于等于 60)
  • 1:对于每一个满足条件的元素,输出一个值 1

最终结果:

这个表达式会生成一个新的列表,其中每一个及格的成绩对应一个 1。比如:

scores = [85, 92, 78, 54, 96]
[1 for k in scores if k >= 60] # 结果是 [1, 1, 1, 1]

这个列表长度就是及格的人数。我们再用 sum() 加起来,就得到了及格人数:

pass_count = sum([1 for k in scores if k >= 60])

更多例子:

  1. 生成平方数列表
squares = [x**2 for x in range(1, 6)]
# 输出: [1, 4, 9, 16, 25]
  1. 筛选偶数
evens = [x for x in range(10) if x % 2 == 0]
# 输出: [0, 2, 4, 6, 8]
  1. 将字符串转为小写
words = ['Hello', 'World']
lower_words = [w.lower() for w in words]
# 输出: ['hello', 'world']

你想我帮你出几个练习题来练习这种写法吗?

Lambda表达式教程

Lambda表达式是Python中一种创建小型匿名函数的方式。这些函数被称为"匿名"是因为它们没有正式的名称,通常用于那些只需使用一次的简短函数。

基本语法

lambda 参数列表: 表达式

Lambda表达式由三部分组成:

  • lambda关键字(固定的)
  • 参数列表(可以有0个或多个参数)
  • 冒号后的表达式(只能有一个表达式,不能包含多条语句)

与普通函数对比

让我们看一个简单的例子,对比普通函数和lambda表达式:

普通函数:

def add(x, y):
return x + y

result = add(5, 3) # 结果为8

等价的lambda表达式:

add = lambda x, y: x + y

result = add(5, 3) # 结果为8

Lambda表达式的实际应用

1. 简单计算

# 计算平方
square = lambda x: x * x
print(square(5)) # 输出: 25

# 判断是否是偶数
is_even = lambda x: x % 2 == 0
print(is_even(4)) # 输出: True

2. 与内置函数结合使用

与map()函数结合(批量处理):

# 将列表中的每个数字翻倍
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # 输出: [2, 4, 6, 8, 10]

与filter()函数结合(筛选):

# 筛选出列表中的偶数
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # 输出: [2, 4, 6]

与sorted()函数结合(自定义排序):

# 按姓名长度排序
names = ["Alice", "Bob", "Charlie", "David"]
sorted_names = sorted(names, key=lambda name: len(name))
print(sorted_names) # 输出: ['Bob', 'Alice', 'David', 'Charlie']

Lambda的限制

  1. 只能包含一个表达式,不能包含多条语句
  2. 不能包含赋值语句
  3. 表达式的结果自动成为返回值

何时使用Lambda

Lambda表达式最适合用于:

  • 需要简单函数的地方,尤其是只使用一次的函数
  • 作为高阶函数的参数(如map、filter、sorted等)
  • 需要函数作为参数的情况,但函数逻辑很简单

当函数逻辑复杂或需要重复使用时,应该使用普通的命名函数。

实用示例

让我们回到您最初的例子:

list(map(lambda word: translate_word(word, dictionary), words_to_translate))

这里,lambda表达式lambda word: translate_word(word, dictionary)为每个单词创建了一个翻译函数,然后map将这个函数应用到words_to_translate列表的每个单词上。

需要理解的关键点是什么呢?还有其他关于lambda表达式您想了解的内容吗?

字典的get方法

dictionary.get(word, "未知") 是Python字典(dict)对象的get()方法的用法,它有两个非常实用的特点:

基本解释

  1. 第一个参数 word:这是你想要在字典中查找的键(key)
  2. 第二个参数 "未知":这是默认值,当键不存在时返回这个值

工作原理

  • 如果word在字典中存在,返回对应的值
  • 如果word在字典中不存在,返回第二个参数"未知"

与直接索引的区别

比较这两种访问字典的方式:

# 方式1:直接索引 - 如果键不存在会引发KeyError
value = dictionary[word] # 键不存在时会报错

# 方式2:使用get方法 - 更安全
value = dictionary.get(word, "未知") # 键不存在返回"未知",不会报错

实际例子

# 例子:简单的英汉字典
english_chinese = {
"apple": "苹果",
"banana": "香蕉",
"orange": "橙子"
}

# 查找存在的单词
print(english_chinese.get("apple", "未知")) # 输出: 苹果

# 查找不存在的单词
print(english_chinese.get("grape", "未知")) # 输出: 未知

在翻译场景中,这种方法特别实用,因为它允许程序在遇到未知单词时继续运行,而不是因为找不到某个单词就崩溃。

字符串拼接

是的,在 Python(以及很多其他语言)里,+ 运算符对字符串做的是"拼接"操作,而不是数学意义上的加法。举几个例子说明:

>>> "Hello, " + "world!"
"Hello, world!"

>>> "2" + "2"
"22"

>>> "abc" + ""
"abc"

字符串拼接的原理与注意

  1. 不可变性(Immutable) 每次做拼接,都会产生一个新的字符串对象:

    s = "a"
    s = s + "b" # 旧的 "a" 不变,s 绑定到新创建的 "ab"

    因此,如果在一个很长的循环里重复用 + 拼接,性能会受影响(因为不断地分配新对象、复制已有内容)。

  2. 高效拼接

    • 对于少量拼接,用 + 最直观,性能影响几乎可以忽略。

    • 如果要拼接大量片段,推荐:

      • 列表收集 + ''.join()

        parts = []
        for piece in some_iterable:
        parts.append(piece)
        result = "".join(parts)
      • io.StringIOarray.array('u')(不常用)

  3. 与数字加法区分

    • "2" + "3" 得到 "23"
    • 2 + 3 得到 5 若想把字符串数字当数值相加,需要先 int()float() 转换:
    >>> int("2") + int("3")
    5

小结

  • + 用于字符串时,是拼接
  • 字符串是不可变类型,频繁用 + 拼会额外创建对象;
  • 大量拼接时,可以用 ''.join(...) 来优化性能。

Python列表合并详解

在代码中,合并列表的部分是这一行:

result = even_numbers + odd_numbers

这看似简单的操作实际上有很多知识点,我来详细解释:

1. 列表合并的基本语法

Python中合并列表有多种方式,最常见的是使用+运算符:

list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2 # 结果为 [1, 2, 3, 4, 5, 6]

2. 合并原理

当使用+运算符合并列表时:

  • Python会创建一个全新的列表对象
  • 第一个列表的所有元素按原顺序被复制到新列表
  • 第二个列表的所有元素接着被复制到新列表
  • 原始列表保持不变

3. 在题目中的应用

在我们的奇偶分离题目中:

even_numbers = [2, 4, 6, 8]
odd_numbers = [1, 3, 5, 7]
result = even_numbers + odd_numbers # 结果为 [2, 4, 6, 8, 1, 3, 5, 7]

这正好满足题目要求:偶数在前,奇数在后。合并顺序很重要,如果写成odd_numbers + even_numbers,结果就会是奇数在前了。

4. 其他合并列表的方法

4.1 使用extend()方法

result = even_numbers.copy()  # 创建副本避免修改原列表
result.extend(odd_numbers) # 在result末尾添加odd_numbers的所有元素

4.2 使用解包运算符(*)

result = [*even_numbers, *odd_numbers]  # Python 3.5+

4.3 使用列表推导式

result = [x for x in even_numbers] + [x for x in odd_numbers]

4.4 使用itertools.chain

from itertools import chain
result = list(chain(even_numbers, odd_numbers))

5. 性能和内存考虑

  • +运算符:创建新列表,时间复杂度O(n),需要额外内存

  • extend()方法:直接修改现有列表,可能更节省内存

    # 如果不再需要保留原始even_numbers列表
    even_numbers.extend(odd_numbers)
    result = even_numbers
  • 大型列表:对于非常大的列表,合并操作可能消耗大量内存和时间

6. 实际应用示例

假设我们的输入是1,2,3,4,5,6,7,8

# 处理后
even_numbers = [2, 4, 6, 8]
odd_numbers = [1, 3, 5, 7]

# 合并
result = even_numbers + odd_numbers # [2, 4, 6, 8, 1, 3, 5, 7]

# 输出
print(','.join(map(str, result))) # "2,4,6,8,1,3,5,7"

对于这道题,使用+运算符是最直观、最简洁的解决方案,因为列表较小,性能和内存消耗不是问题。在实际项目中,可以根据具体需求选择最合适的合并方法。

处理EOF

我来详细解释Python中处理EOF(End of File)的几种方法:

现在我来详细解释Python中处理EOF(End of File)的几种方法:

  1. try-except方法(方法1)
try:
while True:
line = input()
# 处理输入
except EOFError:
# 到达文件末尾
  • 最简单直接的方法
  • 当没有更多输入时,input()会抛出EOFError
  • 适合交互式输入和文件重定向

info = input().split()

让我详细解释 info = input().split() 中的 info

  1. input() 读取一行输入的字符串
  2. .split() 将这行字符串按空白字符(空格、制表符)分割成列表

举个具体例子:

# 假设输入: zhangsan 80 75 65

info = input().split()

# info 现在是一个列表:
# info = ['zhangsan', '80', '75', '65']

拆解说明:

  • info[0] 是姓名 'zhangsan'
  • info[1] 是第一门课成绩 '80'
  • info[2] 是第二门课成绩 '75'
  • info[3] 是第三门课成绩 '65'

处理时的常见操作:

# 提取姓名
name = info[0]

# 转换成绩为整数
scores = list(map(int, info[1:]))
# 等价于:
# scores = [int(info[1]), int(info[2]), int(info[3])]

我来做一个详细的代码示例:

关键点总结:

  1. split() 默认按空白字符分割输入
  2. 返回一个列表,包含分割后的字符串
  3. 可以用 map(int, info[1:]) 快速转换成绩为整数
  4. 通过索引可以方便地提取姓名和成绩

常见的坑和注意事项:

  • 确保输入格式正确
  • 使用 try-except 处理可能的输入错误
  • 注意 split() 会去除多余的空白字符

使用 split() 是处理固定格式输入的常用方法,特别适合在编程竞赛和在线评测系统中快速解析输入。

字符串的方法

在 Python 中,字符串(str)是非常常用的数据类型,它有很多内置方法,帮助你快捷地做查找、替换、分割、格式化等操作。下面按功能分组,给你介绍一些最常用的方法,并配上简短示例。


1. 大小写转换

  • .lower() 将所有字符都转换成小写。

    "Hello World".lower()   # "hello world"
  • .upper() 将所有字符都转换成大写。

    "Hello World".upper()   # "HELLO WORLD"
  • .title() 把每个词的首字母改为大写。

    "hello world".title()   # "Hello World"
  • .capitalize() 把第一个字符大写,其余小写。

    "hELLo".capitalize()    # "Hello"

2. 查找与判断

  • .find(sub) 在字符串中查找子串 sub,返回第一次出现的索引;找不到返回 -1

    "banana".find("na")     # 2
    "banana".find("xy") # -1
  • .index(sub) 类似 .find(),但找不到时会抛出 ValueError

    "banana".index("na")    # 2
    # "banana".index("xy") # ValueError
  • .startswith(prefix) / .endswith(suffix) 判断是否以某前缀/后缀开头或结束,返回布尔值。

    "test.py".startswith("te")   # True
    "test.py".endswith(".py") # True

3. 替换与删除

  • .replace(old, new) 把所有的 old 子串替换为 new(不修改原串,返回新串)。

    "apple".replace("p", "b")   # "abble"
  • 删除字符/子串 直接把 new 设为空字符串:

    s = "python"
    s.replace("t", "") # "pyhon"

    没错,str.replace 的第三个参数就是用来限制替换次数的。你可以这样写:

    s = "python"
    result = s.replace("t", "", 1)
    print(result) # 输出 "pyhon"

    这里的 1 表示最多只替换一次,也就是把第一个遇到的 "t" 删掉。


    更多示例

    s = "tattoo"
    print(s.replace("t", "", 1)) # "atoo",只删第一个 t
    print(s.replace("t", "", 2)) # "ato",删前两个 t

    # 如果你想删最后一个 t,可以把字符串反过来,删完再反转回来:
    s = "tattoo"
    tmp = s[::-1].replace("t", "", 1)
    result = tmp[::-1]
    print(result) # "tatto"

    这样,你就可以灵活地控制要删掉多少个目标字符了。


4. 去除空白

  • .strip() 去掉开头和结尾的空白(包括空格、换行、制表符)。

    "  hello  \n".strip()     # "hello"
  • .lstrip() / .rstrip() 仅去除左侧或右侧的空白。

    "  hello".lstrip()        # "hello"
    "hello ".rstrip() # "hello"

5. 分割与连接

  • .split(sep=None) 按照分隔符 sep 把字符串分割成列表;默认以任意空白分割。

    "a b  c".split()          # ["a","b","c"]
    "1,2,3".split(",") # ["1","2","3"]
  • .join(iterable) 用当前字符串作为分隔符,把可迭代对象拼接成新的字符串。

    "-".join(["2025","04","30"])  # "2025-04-30"

6. 检测字符串类型

  • .isalpha() 全部字母且至少有一个字符。

  • .isdigit() 全部数字且至少有一个字符。

  • .isalnum() 全部字母或数字且至少有一个字符。

  • .isspace() 全部空白字符且至少有一个字符。

    "abc".isalpha()    # True
    "123".isdigit() # True
    "ab12".isalnum() # True
    " \n\t".isspace() # True

7. 格式化与填充

  • .format() 旧式的格式化,配合 {} 占位:

    "Hello, {}".format("Alice")   # "Hello, Alice"
  • f-string(Python 3.6+) 最简洁的内嵌表达式格式化:

    name = "Bob"
    f"Hi, {name}!" # "Hi, Bob!"
  • .zfill(width) 在左侧填充 0,直到字符串总长度为 width

    "42".zfill(5)    # "00042"

小结

Python 的字符串方法非常丰富,这里只是列出了几乎日常都会用到的常见操作。多练习、多读官方文档,你会发现还有更多好用的小工具!如果有具体场景或用法想深入了解,随时告诉我~

检查字符串是否合法

你可以用 Python 的字符串方法和任何内置函数来逐一检测密码是否满足各项条件。下面是一种简单易懂的实现:

pwd = input().strip()

# 条件检测
length_ok = len(pwd) >= 8
has_upper = any(c.isupper() for c in pwd)
has_lower = any(c.islower() for c in pwd)
has_digit = any(c.isdigit() for c in pwd)

# 综合判断
if length_ok and has_upper and has_lower and has_digit:
print("密码合法")
else:
print("密码不合法")

代码说明

  1. len(pwd) >= 8 检查密码长度是否至少为 8。
  2. any(c.isupper() for c in pwd) 遍历密码中的每个字符 c,只要有一个满足 c.isupper()(大写字母)即为 True
  3. any(c.islower() for c in pwd) 类似地检测是否存在小写字母。
  4. any(c.isdigit() for c in pwd) 检测是否存在数字字符。
  5. 最后,用 and 把四个布尔条件连起来,只有全部为 True 时才输出"密码合法",否则输出"密码不合法"。

运行示例

  • 输入Abc12345 输出密码合法
  • 输入password 输出密码不合法

这样就能对密码进行基本的强度验证啦!如果还想加入更多规则(比如特殊字符、不能含有空格等),可以在同样的框架下加上相应的检测条件。

Pandas 基础教程

1. Pandas 简介

Pandas 是一个强大的 Python 数据分析工具包,建立在 NumPy 的基础上。它提供了高性能、易用的数据结构和数据分析工具,广泛应用于数据科学、机器学习和数据分析领域。

主要特点:

  • 高效处理大型数据集
  • 灵活的数据结构
  • 强大的数据清洗和转换功能
  • 与其他 Python 库良好集成

2. 安装

使用 pip 安装 pandas:

pip install pandas

基本导入方式:

import pandas as pd
import numpy as np

3. 核心数据结构

3.1 Series

Series 是一种类似于一维数组的对象,由数据和索引组成。

# 创建 Series
s = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s)

输出:

0    1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64

3.2 DataFrame

DataFrame 是一个二维的、表格型的数据结构,有行索引和列索引。

# 创建 DataFrame
data = {'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'城市': ['北京', '上海', '广州']}

df = pd.DataFrame(data)
print(df)

输出:

   姓名  年龄  城市
0 张三 25 北京
1 李四 30 上海
2 王五 35 广州

4. 数据导入与导出

4.1 读取数据

# 读取 CSV 文件
df = pd.read_csv('文件路径.csv')

# 读取 Excel 文件
df = pd.read_excel('文件路径.xlsx', sheet_name='Sheet1')

# 读取 JSON 文件
df = pd.read_json('文件路径.json')

# 从数据库读取
import sqlite3
conn = sqlite3.connect('database.db')
df = pd.read_sql('SELECT * FROM table_name', conn)

4.2 保存数据

# 保存为 CSV
df.to_csv('输出文件.csv', index=False)

# 保存为 Excel
df.to_excel('输出文件.xlsx', sheet_name='Sheet1')

# 保存为 JSON
df.to_json('输出文件.json')

5. 数据检查与选择

5.1 基本信息

# 查看前几行数据
df.head()

# 查看后几行数据
df.tail()

# 查看数据框信息
df.info()

# 查看统计摘要
df.describe()

# 查看形状(行数和列数)
df.shape

5.2 数据选择

# 选择列
df['列名']
df[['列名1', '列名2']]

# 通过位置选择
df.iloc[0] # 第一行
df.iloc[0:3] # 前三行
df.iloc[0, 2] # 第一行,第三列
df.iloc[0:3, 1:3] # 前三行,第二至第三列

# 通过标签选择
df.loc[0] # 索引为0的行
df.loc[0:2, ['姓名', '年龄']] # 索引0至2的行,姓名和年龄列

# 条件选择
df[df['年龄'] > 30] # 年龄大于30的行
df[(df['年龄'] > 25) & (df['城市'] == '上海')] # 复合条件

6. 数据清洗

6.1 处理缺失值

# 检查缺失值
df.isnull().sum()

# 删除缺失值
df.dropna() # 删除包含任何NaN的行
df.dropna(axis=1) # 删除包含任何NaN的列

# 填充缺失值
df.fillna(0) # 用0填充
df['列名'].fillna(method='ffill') # 用前一个值填充
df['列名'].fillna(df['列名'].mean()) # 用平均值填充

import pandas as pd

data = {
'姓名': ['张三', None, '王五', '赵六', None],
'成绩': [85, 75, 82, None, 72],
'年龄': [18, 20, 19, None, 21]
}

df = pd.DataFrame(data)

# 1)先统计原始缺失值
print("原始缺失值统计:")
print(df.isnull().sum())

# 2)拷贝一份 df,避免改动原来的
ds = df.copy()

# 3)对每列填充,并赋值回去
ds['姓名'] = ds['姓名'].fillna('未知')
ds['成绩'] = ds['成绩'].fillna(ds['成绩'].mean())
ds['年龄'] = ds['年龄'].fillna(ds['年龄'].median())

# 4)如果想要整数,可以再转一次类型
ds['成绩'] = ds['成绩'].astype(int)
ds['年龄'] = ds['年龄'].astype(int)

# 5)输出结果
print("\n处理后的 DataFrame:")
print(ds)

6.2 处理重复值

# 检查重复
df.duplicated().sum()

# 删除重复行
df.drop_duplicates()

# 基于特定列删除重复
df.drop_duplicates(subset=['列名'])

7. 数据操作与转换

7.1 列操作

# 添加新列
df['新列'] = df['列1'] + df['列2']

# 删除列
df.drop('列名', axis=1, inplace=True)

# 重命名列
df.rename(columns={'旧名': '新名'}, inplace=True)

7.2 排序

# 按值排序
df.sort_values('列名')
df.sort_values('列名', ascending=False) # 降序

# 按索引排序
df.sort_index()

7.3 分组与聚合

# 分组
grouped = df.groupby('城市')

# 聚合
grouped.mean() # 平均值
grouped.agg({'年龄': 'mean', '收入': 'sum'}) # 多种聚合

7.4 合并数据

# 合并两个 DataFrame
pd.concat([df1, df2]) # 垂直合并
pd.concat([df1, df2], axis=1) # 水平合并

# SQL式合并
pd.merge(df1, df2, on='共同列') # 内连接
pd.merge(df1, df2, on='共同列', how='left') # 左连接

8. 基础统计与分析

# 描述性统计
df.describe()

# 相关性分析
df.corr()

# 唯一值计数
df['列名'].value_counts()

# 数据透视表
pd.pivot_table(df, values='数值列', index='行索引列', columns='列索引列', aggfunc='mean')

9. 数据可视化

Pandas 与 Matplotlib 集成,提供了简单的绘图功能:

# 折线图
df.plot()

# 柱状图
df.plot.bar()

# 散点图
df.plot.scatter(x='列1', y='列2')

# 饼图
df['列名'].value_counts().plot.pie()

# 直方图
df['数值列'].plot.hist()

10. 时间序列

# 创建日期范围
dates = pd.date_range('20230101', periods=6)

# 时间重采样
ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))
ts.resample('M').mean() # 按月重采样

11. 实用技巧

11.1 应用函数

# 对列应用函数
df['列名'].apply(lambda x: x*2)

# 对整个 DataFrame 应用函数
df.apply(np.sum)

11.2 字符串操作

# 字符串方法
df['列名'].str.upper()
df['列名'].str.contains('字符串')

12. 进阶学习资源

练习建议

熟练掌握 Pandas 的最佳方式是实践。建议:

  1. 下载公开数据集进行练习
  2. 尝试解决实际数据清洗和分析问题
  3. 参与 Kaggle 竞赛来提升技能

希望这份教程能帮助您开始 Pandas 的学习之旅!

DataFrame实际应用

下面我按步骤详细解释一下示例代码中用到的 pandas 语法和常用操作,让你快速上手:


1. 导入 pandas

import pandas as pd
  • 这是标准写法,把 pandas 库引入并简写为 pd,方便后续调用。

2. 创建 DataFrame

data = {
'商品名称': ['手机', '电视', '电脑', '耳机', '平板'],
'商品编号': ['P001', 'T002', 'C003', 'E004', 'T005'],
'单价': [5000, 3000, 4500, 500, 2000],
'销量': [10, 10, 6, 30, 8]
}

df = pd.DataFrame(data)
  • 字典格式:键(key)是列名,值(value)是一个列表,对应这一列的所有数据。
  • pd.DataFrame(...):将字典转换成二维表格结构——DataFrame。
  • 此时 df 就像一个电子表格,有行号和列标签:
商品名称商品编号单价销量
0手机P001500010
1电视T002300010

3. 列运算:新增"销售总额"列

df['销售总额'] = (df['单价'] * df['销量']).round(2)
  • df['单价']df['销量']:分别取出 DataFrame 中名为"单价"和"销量"的列,得到两个 Series(可看作一维数组)。
  • 点乘运算df['单价'] * df['销量'] 会在"同一行"对应位置上做乘法,结果也是一个 Series
  • .round(2):对数值进行四舍五入,保留 2 位小数。
  • 赋值给 df['销售总额']:把计算结果作为新的一列加入到原 df 中。

操作后,df 多了一列:

商品名称商品编号单价销量销售总额
0手机P00150001050000.00
1电视T00230001030000.00

4. 排序与选取前 N 条

top3 = df.sort_values(by='销售总额', ascending=False).head(3)
  • df.sort_values(by='销售总额', ascending=False)
    • 按"销售总额"这一列排序。
    • ascending=False 表示降序(从大到小);若要升序(从小到大),用 ascending=True 或省略。
    • 返回一个新的排好序的 DataFrame,不改变原 df(除非加上 inplace=True)。
  • .head(3)
    • 取排好序后 DataFrame 的前 3 行。
    • 相当于你想要"排名前三"的记录。

top3 也是一个 DataFrame,只含排名最高的三条数据。


5. 遍历行并格式化输出

for _, row in top3.iterrows():
print(f"{row['商品名称']} {row['商品编号']} {row['销售总额']:.2f}")
  • top3.iterrows()
    • 逐行遍历 DataFrame。每次返回一个 (index, row) 对,其中 row 是一个 Series,代表这一行的数据。
    • 这里用 _ 忽略行索引,只取 row
  • row['列名']
    • 取出当前行中指定列的值。
  • f-string
    • Python 格式化字符串:在字符串前加 f,大括号内写变量。
    • {...:.2f} 表示把浮点数格式化为保留两位小数的形式。

小结

  • DataFrame:pandas 的核心数据结构,类似电子表格,行列索引都可自定义。
  • 取列df['列名'] → 返回一个 Series。
  • 列运算:支持向量化操作,如加、减、乘、除,会逐元素作用。
  • 新增/修改列:直接赋值给 df['新列名']
  • 排序sort_values(by=..., ascending=...)
  • 取前几行.head(n);取后几行:.tail(n)
  • 遍历行.iterrows(),适合少量数据格式化输出;若要高效批量操作,推荐列运算或 apply

掌握以上基本操作,就能应付大多数日常的数据清洗、计算、筛选任务了!如果后续有更深入的问题(比如分组聚合 groupby、缺失值处理、时间序列等),随时可以再交流。

是的,.round(2) 是 pandas 提供给 SeriesDataFrame 对象的方法,用来对其中的数值进行四舍五入。

  • 当你对一个 Series(一列)调用,像这样:

    df['销售总额'] = (df['单价'] * df['销量']).round(2)

    就相当于把这一列里所有的数值都按保留两位小数来四舍五入。

  • 如果对整个 DataFrame 调用:

    df_rounded = df.round(2)

    则会对所有数值型的列都执行同样的四舍五入操作。

这个方法内部是基于 NumPy 的数值计算实现的,但它是 pandas 对象上的"向量化"接口,性能和易用性都优于逐个元素调用 Python 内建的 round()

pandas缺失值处理和astype类型转换

下面我结合具体示例,分步讲解在 pandas 中如何检测处理缺失值,以及如何使用 astype类型转换


一、准备示例 DataFrame

import pandas as pd
import numpy as np

df = pd.DataFrame({
'姓名': ['张三', None, '王五', '赵六', None],
'成绩': [85, 75, 82, None, 72],
'年龄': [18, 20, 19, None, 21]
})
print(df)

输出:

姓名成绩年龄
0张三8518
1NaN7520
2王五8219
3赵六NaNNaN
4NaN7221

二、检测缺失值

  1. 各列缺失值计数

    df.isnull().sum()

    结果:

    姓名    2
    成绩 1
    年龄 1
    dtype: int64
  2. 判断是否存在任何缺失

    df.isnull().values.any()   # 返回 True/False
  3. 查看每个元素是否缺失

    df.isnull()

三、删除缺失值

注意:默认这些方法不会改变原 df,若想直接在 df 上修改,要么加 inplace=True,要么把返回值再赋值回 df

  1. 删除含有任意 NaN 的行

    df2 = df.dropna()
    # 或者 df.dropna(inplace=True)
  2. 删除含有任意 NaN 的列

    df3 = df.dropna(axis=1)
  3. 只删除全是 NaN 的行/列

    df4 = df.dropna(how='all')         # 行全 NaN 则删除
    df5 = df.dropna(axis=1, how='all') # 列全 NaN 则删除
  4. 保留至少有 N 个非 NaN 的行/列

    df6 = df.dropna(thresh=2)          # 至少两项非 NaN 的行才能保留

四、填充缺失值

1. 全表统一填充值

df_fill0 = df.fillna(0)     # 用 0 填充所有 NaN

2. 不同列用不同策略

df_filled = df.fillna({
'姓名': '未知',
'成绩': df['成绩'].mean(), # 平均值
'年龄': df['年龄'].median() # 中位数
})

3. 单列填充并赋值

# 注意:fillna 不 inplace,需赋回
df['姓名'] = df['姓名'].fillna('未知')
df['成绩'] = df['成绩'].fillna(df['成绩'].mean())
df['年龄'] = df['年龄'].fillna(df['年龄'].median())

4. 前向/后向填充

df_ffill = df.fillna(method='ffill')  # 用前一个有效值填充
df_bfill = df.fillna(method='bfill') # 用后一个有效值填充

五、类型转换:astype

  • 用途:将 Series 或 DataFrame 的数据类型转换成指定类型。
  • 返回:默认返回新的对象,不修改原始,除非你把结果赋回或用 inplace(DataFrame 上的 astype 没有 inplace 参数)。

常见用法

  1. 将浮点转整数

    df['成绩'] = df['成绩'].astype(int)

    小数部分会被截断,不会四舍五入。

  2. 将字符串转数值

    df['年龄'] = df['年龄'].astype('int64')
  3. 整体转换

    # 所有列都变成字符串
    df_str = df.astype(str)
  4. 为不同列指定不同类型

    df_new = df.astype({
    '成绩': 'int64',
    '年龄': 'int64'
    })

六、完整示例:先填充再转类型

import pandas as pd

# 1. 原始 DataFrame
df = pd.DataFrame({
'姓名': ['张三', None, '王五', '赵六', None],
'成绩': [85, 75, 82, None, 72],
'年龄': [18, 20, 19, None, 21]
})

# 2. 检查缺失
print("缺失值统计:\n", df.isnull().sum(), "\n")

# 3. 填充
df['姓名'] = df['姓名'].fillna('未知')
df['成绩'] = df['成绩'].fillna(df['成绩'].mean())
df['年龄'] = df['年龄'].fillna(df['年龄'].median())

# 4. 转整型
df['成绩'] = df['成绩'].astype(int)
df['年龄'] = df['年龄'].astype(int)

# 5. 查看结果
print("处理后的 DataFrame:")
print(df)

输出:

缺失值统计:
姓名 2
成绩 1
年龄 1
dtype: int64

处理后的 DataFrame:
姓名 成绩 年龄
0 张三 85 18
1 未知 75 20
2 王五 82 19
3 赵六 78 20
4 未知 72 21

以上就是 pandas 中缺失值检测删除/填充,以及**astype 类型转换**的常用方法和示例。你可以根据具体需求灵活组合和调整!

pandas 的 DataFrame副本问题

是的,

ds = df

会创建新的副本,它只是把 dsdf 指向了同一个 DataFrame 对象。此后,对 dsdf 的任何原地(in-place)修改都会互相影响。

举例说明:

import pandas as pd

df = pd.DataFrame({'A': [1, 2, None]})
ds = df # 这里 ds 和 df 引用的是同一个对象

ds['A'] = ds['A'].fillna(0) # 用 0 填充缺失值
print(df)

输出:

     A
0 1.0
1 2.0
2 0.0

你会发现,虽然只在 ds 上调用了 fillna,但 df 也被改了。


如果你想要一个独立的副本,以便后续修改时互不干扰,应使用:

ds = df.copy()

这样 ds 就是 df 当时内容的一个拷贝,之后对 ds 的改动不会影响到原来的 df,反之亦然。

DataFrame四舍五入

是的,直接用 astype(int)截断(向零取整)小数部分,而 不会 做四舍五入。举个例子:

import pandas as pd

s = pd.Series([3.9, 2.1, -1.7, -2.9])
print(s.astype(int))
# 输出:
# 0 3
# 1 2
# 2 -1
# 3 -2
# dtype: int64

如果你想先四舍五入再转成整数,可以这样做:

s_rounded = s.round(0).astype(int)
print(s_rounded)
# 输出:
# 0 4
# 1 2
# 2 -2
# 3 -3
# dtype: int64

或者直接在 DataFrame 列上链式调用:

df['成绩'] = df['成绩'].round(0).astype(int)

这样就能保证你得到的是四舍五入后的整数,而不是简单的截断。

进制转换和字符串计数

下面分别对两者做更深入的讲解和示例。


一、str.count(sub[, start[, end]])

1. 功能概述

  • 作用:统计子串 sub 在字符串中出现的次数。
  • 返回值:一个整数,表示非重叠出现的总次数。

2. 方法签名

count(sub[, start[, end]])int
  • sub:要查找的子串,可以是单个字符,也可以是多个字符。
  • start(可选):在原字符串中开始查找的索引(含该位置)。
  • end(可选):在原字符串中结束查找的索引(不含该位置)。

3. 计数原则

  • 非重叠:每次匹配后,搜索位置会移动到匹配子串的末尾,不会对同一段字符重复计数。
  • 默认范围:如果不传 startend,则在整个字符串范围内查找。

4. 典型示例

s = "banana"

# 基本用法
print(s.count("a")) # 输出 3,因为 'a' 出现了 3 次
print(s.count("ana")) # 输出 1,因为子串 "ana" 只在 "b[ana]na" 中出现一次

# 带 start/end 参数
print(s.count("a", 2)) # 从索引 2 开始算,"banana"[2:] = "nana",此处 'a' 出现 2 次
print(s.count("na", 2, 5)) # 在 s[2:5] = "nan" 中查找 "na",仅出现一次

# 注意非重叠原则
s2 = "aaaa"
print(s2.count("aa")) # 输出 2,而不是 3
# 解释:匹配过程如下
# 第一次匹配到 [aa]aa → count=1,下一次从第二个 'a' 后面开始 → aa[aa] → count=2

5. 复杂度

  • 时间复杂度 O(n×m),其中 n 是被查找字符串长度,m 是 sub 的长度;典型实现会用滑动窗口或其他算法。

二、hex_str = format(n, 'x')

1. format() 函数概述

  • 作用:按照给定的格式规格,将对象转换为字符串。
  • 等价:对于整数 nformat(n, spec) 等同于调用 n.__format__(spec)

2. 格式说明符(Format Specification Mini-Language)

一个完整的格式说明符一般形如:

[[fill]align][sign][#][0][width][,][.precision][type]

但这里我们只关注 type 部分。

type含义举例
d十进制整数(默认)format(255,'d')'255'
x16 进制,小写 a–fformat(255,'x')'ff'
X16 进制,大写 A–Fformat(255,'X')'FF'
b二进制format(5,'b')'101'
o八进制format(8,'o')'10'
#x带前缀 0x 的 16 进制,小写format(255,'#x')'0xff'
04x宽度 4,不足左侧补零,再转 16 进制小写format(10,'04x')'000a'

3. 具体到 'x'

  • 'x':将整数转换为小写的 16 进制表示,不带任何前缀。

  • 常见用法:

    n = 46883310
    hex_str = format(n, 'x') # => "2ca2b96"

4. 同等写法

  • hex(n)[2:]:内置 hex() 函数返回的字符串带前缀 "0x",用切片去掉它。

    hex(255)     # => '0xff'
    hex(255)[2:] # => 'ff'
  • format() 更灵活,可直接利用宽度、对齐、前缀等多种选项。

5. 复合示例

n = 305441741

print(format(n, 'x')) # '1234abcd'
print(format(n, 'X')) # '1234ABCD'
print(format(n, '#x')) # '0x1234abcd'
print(format(n, '010x')) # '001234abcd'(宽度 10,不够左侧补 '0')

通过以上讲解,你可以了解:

  1. str.count(sub[, start[, end]]):统计非重叠子串出现次数,可指定查找区间。
  2. format(n, 'x'):调用整数的 __format__,生成小写 16 进制字符串,且支持丰富的格式化选项(前缀、宽度、填充等)。

如果对格式化迷你语言或字符串方法有更深入的问题,欢迎继续探讨!

Pandas中的map函数用法

在Pandas中,Series.map()方法的用法确实与Python内置的map()函数有些不同。让我解释一下在Pandas中Series.map()方法的工作原理,特别是当传入字典作为参数时的情况。

在Python中:

  • 内置的map(function, iterable)函数将一个函数应用于可迭代对象的每个元素,并返回结果的迭代器

在Pandas中:

  • Series.map(arg)方法更加灵活,它可以接受不同类型的参数:
    1. 函数:类似Python内置map,将函数应用于Series的每个元素
    2. 字典:用于值的替换映射
    3. Series:用于值的替换映射

当我们传入字典时,Series.map()会查找字典中与Series元素匹配的键,并用相应的值替换。这是一种非常方便的方式来进行值的映射/编码转换。

例如,在代码中:

df_encoded['敲声编码'] = df['敲声'].map(敲声_mapping)

这里我们有一个字典敲声_mapping = {'青绿': 0, '乌黑': 1}。当df['敲声']中有'青绿'值时,map操作会将其替换为0,当有'乌黑'值时,替换为1。

Pandas中Series.map()方法的详细解释

您提出了一个很好的问题。在Pandas中,Series.map()方法的用法确实与Python内置的map()函数有所不同。让我详细解释一下:

Python内置map()函数 vs Pandas Series.map()方法

Python内置map()函数

Python内置的map(function, iterable)函数接受两个参数:

  • 一个函数
  • 一个可迭代对象 它将函数应用于可迭代对象的每个元素,并返回一个迭代器。
# 示例:将lambda函数应用到列表的每个元素
result = map(lambda x: x*2, [1, 2, 3, 4])
list(result) # [2, 4, 6, 8]

Pandas Series.map()方法

Pandas的Series.map()方法更灵活,它可以接受不同类型的参数:

  1. 函数:类似Python内置map,将函数应用于Series的每个元素

    s = pd.Series([1, 2, 3])
    s.map(lambda x: x*2) # 返回 [2, 4, 6]
  2. 字典:用于值的替换/映射(这就是您在问题中看到的用法)

    s = pd.Series(['a', 'b', 'c'])
    s.map({'a': 1, 'b': 2, 'c': 3}) # 返回 [1, 2, 3]
  3. Series:同样用于值的替换/映射

    s = pd.Series(['a', 'b', 'c'])
    mapping = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
    s.map(mapping) # 返回 [1, 2, 3]

在您的代码中的应用

在您的代码中:

df_encoded['敲声编码'] = df['敲声'].map(敲声_mapping)

这里的敲声_mapping是一个字典{'青绿': 0, '乌黑': 1}

当执行此操作时,Pandas会:

  1. 查看df['敲声']中的每个值
  2. 敲声_mapping字典中查找对应的键
  3. 将找到的值替换到新的Series中

举例说明:

  • 如果df['敲声']中有值为'青绿',它会被替换为0
  • 如果有值为'乌黑',它会被替换为1

使用map()的优势

这种方法在数据预处理中特别有用,尤其是:

  • 进行特征编码(如您的例子)
  • 数据清洗
  • 值的替换和转换

使用字典进行映射比编写复杂的条件逻辑更简洁、更高效,尤其是当需要处理多个不同的值时。

希望这个解释能帮助您理解Pandas中map()方法与字典的用法!

在 Pandas 里,map 只有在 Series 上可用,DataFrame 本身并没有 .map() 这个方法,所以你这行

ds = df.map(knock_mapping)

会报:

AttributeError: 'DataFrame' object has no attribute 'map'

要把整个表里的值都按照你的 knock_mapping 转成数字,有几种常见做法:

  1. replace

    ds = df.replace(knock_mapping)

    replace 会自动对所有单元格做「值→映射」替换,最简洁。

  2. applymap

    ds = df.applymap(lambda x: knock_mapping[x])

    applymap 会对每个元素调用一次给定的函数,也能达到同样效果。

  3. 按列 map 如果你想保留对每列单独处理的灵活性,也可以对每一列做 .map()

    ds = df.copy()
    for col in ds.columns:
    ds[col] = ds[col].map(knock_mapping)

例如,用 replace 的完整示例:

import pandas as pd

data = {
'敲声': ['青绿', '乌黑', '青绿', '乌黑'],
'色泽': ['冲响', '冲响', '清脆', '沉闷'],
'根蒂': ['蜷缩', '蜷缩', '硬挺', '稍蜷']
}
df = pd.DataFrame(data)

knock_mapping = {
'青绿': 0, '乌黑': 1,
'冲响': 0, '清脆': 1, '沉闷': 2,
'蜷缩': 0, '稍蜷': 1, '硬挺': 2
}

# 方法一:replace
ds = df.replace(knock_mapping)
print(ds)

输出就是你想要的数值表:

   敲声  色泽  根蒂
0 0 0 0
1 1 0 0
2 0 1 2
3 1 2 1

——这样写就没有问题了。

DataFrame的applymap用法

pandas 中的 applymap() 方法详解

applymap() 是 pandas DataFrame 的一个方法,它可以将一个函数应用到 DataFrame 中的每个元素上。这与 map() 方法(应用于 Series)和 apply() 方法(应用于行或列)不同。

基本语法

DataFrame.applymap(func)

其中 func 是一个接受单个值并返回单个值的函数。

工作原理

applymap() 会遍历 DataFrame 中的每个单元格,将单元格的值传递给指定的函数,然后用函数的返回值替换原始值。这对于对整个 DataFrame 进行元素级转换非常有用。

基本示例

import pandas as pd
import numpy as np

# 创建一个示例 DataFrame
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
})

# 使用 applymap 对每个元素应用函数
result = df.applymap(lambda x: x**2)
print(result)

输出:

    A   B   C
0 1 16 49
1 4 25 64
2 9 36 81

应用到您之前的例子

对于您之前的映射示例,可以使用 applymap 如下:

import pandas as pd 

data = {
'敲声': ['青绿', '乌黑', '青绿', '乌黑'],
'色泽': ['冲响', '冲响', '清脆', '沉闷'],
'根蒂': ['蜷缩', '蜷缩', '硬挺', '稍蜷']
}

df = pd.DataFrame(data)

knock_mapping = {'青绿': 0, '乌黑': 1, '冲响': 0, '清脆': 1, '沉闷': 2, '蜷缩': 0, '稍蜷': 1, '硬挺': 2}

# 使用 applymap 进行转换
def map_value(x):
return knock_mapping.get(x, x) # 如果映射中没有该值,则保持原值

df_mapped = df.applymap(map_value)
print(df_mapped)

注意事项

  1. 性能考虑:对于大型 DataFrame,applymap() 可能比向量化操作慢,因为它是循环遍历每个元素。

  2. 替代方案:在较新版本的 pandas 中,推荐使用更高效的方法:

    • 对于简单映射:replace() 方法(如前面示例所示)
    • 对于复杂函数:DataFrame.transform() 或 numpy 的向量化操作
  3. 弃用警告:需要注意的是,在 pandas 的未来版本中,applymap() 可能会被弃用,建议逐渐转向其他方法。

何时使用 applymap()

  • 当您需要对 DataFrame 中的每个元素都应用同一个函数时
  • 当函数逻辑相对复杂,无法用简单的向量化操作表达时
  • 当 DataFrame 规模较小,性能不是主要考虑因素时

与其他方法比较

方法作用对象适用场景
map()Series(单列)对单个列的每个元素应用映射
apply()DataFrame 的行或列对整行或整列进行操作
applymap()DataFrame 的每个元素对每个单元格应用相同的函数
replace()Series 或 DataFrame简单的值替换(推荐用于您的映射场景)

希望这个解释有所帮助!如果您有更多问题,请随时提问。

DataFrame.columns列标签

在 Pandas 里,ds.columns 是访问 DataFrame(或类似对象,如你用 replace/applymap 后得到的 ds)列标签(column names)的一种最常用方式。下面拆开来讲:


1. columns 的类型

  • 对象类型:pandas.Index
  • 你可以把它看作一个不可变的、标签化的一维数组
>>> import pandas as pd
>>> df = pd.DataFrame({
... '敲声': [0,1,0,1],
... '色泽': [0,0,1,2],
... '根蒂': [0,0,2,1]
... })
>>> type(df.columns)
<class 'pandas.core.indexes.base.Index'>

2. 常见用途

  1. 查看列名

    >>> df.columns
    Index(['敲声', '色泽', '根蒂'], dtype='object')
  2. 转换为 Python 列表

    cols_list = list(df.columns)
    # ['敲声', '色泽', '根蒂']
  3. 遍历列名

    for col in df.columns:
    print(col)
  4. 重命名列

    • 直接赋值新列表(长度要一致):

      df.columns = ['knock', 'color', 'root']
    • 使用 rename 方法(更灵活,可局部改名):

      df = df.rename(columns={'敲声': 'knock', '色泽': 'color'})
  5. 索引/选择子集

    # 只要 '敲声' 和 '根蒂' 两列
    df[['敲声', '根蒂']]

3. 背后原理

  • df.columns 本质上是 df.axes[1],其中 axes[0] 是行索引(index),axes[1] 是列索引(columns)。
  • 因为它返回的是 Index 对象,所以下列操作都能用:
    • df.columns[0] → 第一个列名
    • '色泽' in df.columns → 判断某个列名是否存在

小结

  • ds.columns:获取列标签
  • 类型pandas.Index,提供了过载好的索引工具
  • 常用:查看、遍历、重命名、子集选择

了解并善用 columns,能让你在处理表格数据时更灵活地管理各个字段名称。

math库

Python 的 math 模块提供了大量的数学函数和常量,用于浮点运算、三角函数、指数/对数、特殊函数等场景。下面从基础使用、常量、分类函数、注意事项,以及一些示例,来全面介绍这个模块。


1. 导入方式

import math

或者只导入需要的函数/常量:

from math import sqrt, pi, sin

2. 常用常量

常量说明
math.pi3.141592653589793圆周率 π
math.e2.718281828459045自然常数 e
math.tau6.283185307179586
math.inf正无穷大可用于比较、溢出检测
math.nan非数(Not a Number)未定义或不可表示的值

3. 数值与幂运算

函数说明
math.sqrt(x)平方根,等同于 x**0.5
math.pow(x, y)幂运算,返回 x**y (浮点运算)
x ** yPython 内置运算符,通常更快也更灵活
math.exp(x)e^x 指数函数
math.log(x[, base])自然对数 ln(x) 或以 base 为底的对数
math.log2(x)以 2 为底的对数
math.log10(x)以 10 为底的对数

示例

import math

print(math.sqrt(2)) # ≈1.414213562
print(math.pow(2, 10)) # 1024.0
print(math.exp(1)) # ≈2.718281828
print(math.log(8, 2)) # 3.0
print(math.log2(8)) # 3.0
print(math.log10(100)) # 2.0

4. 三角与反三角函数

函数说明
math.sin(x)正弦,参数 x弧度为单位
math.cos(x)余弦
math.tan(x)正切
math.asin(x)反正弦,返回值在 [-π/2, π/2]
math.acos(x)反余弦,返回值在 [0, π]
math.atan(x)反正切,返回值在 (-π/2, π/2)
math.atan2(y, x)根据点 (x,y) 计算极角,完整覆盖四象限
math.degrees(x)弧度转度数
math.radians(x)度数转弧度

示例

import math

angle_deg = 30
angle_rad = math.radians(angle_deg)
print(math.sin(angle_rad)) # 0.5

# 反过来
print(math.degrees(math.asin(0.5))) # 30.0

5. 舍入与取整

函数说明
math.ceil(x)向上取整,返回大于等于 x 的最小整数
math.floor(x)向下取整,返回小于等于 x 的最大整数
math.trunc(x)截断小数部分,等同于 int(x)(对负数向 0 方向截断)
round(x[, n])内置函数,四舍五入,n 指定小数位;与 math 模块同名
import math

print(math.ceil(3.2)) # 4
print(math.floor(3.8)) # 3
print(math.trunc(-3.8)) # -3
print(round(3.14159, 2)) # 3.14

6. 组合与特殊函数

函数说明
math.hypot(x, y)计算 √(x² + y²),常用于欧几里得距离
math.dist(p, q)计算两点(向量)之间的欧几里得距离,Python 3.8+
math.comb(n, k)组合数 C(n, k),Python 3.8+
math.perm(n, k)排列数 P(n, k),Python 3.8+
math.factorial(n)阶乘 n!,整型运算
math.gcd(a, b)最大公约数
math.lcm(a, b)最小公倍数,Python 3.9+
math.isfinite(x)检查是否为有限数(非 inf, 非 nan)
math.isinf(x)检查是否为正负无穷
math.isnan(x)检查是否为 NaN
import math

print(math.hypot(3, 4)) # 5.0
print(math.dist((1,2,3), (4,5,6)))# 5.196152422... (sqrt(27))
print(math.comb(5, 2)) # 10
print(math.perm(5, 2)) # 20
print(math.factorial(6)) # 720
print(math.gcd(12, 30)) # 6
print(math.lcm(12, 30)) # 60
print(math.isfinite(1e100)) # True
print(math.isinf(float('inf'))) # True
print(math.isnan(float('nan'))) # True

7. 使用注意

  1. 类型限制
    • 大多数函数要求参数为数值(intfloat);
    • 若传入其他类型(如 Nonestr),会抛出 TypeError
  2. 精度误差
    • 浮点运算不可避免存在精度误差,尤其是 sincoslog 等函数;
    • 比较浮点数时,建议使用差值绝对值 abs(a - b) < ε
  3. 性能
    • Python 内置的幂运算 x**y 往往比 math.pow(x, y) 更快;
    • 组合数、排列数、阶乘等大整数运算速度依赖于 Python 整数的性能。
  4. 扩展
    • 若需更高阶的数学函数(Bessel 函数、椭圆积分等),可使用 scipy.specialsympy

通过上面对常量、基础运算、三角函数、取整/舍入、组合与特殊函数,以及使用注意的全面介绍,你可以掌握 math 模块的绝大多数常见功能。如有更深入或特殊场景的需求,欢迎继续提问!

eval()字符串表达式求值

下面我从多个角度来详细介绍 Python 内置函数 eval() 的用法、原理、风险和替代方案。


一、eval() 是什么?

  • 定义 eval(expression, globals=None, locals=None) 会把字符串 expression 当作一个Python 表达式来求值,并返回计算结果。
  • 作用范围
    • 只能计算单个表达式(如算术、逻辑、索引、函数调用等),不能执行多条语句或定义类、函数、循环等。
    • 如果需要执行多条语句,请使用 exec()

二、函数签名与参数

eval(expression, globals=None, locals=None)
  1. expression(必选)
    • 要求是一个字符串,内容必须是合法的 Python 表达式,否则会抛出 SyntaxError
  2. globals(可选)
    • 指定全局命名空间,通常是一个字典。
    • 如果不传,默认使用当前模块的全局命名空间。
  3. locals(可选)
    • 指定局部命名空间,也可以是一个字典。
    • 如果只传 globals 而不传 localslocals 会默认与 globals 相同。
# 示例:在自定义命名空间中求值
ns = {"x": 10, "y": 20}
print(eval("x + y", ns)) # 30
print(eval("z", {"z": 5}, {})) # 5

三、基本使用示例

# 简单算术表达式
print(eval("1 + 2 * 3")) # 输出 7

# 使用变量
a = 5
b = 8
print(eval("a * b + 10")) # 输出 50

# 调用内置函数
print(eval("min([3, 1, 4, 2])")) # 输出 1

# 结合 globals/locals
env = {"foo": lambda x: x**2}
print(eval("foo(7)", env)) # 输出 49

四、evalexec 的区别

evalexec
输入类型仅限单个表达式字符串任意代码块(表达式、语句、定义)
返回值返回表达式计算结果无返回(返回 None
用途计算并获取结果动态执行代码,如定义函数类等

五、使用 eval 的风险与注意

  1. 安全问题

    • 任意代码执行:如果把用户输入直接传给 eval,对方就能执行任意 Python 代码,比如读取文件、删除数据、网络请求等。

    • 示例

      user_input = "__import__('os').system('rm -rf /')"
      eval(user_input) # 极端危险:系统命令被执行
  2. 性能开销

    • 每次调用都要经过解析器重新编译表达式为字节码,再执行。对简单、小规模运算无碍,但大量或频繁调用时,性能不如直接写运算式。
  3. 调试难度

    • 字符串里有语法错误,只有在运行到 eval 时才发现,难以在开发期提前捕获。

六、安全使用技巧

  1. 限定命名空间 只给 eval 一个"干净"的全局和局部字典,屏蔽不必要的内置或敏感模块:

    safe_globals = {"__builtins__": {}}
    safe_locals = {"x": 42, "y": 3.14}
    print(eval("x * y", safe_globals, safe_locals)) # 131.88

    这样用户即便写了 __import__('os') 也无法访问。

  2. 预先校验表达式

    • 用正则或 AST 模块检查,只允许数字、运算符、括号、指定变量名等。
    • 例如用 ast.parse(expr, mode='eval') 并遍历 AST,拒绝 CallAttributeImport 等节点。
  3. 替代小型 DSL 对于只需要算术或简单函数的场景,不用完全暴露 Python,自己写一个简单的解析器或使用第三方安全表达式库(如 astevalnumexprsimpleeval 等)。


七、避免或替代方案

  1. 内置运算
    • 直接用 Python 表达式或函数,不用动态 eval
  2. 第三方库
    • simpleeval:轻量安全,可自定义允许的操作符与函数。
    • asteval:基于 AST,支持大部分 Python 语法但限制危险操作。
  3. 手写解析器
    • 对于非常简单的语法(如只要加减乘除、括号、整数),用递归下降或**栈算法(算术表达式求值)**常常更安全、更可控。

小结

  • eval() 功能强大——能动态计算任意表达式,但也因此非常危险。
  • 若场景确实需要动态求值,请务必限制命名空间校验输入、或使用更安全的替代方案。
  • 日常开发中,优先考虑直接写代码使用专用库,只有在确实需要时才用 eval

set用add()添加元素

没关系,容易混淆。简单总结下:

  • 列表list)是有序可重复的容器,向里添加元素要用 append()

    L = [1, 2]
    L.append(3) # L 变成 [1, 2, 3]
  • 集合set)是无序不重复的容器,向里添加元素要用 add()

    S = {1, 2}
    S.add(2) # S 还是 {1, 2}
    S.add(3) # S 变成 {1, 2, 3}

注意:

  1. append 是列表的方法,不能用在集合上;
  2. add 是集合的方法,用来把一个新元素放进集合,如果元素已存在则什么也不做;
  3. 集合会自动去重,所以无需额外检查。