普通视图

发现新文章,点击刷新页面。

14 Python 分支语句if

作者 木灵鱼儿
2026年1月28日 14:30

前言

生活中我们无时无刻不在做选择:如果 明天下雨, 带伞;否则 戴墨镜。

在编程中,这种“做选择”的逻辑就是分支语句(也叫条件判断)。在Python中,我们主要使用 if(如果)、else(否则)这两个关键字来实现。

⚠️ 写在前面的重要规则:缩进

Python 和其他语言最大的不同在于:它靠“缩进”(通常是4个空格或1个Tab)来判断代码属于哪一部分。

  • 如果代码缩进了,说明它属于上面那行语句的“下级”。
  • 记住: 冒号 : 后面一定要换行并缩进!

1. 单分支语句 (if)

这是最简单的判断。只有当条件满足(为真)时,才执行代码;如果不满足,就什么都不做,直接跳过。

语法结构:

if 条件:
    # 条件成立时执行的代码

生活场景: 如果你满18岁了,就可以进网吧。

示例代码:

age = 20  # 定义一个年龄变量

if age >= 18:
    # 这行代码缩进了,只有条件满足才会执行
    print("恭喜你,已满18岁!")
    print("你可以进入网吧。")

2. 双分支语句 (if - else)

这个比单分支多了一个选择。如果条件满足,做A事;否则(条件不满足),做B事。二者必选其一。

语法结构:

if 条件:
    # 条件成立时执行的代码
else:
    # 条件不成立时执行的代码

生活场景: 考试成绩判断,及格了吃大餐,不及格吃“竹笋炒肉”。

示例代码:

score = 59  # 你的分数

if score >= 60:
    print("成绩及格!")
    print("奖励一顿火锅。")
else:
    # 注意 else 后面也有冒号
    print("成绩不及格...")
    print("准备回家挨骂吧。")

3. 分支嵌套 (if 里面还有 if)

所谓嵌套,就像“俄罗斯套娃”一样,在一个判断里面,再包一层判断。当你需要满足“层层关卡”才能触发结果时,就会用到嵌套。

语法结构:

if 条件1:
    if 条件2:
        # 条件1 和 条件2 都满足
    else:
        # 条件1 满足,但 条件2 不满足
else:
    # 条件1 就不满足

生活场景: 坐公交车。

  • 第一关:你有没有钱(或公交卡)?如果有,上车;如果没有,走路。
  • 第二关(上车后):有没有空座位?如果有,坐下;如果没有,站着。

示例代码:

has_money = True   # 有没有钱
has_seat = False   # 有没有座位

print("准备坐公交车...")

if has_money:
    print("投币成功,上车了!")

    # 开始进行第二层判断(注意缩进层级变深了)
    if has_seat:
        print("发现空座位,坐下休息。")
    else:
        print("哎呀没座位了,只能站着。")

else:
    # 对应最外层的 if
    print("身上没钱,只能走路回家了。")

💡 额外补充:多分支 (if - elif - else)

虽然你问的是单分支和双分支,但还有一个很常用的多分支,用于处理超过两种情况的场景。

生活场景: 这里的衣服有三个尺码:S码、M码、L码。

size = "M"

if size == "S":
    print("这是小号")
elif size == "M":  # elif 是 "else if" 的缩写,意思是“或者...”
    print("这是中号")
elif size == "L":
    print("这是大号")
else:
    print("没有这个尺码")

13 Python 字符串详解

作者 木灵鱼儿
2026年1月23日 14:35

字符串的转义符 (Escape Characters)

在字符串中,反斜杠 \ 用来引入特殊的字符序列,这些序列被称为转义符。使用转义符是因为在 Python 字符串中,有些字符具有特殊含义(比如换行、制表符、引号等),直接使用可能会引起语法错误或无法达到预期效果。

比如:字符串表示常常用单引号 ' 或双引号 " 包围,如果字符串本身包含这些引号,就需要使用转义符。

常见的转义符包括:

转义符含义示例说明
\\反斜杠本身"C:\\Users\\name"C:\Users\name
\'单引号'It\'s OK'It's OK
\"双引号"He said \"Hi\""He said "Hi"
\n换行符"Hello\nWorld" → 两行输出
\t制表符(Tab)"Name:\tAlice" → 名字后有空格对齐
\r回车(Carriage Return)通常与 \n 配合用于 Windows 换行
\b退格(Backspace)"a\bc" → 显示为 c(较少用)
\f换页符(Form Feed)打印时换页(现代很少用)
\v垂直制表符控制打印机垂直间距(罕见)
\0空字符(Null)表示 ASCII 0(极少用)
💡 注意:如果在字符串中写了无效的转义序列(如 \z),Python 会发出 SyntaxWarning(3.12+ 版本会报错),所以建议只使用标准转义符。

转义符使用示例

# 示例 1:包含单引号和双引号的字符串
s1 = 'It\'s a "great" day!'
print(s1)  # 输出: It's a "great" day!

# 示例 2:换行和制表
s2 = "第一行\n\t第二行(缩进)\n第三行"
print(s2)
# 输出:
# 第一行
#     第二行(缩进)
# 第三行

# 示例 3:文件路径中的反斜杠(需转义)
path = "C:\\Users\\Alice\\Documents"
print(path)  # 输出: C:\Users\Alice\Documents

原始字符串(Raw String)

为了避免频繁使用反斜杠转义,Python 提供了原始字符串(raw string)。在字符串前加 rR,即可让字符串中的所有反斜杠都被视为普通字符,不再具有转义功能。

# 使用原始字符串表示文件路径
raw_path = r"C:\Users\Alice\Documents"
print(raw_path)  # 输出: C:\Users\Alice\Documents

# 原始字符串常用于正则表达式
import re
pattern = r"\d+\.\d+"  # 匹配小数,如 3.14
text = "The value is 3.14 and 2.71."
matches = re.findall(pattern, text)
print(matches)  # 输出: ['3.14', '2.71']
⚠️ 注意:原始字符串不能以奇数个反斜杠结尾,例如 r"hello\" 是非法的,因为最后一个 \ 会试图转义后面的引号,但原始字符串不允许转义。如果需要结尾反斜杠,可改用普通字符串或拼接:r"C:\folder" + "\\"

字符串的格式化输出

Python 有多种格式化方式(如 % 操作符, .format()),但从 Python 3.6 开始,f-string (Formatted String Literals) 成为了最推荐、最高效的方式。所以这里就不多说其他方式了。

f-string 的详细用法:

语法:在字符串引号前加上 fF,在大括号 {} 中直接填入变量或表达式。

基础变量与表达式

name = "Python"
age = 30
# 直接引用变量
print(f"I am learning {name}, it is {age} years old.")

# 支持表达式
print(f"Next year, it will be {age + 1} years old.")

# 支持函数调用
print(f"Name in upper case: {name.upper()}")

浮点数精度控制

使用 :.nf 控制小数点位数。

pi = 3.1415926
print(f"Pi is approx {pi:.2f}")  # 保留2位小数 -> 3.14

宽度与对齐

格式:{value : 填充字符 对齐方式 宽度}

  • < : 左对齐
  • > : 右对齐
  • ^ : 居中对齐
text = "Hi"
# 宽度10,默认右对齐(字符串通常默认左对齐,但在f-string指定宽度后需注意)
print(f"|{text:10}|")    # 输出: |Hi        |

# 宽度10,右对齐
print(f"|{text:>10}|")   # 输出: |        Hi|

# 宽度10,居中对齐,使用 * 填充
print(f"|{text:*^10}|")  # 输出: |****Hi****|

数字格式化 (千分位、进制)

money = 1234567890
print(f"Total: {money:,}")   # 千分位分隔 -> 1,234,567,890

num = 255
print(f"Binary: {num:b}")    # 二进制 -> 11111111
print(f"Hex: {num:x}")       # 十六进制 -> ff

调试模式 (Python 3.8+)

在变量后加 =,可以同时输出“变量名=变量值”,非常适合调试。

x = 10
y = 20
print(f"{x=}, {y=}, {x+y=}")
# 输出: x=10, y=20, x+y=30

字符串的索引和切片

字符串是有序序列,每个字符都有确定的位置。

索引 (Indexing)

  • 正向索引:从 0 开始,从左向右递增。
  • 负向索引:从 -1 开始,从右向左递减(-1 代表最后一个字符)。
s = "PYTHON"
#  0  1  2  3  4  5  (正向)
#  P  Y  T  H  O  N
# -6 -5 -4 -3 -2 -1  (负向)

print(s[0])   # P
print(s[5])   # N
print(s[-1])  # N (倒数第一个)
print(s[-2])  # O (倒数第二个)

切片 (Slicing)

语法:[start : stop : step]

  • start: 起始索引(包含)。默认为 0。
  • stop: 结束索引(不包含)。默认为字符串长度。
  • step: 步长。默认为 1,如果设置为2,则每两个字符取一个。

重要规则:

  1. 顾头不顾尾:取值范围是 [start, stop)
    假设现在有字符串 s = "Hello World",则 s[0:5] 会取索引取得 0 到 4 的字符,即 "Hello",不包含索引 5 的字符(空格)。所以被称为“顾头不顾尾”。
  2. 步长方向

    • step 为正,从左往右取。
    • step 为负,从右往左取(实现反序的关键)。
s = "Hello World"

# 1. 基础切片
print(s[0:5])    # 取索引0到4 -> "Hello"
print(s[:5])     # start省略,默认从头 -> "Hello"
print(s[6:])     # stop省略,默认到尾 -> "World"
print(s[:])      # 完整拷贝 -> "Hello World"

# 2. 带步长的切片
print(s[::2])    # 每隔一个字符取一个 -> "HloWrd"

# 3. 负数索引切片
print(s[-5:-1])  # 取倒数第5个到倒数第2个 -> "Worl" (不包含-1对应的d)

# 4. 字符串反转 (面试常考)
print(s[::-1])   # 步长为负,从后往前取 -> "dlroW olleH"

字符串的常见操作符

除了方法外,Python 提供了很多操作符来处理字符串。

操作符描述示例
+字符串拼接"Hi" + "!" -> "Hi!"
*重复输出"A" * 3 -> "AAA"
in成员判断(存在)"a" in "abc" -> True
not in成员判断(不存在)"z" not in "abc" -> True
==判断内容是否相等"a" == "a" -> True
> / <按字典序(ASCII码)比较"a" < "b" -> True

特别说明:

  • +=:虽然看起来像是在原字符串上修改,但因为字符串是不可变的,实际上 s += "a" 是创建了一个新字符串并重新赋值给变量 s
s1 = "Hello"
s2 = "World"

# 拼接
print(s1 + " " + s2)  # "Hello World"

# 重复
print("-" * 20)       # 打印分割线: --------------------

# 成员检测
text = "Python is cool"
if "cool" in text:
    print("Found it!")

# 比较
print("apple" < "banana") # True (a 在 b 前面)

字符串的常见内置方法

方法功能简述
str.capitalize()首字母大写,其余小写
str.casefold()返回更激进的小写形式(用于无大小写比较)
str.center(width[, fillchar])居中对齐,用指定字符填充
str.count(sub[, start[, end]])统计子串出现次数
str.encode([encoding[, errors]])编码为 bytes
str.endswith(suffix[, start[, end]])判断是否以某后缀结尾
str.expandtabs([tabsize])\t 替换为空格
str.find(sub[, start[, end]])查找子串首次出现位置(未找到返回 -1)
str.rfind(sub[, start[, end]])从右向左查找子串
str.index(sub[, start[, end]])类似 find,但未找到会抛出异常
str.rindex(sub[, start[, end]])从右向左查找,未找到抛异常
str.isalnum()是否只包含字母和数字
str.isalpha()是否只包含字母
str.isascii()是否所有字符都是 ASCII
str.isdecimal()是否只包含十进制数字(如 '0'–'9')
str.isdigit()是否只包含数字(包括 Unicode 数字)
str.isnumeric()是否表示数值(包括分数、罗马数字等)
str.islower()是否全为小写
str.isupper()是否全为大写
str.istitle()是否为标题格式(每个单词首字母大写)
str.isspace()是否只包含空白字符(空格、\n、\t 等)
str.join(iterable)用当前字符串连接可迭代对象中的元素
str.ljust(width[, fillchar])左对齐,右侧填充
str.rjust(width[, fillchar])右对齐,左侧填充
str.lower()转为小写
str.upper()转为大写
str.title()每个单词首字母大写
str.swapcase()大小写互换
str.lstrip([chars])去除左侧指定字符(默认空白)
str.rstrip([chars])去除右侧指定字符
str.strip([chars])去除两侧指定字符
str.partition(sep)从左分割为三元组(前、分隔符、后)
str.rpartition(sep)从右分割为三元组
str.replace(old, new[, count])替换子串
str.split([sep[, maxsplit]])按分隔符分割为列表
str.rsplit([sep[, maxsplit]])从右开始分割
str.splitlines([keepends])按行分割(\n, \r\n 等)
str.startswith(prefix[, start[, end]])判断是否以某前缀开头
str.zfill(width)在左侧补零至指定宽度
💡 所有方法均不改变原字符串,而是返回新对象。

1. capitalize()

将字符串首字母大写,其余转为小写。

s = "hello WORLD"
print(s.capitalize())  # 输出: Hello world

2. casefold()

lower() 更彻底地转换为小写,适用于国际化比较。

s = "ß"  # 德语 sharp s
print(s.lower())     # 'ß'
print(s.casefold())  # 'ss'

3. center(width, fillchar=' ')

居中对齐,总长度为 width,不足部分用 fillchar 填充。

s = "Python"
print(s.center(10, '*'))  # 输出: **Python**

4. count(sub, start=0, end=len(s))

统计子串 sub 出现的次数。

s = "banana"
print(s.count('a'))      # 3
print(s.count('na', 2))  # 2(从索引2开始)

5. encode(encoding='utf-8', errors='strict')

将字符串编码为 bytes。

s = "你好"
b = s.encode('utf-8')
print(b)  # b'\xe4\xbd\xa0\xe5\xa5\xbd'

6. endswith(suffix, start=0, end=len(s))

判断是否以 suffix 结尾(支持元组)。

filename = "script.py"
print(filename.endswith('.py'))       # True
print(filename.endswith(('.py', '.js')))  # True

7. expandtabs(tabsize=8)

\t 替换为指定数量的空格。

s = "a\tb"
print(s.expandtabs(4))  # "a   b"(a 后跟 3 个空格)

8. find(sub, start=0, end=len(s))

返回子串首次出现的索引,未找到返回 -1

s = "hello"
print(s.find('l'))   # 2
print(s.find('x'))   # -1

9. rfind(sub, ...) / index(sub, ...) / rindex(sub, ...)

  • rfind: 从右往左找,返回最右匹配位置
  • index: 同 find,但找不到时抛出 ValueError
  • rindex: 同 rfind,但找不到时报错
s = "hello"
print(s.rfind('l'))   # 3
print(s.index('e'))   # 1
# print(s.index('x')) # 报错!

10. is...() 系列判断方法

返回 TrueFalse,常用于输入验证。

print("abc".isalpha())      # True
print("abc123".isalnum())   # True
print("123".isdigit())      # True
print("⅕".isnumeric())      # True(分数)
print("Hello".istitle())    # True
print("   \t\n".isspace())  # True

区别:

  • isdecimal():仅限 0–9(最严格)
  • isdigit():包括上标数字(如²)
  • isnumeric():还包括中文数字、罗马数字等

11. join(iterable)

用当前字符串连接可迭代对象中的字符串。

words = ['Python', 'is', 'fun']
print(' '.join(words))   # "Python is fun"
print('-'.join('abc'))   # "a-b-c"
⚠️ 可迭代对象中的元素必须是字符串,否则报错。

12. ljust(width, fillchar) / rjust(width, fillchar)

左对齐 / 右对齐填充。

s = "OK"
print(s.ljust(5, '.'))   # "OK..."
print(s.rjust(5, '.'))   # "...OK"

13. lower() / upper() / title() / swapcase()

大小写转换。

s = "Hello World"
print(s.lower())     # "hello world"
print(s.upper())     # "HELLO WORLD"
print(s.title())     # "Hello World"
print(s.swapcase())  # "hELLO wORLD"

14. strip() / lstrip() / rstrip()

去除首尾指定字符(默认为空白字符)。

s = "  \t hello  \n "
print(s.strip())         # "hello"
print("...hello...".strip('.'))  # "hello"

15. partition(sep) / rpartition(sep)

按第一个/最后一个分隔符分割为三部分。

s = "name=value=extra"
print(s.partition('='))    # ('name', '=', 'value=extra')
print(s.rpartition('='))   # ('name=value', '=', 'extra')

16. replace(old, new, count=-1)

替换子串,count 控制替换次数。

s = "apple apple"
print(s.replace("apple", "orange"))      # "orange orange"
print(s.replace("apple", "orange", 1))   # "orange apple"

17. split(sep=None, maxsplit=-1) / rsplit(...)

按分隔符分割为列表。若 sep=None,按任意空白分割。

s = "a,b,c"
print(s.split(','))          # ['a', 'b', 'c']
print("a  b\n c".split())    # ['a', 'b', 'c']

s = "a.b.c.d"
print(s.rsplit('.', 1))      # ['a.b.c', 'd'](从右切一次)

18. splitlines(keepends=False)

按行分割(识别 \n, \r\n, \r 等)。

s = "Line1\nLine2\r\nLine3"
print(s.splitlines())        # ['Line1', 'Line2', 'Line3']
print(s.splitlines(True))    # 保留换行符

19. startswith(prefix, start=0, end=len(s))

判断是否以某前缀开头(支持元组)。

url = "https://example.com"
print(url.startswith("http"))           # True
print(url.startswith(("http", "ftp")))  # True

20. zfill(width)

在左侧补零,常用于数字字符串对齐。

print("42".zfill(5))     # "00042"
print("-42".zfill(5))    # "-0042"(负号保留在最左)
昨天以前首页

12 Python 语句、表达式与运算符

作者 木灵鱼儿
2026年1月21日 17:37

语句与表达式 (Statements vs. Expressions)

这是编程中非常核心的概念,区分它们有助于你理解代码是如何运行的。

什么是表达式 (Expression)?

表达式是“由于计算而产生值”的代码片段。 简单来说,只要你能把它放在 print() 函数里打印出来的,通常都是表达式。

  • 特点:它总会返回一个结果(值)。
  • 例子

    • 1 + 1 (计算结果为 2)
    • "Hello World" (字符串本身也是表达式)
    • len("abc") (函数调用返回 3)

什么是语句 (Statement)?

语句是“执行某种动作”的代码单位。 它像是一个指令,告诉 Python 去做某件事(比如赋值、循环、判断),而不是为了生成一个值。

  • 特点:它改变了程序的状态(例如改变变量的值),但它本身不“返回”值。
  • 例子

    • a = 10 (赋值语句)
    • if x > 0: (条件语句)
    • import math (导入语句)

代码对比

# --- 表达式 (Expressions) ---
# 它们都有“值”,可以直接打印
print(3 + 5)        # 8
print("Py" + "thon") # Python

# --- 语句 (Statements) ---
x = 10  # 这是一个赋值语句。
# print(x = 10)  # 报错!因为赋值语句本身没有返回值,不能被打印。

# 特例:Python 3.8+ 引入了海象运算符 (:=),让赋值也能成为表达式
if (n := len("test")) > 3:
    print(f"长度是 {n}") # 这里 n := ... 既赋值了又返回了值

计算/算术运算符 (Arithmetic Operators)

用于执行常见的数学运算。Python 的数学运算非常符合直觉。

运算符描述示例结果
+加法5 + 38
-减法5 - 32
*乘法5 * 315
/除法 (总是返回浮点数)10 / 25.0
//整除 (向下取整)10 // 33
%取模 (求余数)10 % 31
**幂运算 (次方)2 ** 38

代码示例

a = 10
b = 3

print(f"{a} 除以 {b} 等于: {a / b}")       # 3.3333...
print(f"{a} 整除 {b} 等于: {a // b}")      # 3 (去掉小数部分)
print(f"{a} 对 {b} 取余 等于: {a % b}")    # 1 (10 - 3*3 = 1)
print(f"2 的 3 次方: {2 ** 3}")           # 8

# 小技巧:利用 % 判断奇偶
num = 7
if num % 2 == 0:
    print("偶数")
else:
    print("奇数") # 输出这个

比较运算符 (Comparison Operators)

用于比较两个值,结果永远是布尔值 (TrueFalse)。

运算符描述示例
==等于 (注意是两个等号)5 == 5 (True)
!=不等于5 != 3 (True)
>大于5 > 3 (True)
<小于5 < 3 (False)
>=大于等于5 >= 5 (True)
<=小于等于4 <= 5 (True)

代码示例

score = 85
pass_mark = 60

is_passed = score >= pass_mark  # 这是一个表达式,计算结果赋值给变量
print(f"是否及格: {is_passed}") # True

# Python 独有的连写比较
x = 5
# 判断 x 是否在 1 到 10 之间
print(1 < x < 10)  # True (相当于 1 < x and x < 10)

赋值运算符 (Assignment Operators)

除了基本的赋值 =,Python 还提供了复合赋值运算符,用于简化代码(Syntactic Sugar)。

运算符等价于示例
=赋值c = a + b
+=c = c + ac += a
-=c = c - ac -= a
*=c = c * ac *= a
/=c = c / ac /= a

代码示例

count = 0

# 传统写法
count = count + 1

# 推荐写法 (更简洁)
count += 1
print(count) # 2

price = 100
price *= 0.8 # 打八折
print(price) # 80.0

逻辑运算符 (Logical Operators)

用于组合多个条件,结果也是布尔值。

运算符描述逻辑
and只有两边都为 True,结果才为 True
or只要有一边为 True,结果就为 True
not取反,TrueFalse,反之亦然

关键概念:短路运算 (Short-circuit Evaluation)

Python 比较“懒”,如果看完左边就能确定结果,它就不会去计算右边。

  • A and B: 如果 A 是 False,结果必为 False,Python 不会去计算 B。
  • A or B: 如果 A 是 True,结果必为 True,Python 不会去计算 B。

代码示例

age = 25
has_ticket = True

# and 演示
if age >= 18 and has_ticket:
    print("允许进入")

# not 演示
is_raining = False
if not is_raining:
    print("天气不错,出去玩吧")

# 短路演示
def risky_function():
    print("我被运行了!")
    return True

# 因为 True or ... 结果已经是 True,所以 risky_function 不会被执行
if True or risky_function():
    print("短路测试结束")
# 输出结果中不会出现 "我被运行了!"

成员运算符 (Membership Operators)

用来测试一个序列(如列表、字符串、元组)中是否包含指定的项。这让代码读起来非常像英语。

运算符描述
in如果在序列中找到值,返回 True
not in如果在序列中没有找到值,返回 True

代码示例

# 1. 在列表中查找
fruits = ["apple", "banana", "cherry"]
if "banana" in fruits:
    print("我想吃香蕉")

# 2. 在字符串中查找
message = "Error: File not found"
if "Error" in message:
    print("出现错误!")

# 3. 在字典中查找 (默认查找 Key)
user = {"name": "Jack", "age": 20}
if "name" in user:
    print("用户有名字属性")

运算符优先级 (Operator Precedence)

当一个表达式中包含多个运算符时,Python 按照什么顺序计算?这就涉及到了优先级。

基本原则 (由高到低)

  1. () 括号 (最高优先级,想先算谁就括谁)
  2. ** 幂运算
  3. * / // % 乘除类
  4. + - 加减类
  5. > < == 比较类
  6. not
  7. and
  8. or (逻辑运算通常最低)

代码示例

# 案例 1: 乘法优先于加法
result = 2 + 3 * 4
print(result) # 14 (先算 3*4=12,再加 2)

# 案例 2: 使用括号改变顺序
result = (2 + 3) * 4
print(result) # 20

# 案例 3: 逻辑运算优先级 (not > and > or)
# 相当于: True or (True and False) -> True or False -> True
print(True or True and False) # True

建议:虽然有优先级规则,但在写复杂表达式时,最好使用括号 () 来明确你的意图。这样不仅避免出错,也让代码更容易被别人(和你自己)读懂。

11 Python 数据类型转换

作者 木灵鱼儿
2026年1月21日 16:51

前言

在 Python 编程中,数据类型决定了“这个数据能做什么”。例如,数字可以相加,字符串可以拼接。但如果你试图把“字符串”和“数字”相加,Python 就会直接报错。

为了让代码跑通,我们需要进行数据类型转换

显式类型转换 (强制转换)

这是最常用的方式。作为程序员,你必须明确告诉 Python:“把这个数据变成那个类型”。通常使用 Python 的内置函数(类构造器)来实现。

1. 基础转换:数字与字符串互转

这是处理用户输入(input())和日志输出时必用的技巧。

  • int(x): 转为整数。
  • float(x): 转为浮点数。
  • str(x): 转为字符串。
# 场景 1:用户输入的通常是字符串,需要转数字计算
age_str = "18"
# print(age_str + 1) # ❌ 报错:不能把字符串和数字相加

age_num = int(age_str)
print(f"明年你就 {age_num + 1} 岁了")  # ✅ 输出 19

# 场景 2:拼接日志时,必须把数字转字符串
score = 98.5
# print("成绩是: " + score) # ❌ 报错
print("成绩是: " + str(score)) # ✅ 输出 "成绩是: 98.5"

# 场景 3:小数点处理
pi = 3.14159
print(int(pi))  # ✅ 输出 3 (直接截断小数,不四舍五入)

⚡️ 避坑指南:
字符串转整数时,内容必须“看着像整数”。

  • int("123")
  • int("12.3") ❌ (报错:ValueError)
  • 解决方法:先转 float 再转 int -> int(float("12.3")) -> 12

2. 进阶转换:容器类型 (list, tuple, set)

这在数据处理中非常强大,常用于去重或修改不可变数据。

  • list(x): 转为列表(可修改)。
  • tuple(x): 转为元组(不可修改,安全)。
  • set(x): 转为集合(自动去重,无序)。
# 场景 1:利用 set 快速去重
ids = [101, 102, 101, 103, 102]
unique_ids = list(set(ids)) # 先转集合去重,再转回列表
print(unique_ids) # 输出 [101, 102, 103] (顺序可能变)

# 场景 2:修改元组(Tuple 是不可变的,必须先转 List)
data = (10, 20)
# data[0] = 99 # ❌ 报错
temp_list = list(data)
temp_list[0] = 99
data = tuple(temp_list)
print(data) # 输出 (99, 20)

3. 逻辑转换:布尔值 (bool)

用于显式判断一个变量是否“有效”。

  • bool(x): 将 x 转为 True 或 False。
print(bool(0))        # False
print(bool(""))       # False (空字符串)
print(bool([]))       # False (空列表)
print(bool("Python")) # True
print(bool(1))        # True

隐式类型转换 (自动转换)

Python 解释器在某些特定场景下,会自动帮你把一种类型转换成另一种,通常是为了保证运算不报错逻辑通顺

1. 场景一:整数与浮点数运算 (自动升格)

当整数 (int) 遇上浮点数 (float),结果会自动变成浮点数,以防止精度丢失。

a = 10   # int
b = 2.5  # float

result = a + b
print(result)       # 输出 12.5
print(type(result)) # <class 'float'>

2. 场景二:除法运算 (只要是除法,就是 float)

这是 Python 3 的特性。哪怕你能整除,单斜杠 / 的结果永远是浮点数。

x = 10
y = 2

res = x / y
print(res)       # 输出 5.0 (注意有了小数点)
print(type(res)) # <class 'float'>

# 如果想要整数结果,必须用 // (地板除)
print(x // y)    # 输出 5

3. 场景三:布尔值参与数学运算 (Bool 即 Int)

这是个冷知识:在 Python 中,True 等于 1False 等于 0。它们可以直接参与加减乘除。

# 统计及格人数的骚操作
scores = [60, 55, 80, 45, 90]

# (s >= 60) 会生成一堆 True/False
# sum() 会自动把 True 当作 1 加起来
pass_count = sum(s >= 60 for s in scores)

print(f"及格人数: {pass_count}") # 输出 3
print(True + True)             # 输出 2

4. 场景四:条件判断中的隐式转换 (Truthiness)

ifwhile 语句中,你不需要写 if len(mylist) > 0:,Python 会自动把非布尔值转换为布尔值进行判断。

  • 被视为 False: 0, 0.0, ""(空串), [], {}, None
  • 被视为 True: 其他所有值
username = ""
cart = ["Apple", "Banana"]

# 隐式转换:空字符串转为 False
if not username:
    print("请输入用户名")

# 隐式转换:非空列表转为 True
if cart:
    print(f"购物车里有 {len(cart)} 件商品")

三、 特别注意:Python 绝对不会做的隐式转换

虽然 Python 会自动把 intfloat,或者把 listbool(在 if 中),但作为一门强类型语言,它严禁数字和字符串的隐式混合运算。这与 JavaScript 等弱类型语言不同。

# JavaScript 中: "5" + 5 = "55" (自动把数字变字符串拼接)
# Python 中:

x = "5"
y = 5

# print(x + y)
# 💥 崩溃:TypeError: can only concatenate str (not "int") to str

结论:

  • 同一个家族(数字家族 int/float/bool)之间,Python 会尝试帮你隐式转换
  • 跨家族(数字 vs 字符串)之间,必须由你进行显式转换

总结速查表

转换方式核心函数/场景典型案例备注
显式 (手动)int(), float()int("10")处理输入最常用
显式 (手动)str()str(10)拼接日志/打印
显式 (手动)list(), set()list((1,2))修改数据、去重
隐式 (自动)数学运算10 + 2.5 -> 12.5低精度 -> 高精度
隐式 (自动)除法 /4 / 2 -> 2.0结果必为 float
隐式 (自动)Bool 运算True + 1 -> 2True=1, False=0
隐式 (自动)if 判断if [] -> False空值为假,非空为真

10 Python 输入输出(Input、Output、IO)函数

作者 木灵鱼儿
2026年1月21日 16:16

前言

在 Python 编程中,输入(Input)输出(Output) 是程序与用户或外部环境进行交互的桥梁。无论是简单的打印“Hello World”,还是复杂的数据处理,都离不开这两个环节。

输出函数:print()

print() 是 Python 中最常用的函数,用于将数据输出到控制台(标准输出)。

基本用法

最简单的用法是直接在括号内放入要打印的内容(字符串、数字、变量等)。

print("Hello, Python!")
print(100)
name = "Alice"
print(name)

print() 函数的参数详解

print() 函数的完整签名如下:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

参数说明

  1. *objects (位置参数):

    • 含义: 这是一个可变参数,表示你可以一次性传入多个对象,print 会自动将它们转换为字符串并输出。
    • 默认: 无。
  2. sep (分隔符):

    • 含义: 当你传入多个对象时,用来隔开这些对象的字符。
    • 默认值: ' ' (空格)。
  3. end (结束符):

    • 含义: 打印完所有内容后,自动追加的字符。
    • 默认值: '\n' (换行符)。这就是为什么每次调用 print 都会自动换行。
  4. file (输出流):

    • 含义: 指定输出的位置。
    • 默认值: sys.stdout (控制台)。你可以将其修改为文件对象,从而将内容写入文件。
  5. flush (刷新缓冲区):

    • 含义: 是否强制将流缓冲区的内容立即输出。
    • 默认值: False

代码示例

示例 A:使用 sep 自定义分隔符

# 默认使用空格分隔
print("Apple", "Banana", "Cherry")
# 输出: Apple Banana Cherry

# 使用逗号分隔
print("Apple", "Banana", "Cherry", sep=", ")
# 输出: Apple, Banana, Cherry

# 使用换行符分隔(实现列表效果)
print("Line 1", "Line 2", "Line 3", sep="\n")

示例 B:使用 end 控制换行

# 默认换行
print("Hello")
print("World")
# 输出:
# Hello
# World

# 不换行,用空格连接
print("Hello", end=" ")
print("World")
# 输出: Hello World

# 制作简单的进度条效果
import time
print("Loading", end="")
for i in range(3):
    print(".", end="")
    time.sleep(0.5)
# 输出: Loading... (动态出现)

输入函数:input()

input() 函数用于暂停程序运行,等待用户从键盘输入文本,并按下回车键。

基本用法与参数

variable = input(prompt)
  • prompt (可选): 一个字符串,用来提示用户输入什么内容。
  • 返回值: 永远是字符串 (str) 类型。这是初学者最容易犯错的地方。

代码示例

示例 A:基础输入

name = input("请输入您的名字: ")
print("你好,", name)

示例 B:类型转换 (关键点)

由于 input() 返回的是字符串,如果你需要数字(例如进行计算),必须进行类型转换(Casting)

# 错误示范
# age = input("请输入年龄: ")
# print(age + 1)  # 报错!不能将字符串和整数相加

# 正确示范
age_str = input("请输入年龄: ")
age = int(age_str)  # 转换为整数
print(f"明年由于您将是 {age + 1} 岁。")

# 简写方式
height = float(input("请输入身高(米): ")) # 转换为浮点数
print("您的身高是:", height)

进阶:格式化输出

在实际开发中,我们经常需要将变量嵌入到字符串中输出。Python 提供了几种主要方式。

f-Strings (推荐,Python 3.6+)

这是目前最现代、最快、最易读的方式。在字符串前加 f,然后在 {} 中直接写变量或表达式。

name = "Bob"
score = 95.5

print(f"学生 {name} 的分数是 {score}")
print(f"分数翻倍是: {score * 2}")

format() 方法 (兼容性好)

在字符串中使用 {} 占位,后面调用 .format()

name = "Bob"
score = 95.5

print("学生 {} 的分数是 {}".format(name, score))
# 也可以指定位置
print("学生 {0} 的分数是 {1}".format(name, score))

百分号 % (旧式,类似 C 语言)

name = "Bob"
score = 95

print("学生 %s 的分数是 %d" % (name, score))
# %s 代表字符串,%d 代表整数,%f 代表浮点数

拓展:文件输入输出 (File I/O)

虽然通常说的“输入输出”指控制台,但读写文件也是 I/O 的重要部分。Python 使用 open() 函数。

写入文件 (printfile 参数技巧)

你可以直接利用 print 函数将内容写入文件,而不需要显式调用 write 方法。

# 'w' 模式表示写入(如果文件存在则覆盖)
with open("output.txt", "w", encoding="utf-8") as f:
    print("这是一行测试文本", file=f)
    print("这是第二行", file=f)

print("写入完成!")

读取文件

# 'r' 模式表示读取
with open("output.txt", "r", encoding="utf-8") as f:
    content = f.read() # 读取全部内容
    print("文件内容如下:")
    print(content)

10 Python 输入输出(Input、Output、IO)函数

9 Python 基础数据类型:NoneType 与 None

作者 木灵鱼儿
2026年1月21日 15:46

前言

在学习 Python 的过程中,你一定会经常看到 None 这个词。它看起来像是一个单词,但它在 Python 编程中有着非常特殊的地位。
简单来说,None 是 Python 中表示“空”或“无”的特殊对象。

什么是 NoneType?

在 Python 中,每样东西都是对象,都有自己的类型(Type)。

  • 数字属于 intfloat 类型
  • 字符串属于 str 类型
  • None 属于 NoneType 类型

NoneType 只有一个值,那就是 None。它是一个单例对象(Singleton),这意味着无论你在程序的哪个地方使用 None,它引用的都是内存中同一个“空”对象。

如何声明和定义

声明一个 None 变量非常简单,直接赋值即可。注意首字母必须大写

# 正确写法
my_variable = None

# 查看类型
print(type(my_variable))
# 输出: <class 'NoneType'>
注意None 不等于 0,也不等于空字符串 "",更不等于 False。它就是一个独特的、表示“什么都没有”的对象。
x = None
print(x == 0)       # False
print(x == "")      # False
print(x == False)   # False

常见使用场景

为什么我们需要一个表示“无”的对象?以下是 None 在日常开发中最常见的三个用途:

场景一:变量占位符(初始化)

当你需要声明一个变量,但暂时还不知道它的具体值(或者它的值需要在稍后通过逻辑计算得出)时,可以使用 None 进行初始化。

# 比如:我们要从数据库读取用户信息,但还没开始读
user_data = None

# ... 执行一些连接数据库的操作 ...
success = True

if success:
    user_data = {"name": "Alice", "age": 25}

# 检查是否成功获取数据
if user_data is not None:
    print(f"获取成功: {user_data['name']}")
else:
    print("获取失败或尚未初始化")

场景二:函数的默认返回值

在 Python 中,如果一个函数没有显式地写 return 语句,或者只写了 return 但后面没跟值,那么这个函数默认返回的就是 None

def say_hello(name):
    print(f"Hello, {name}!")
    # 这里没有写 return,默认 return None

result = say_hello("Bob")
print(result)
# 输出: None

利用这个特性,我们可以通过判断返回值是否为 None 来处理异常情况(比如在列表中查找元素,没找到时返回 None)。

def find_student(student_list, target):
    for student in student_list:
        if student == target:
            return f"找到了: {student}"
    # 循环结束都没找到,返回 None
    return None

res = find_student(["Tom", "Jerry"], "Spike")

if res is None:
    print("查无此人")
else:
    print(res)

场景三:作为函数参数的默认值(避免可变对象陷阱)

这是 Python 面试和实战中非常重要的一个知识点。
如果你想让函数的某个参数默认是一个空列表 [] 或空字典 {}千万不要直接在参数里写 [],而应该使用 None

❌ 错误的写法:

def add_item(item, box=[]):  # 危险!默认参数 box 在内存中只创建一次
    box.append(item)
    return box

print(add_item("苹果")) # 输出 ['苹果']
print(add_item("香蕉")) # 输出 ['苹果', '香蕉'] -> 并不是我们预期的只有香蕉!

✅ 正确的写法(使用 None):

def add_item(item, box=None):
    if box is None:
        box = []  # 每次调用时创建一个新的列表
    box.append(item)
    return box

print(add_item("苹果")) # 输出 ['苹果']
print(add_item("香蕉")) # 输出 ['香蕉'] -> 正常了

如何正确判断 None

if 语句中判断一个变量是否为 None 时,Python 社区的最佳实践是使用 is 运算符,而不是 ==

  • 推荐if x is None:
  • 不推荐if x == None:

原因

  1. 速度更快is 比较的是对象的内存地址(ID),而 None 是单例,比较地址效率极高。
  2. 更安全:有些自定义的类可能会重写 __eq__(即 ==)方法,导致 x == None 返回不可预知的结果,而 is 永远只判断它是不是那个唯一的 None 对象。
value = None

# 判断是否为 None
if value is None:
    print("变量是空的")

# 判断是否不为 None
if value is not None:
    print("变量有值")

8 Python 基础数据类型:布尔类型 (Boolean)

作者 木灵鱼儿
2026年1月21日 15:05

前言

在 Python 编程中,布尔类型(bool) 是最基础也是最重要的数据类型之一。这就好比我们生活中的电灯开关,只有“开”和“关”两种状态。在程序的世界里,它们对应着 真(True)假 (False)

掌握布尔类型,是编写程序逻辑(比如“如果……就……”)的关键。

什么是布尔值?如何声明?

Python 中的布尔值只有两个:

  • True:代表“真”、“对”、“成立”。
  • False:代表“假”、“错”、“不成立”。

⚠️ 注意: 在 Python 中,TrueFalse 的首字母必须大写

声明方式

你可以直接将布尔值赋值给变量,或者通过比较运算产生布尔值。

# 1. 直接赋值
is_student = True
is_raining = False

print(is_student)  # 输出: True
print(type(is_student)) # 输出: <class 'bool'>

# 2. 通过比较运算产生
# 比如:3 大于 1 吗?
result1 = 3 > 1
print(result1)  # 输出: True

# 比如:10 等于 20 吗?
result2 = 10 == 20
print(result2)  # 输出: False

布尔值的核心:零值转换(Truthiness)

这是 Python 中非常方便的一个特性。几乎所有的 Python 对象都可以被转换成布尔值

在进行逻辑判断(如 if 语句)时,Python 会自动判断一个变量是“真”还是“假”。

判定规则

  • 被视为 False 的情况(零值/空值):

    • False 本身
    • None(空值)
    • 数字 00.0
    • 空序列:空字符串 ""、空列表 []、空元组 ()
    • 空字典 {}、空集合 set()
  • 被视为 True 的情况:

    • 除了上述“空/零”情况以外的所有值。

我们可以使用 bool() 函数来查看一个值的真假状态:

# --- 这些都会被转换为 False ---
print(bool(0))          # False (数字0)
print(bool(0.0))        # False (浮点数0)
print(bool(""))         # False (空字符串)
print(bool([]))         # False (空列表)
print(bool(None))       # False (空对象)

# --- 这些都会被转换为 True ---
print(bool(1))          # True (非0数字)
print(bool(-5))         # True (负数也是非0,所以是True)
print(bool("Hello"))    # True (非空字符串)
print(bool([1, 2]))     # True (非空列表)
print(bool("False"))    # True (注意!这是一个包含了文字的字符串,不是布尔值False)

布尔逻辑运算

有了布尔值,我们通常需要组合使用它们。Python 提供了三个关键字:

  • and (与):两边都为真,结果才为真。
  • or (或):只要有一边为真,结果就为真。
  • not (非):取反,真变假,假变真。
has_ticket = True
is_weekend = False

# AND 运算
if has_ticket and is_weekend:
    print("可以去看电影")
else:
    print("条件不满足")
# 结果:条件不满足(因为 is_weekend 是 False)

# OR 运算
if has_ticket or is_weekend:
    print("至少满足一个条件")
# 结果:至少满足一个条件(因为 has_ticket 是 True)

# NOT 运算
print(not has_ticket) # 输出: False

常见使用场景

布尔类型主要用于控制程序的流程。

场景一:条件判断(利用零值转换)

这是 Python 程序员最喜欢的写法。比如判断一个列表是否有数据,不需要写 if len(my_list) > 0:,直接写变量名即可。

# 接收用户输入的用户名
username = ""  # 假设用户没有输入

# 直接判断字符串变量
# 如果字符串为空,bool(username) 为 False,不执行
# 如果字符串有内容,bool(username) 为 True,执行
if username:
    print(f"欢迎你, {username}")
else:
    print("用户名不能为空!")

# 列表同理
shopping_cart = ["Apple", "Banana"]
if shopping_cart:
    print("准备结算...")
else:
    print("购物车是空的")

场景二:开关标志(Flag)

在循环或复杂逻辑中,用一个布尔变量作为“标志位”来控制状态。

game_running = True

while game_running:
    user_input = input("请输入命令 (输入 'exit' 退出): ")

    if user_input == 'exit':
        print("游戏结束")
        game_running = False  # 修改布尔值,循环将在下一次判断时停止
    else:
        print(f"你执行了: {user_input}")

场景三:函数返回值

很多函数的作用就是为了告诉调用者“是”或“否”。通常这类函数以 is_has_ 开头。

def is_even(number):
    """判断一个数是否是偶数"""
    if number % 2 == 0:
        return True
    else:
        return False

# 使用
num = 10
if is_even(num):
    print(f"{num} 是偶数")

7 Python 基础类型:字符串(String)

作者 木灵鱼儿
2026年1月21日 11:39

前言

在 Python 中,字符串(String) 是最常用的数据类型之一。简单来说,只要是用引号括起来的文本,在 Python 眼里都是字符串。无论是名字、一句话,还是一篇长文章,都可以用字符串来表示。

声明字符串的几种方式

在 Python 中,创建字符串非常灵活。主要有三种方式:使用单引号双引号三引号

① 使用单引号 ' '

这是最简单的形式。

# 使用单引号声明
name = 'Python'
message = 'Hello World'

print(name)
print(message)

② 使用双引号 " "

双引号的作用和单引号完全一样。在 Python 中,单引号和双引号没有区别,你可以根据习惯选择。

# 使用双引号声明
name = "Python"
sentence = "这是一个字符串"

print(name)
print(sentence)

💡 为什么要有两种引号?

既然功能一样,为什么 Python 要同时支持单引号和双引号呢?
主要也是为了方便处理“引号嵌套”的情况。

  • 如果字符串内容里包含了单引号(例如英文缩写 I'm),外面就用双引号包围。
  • 如果字符串内容里包含了双引号,外面就用单引号包围。

这样就不需要使用复杂的转义字符(\)了。

# 场景1:内容里有单引号 (I'm),外面用双引号
text1 = "I'm a programmer."

# 场景2:内容里有双引号 ("Python"),外面用单引号
text2 = '他说:"Python 很有趣"'

print(text1)
print(text2)

三重引号的使用 ''' '''""" """

如果你需要写一段很长的文字,或者文字中包含换行,那么单引号和双引号就不太好用了。这时候,三重引号就派上用场了。

特点:保留格式(换行)

三重引号允许字符串跨越多行,并且会原封不动地保留你输入的格式(包括换行符和空格)。

# 使用三重引号(三个单引号或三个双引号均可)
poem = """
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
"""

print(poem)

输出结果:

床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。

(你会发现输出结果和代码里的排版是一模一样的)

这种方式常用于

  1. 大段文本:如HTML代码片段、SQL语句、长篇文字。
  2. 文档注释:在函数或类的开头,用来解释代码功能的注释。

6 Python 基础数据类型:整型 (int) 与浮点型 (float) 及精度避坑指南

作者 木灵鱼儿
2026年1月21日 11:09

前言

在 Python 编程中,数字是我们最打交道的数据类型。其中,整型 (int)浮点型 (float) 是最基础、最常用的两种。看似简单,但如果不了解底层原理,特别是浮点数的精度问题,很容易在计算金额或科学运算时踩进“深坑”。

整型 (int)

Python 中的整型用于表示整数(没有小数部分的数字),包括正整数、负整数和零。

1. 核心特性:无限精度

与 C/Java 等语言不同,Python 3 的 int 类型没有固定的大小限制(不再区分 intlong)。只要你的内存够大,Python 就能处理任意大的整数。这使得 Python 非常适合进行大数运算。

2. 基础用法与进制表示

除了十进制,Python 还支持二进制、八进制和十六进制的直接输入。

# 1. 基础赋值
a = 10
b = -5
c = 0

# 2. 大数运算(自动处理,不会溢出)
big_num = 2 ** 100
print(f"2的100次方: {big_num}")
# 输出: 1267650600228229401496703205376

# 3. 多进制表示
n_bin = 0b1010  # 二进制 (0b 开头) -> 10
n_oct = 0o12    # 八进制 (0o 开头) -> 10
n_hex = 0xA     # 十六进制 (0x 开头) -> 10

print(f"二进制: {n_bin}, 八进制: {n_oct}, 十六进制: {n_hex}")

浮点型 (float)

浮点型用于表示带小数点的数字,或者使用科学计数法表示的数字。

基础用法

Python 的 float 类型通常对应于 C 语言中的 double(双精度浮点数),遵循 IEEE 754 标准。

# 1. 小数形式
pi = 3.14159
negative = -0.01

# 2. 科学计数法 (e 代表 10 的幂)
large_f = 1.23e9       # 1.23 * 10^9
small_f = 1.5e-3       # 1.5 * 10^-3 (0.0015)

print(f"科学计数法: {large_f}")
print(type(pi)) # <class 'float'>

高能预警:浮点数的精度问题

新手在 Python 中最容易遇到的“灵异事件”莫过于此:

print(0.1 + 0.2)
# 你以为会输出: 0.3
# 实际输出: 0.30000000000000004

为什么会这样?

这不是 Python 的 bug,而是计算机底层二进制存储机制决定的。

  • 十进制中,我们用 $1/10$ 表示 0.1。
  • 二进制中,计算机需要用 $1/2, 1/4, 1/8...$ 来拼凑数字。
  • 遗憾的是,0.1 在二进制中是一个无限循环小数 (0.0001100110011...)。

由于计算机内存(64位)是有限的,它不得不截断这个无限循环小数。截断就意味着精度的丢失。当你把两个本身就存在微小误差的数相加时,误差被放大,最终显示出来。

解决精度问题的 4 种方案

在涉及金融计算科学实验等对精度要求极高的场景下,直接使用 float 是不安全的。以下是常见的解决方案:

方案 1:用于展示 —— formatround (不推荐用于计算)

如果你只是想把结果打印得好看一点,可以使用格式化字符串或 round()

  • 注意round() 在 Python 3 中采用“银行家舍入法”(四舍六入五取偶),且 round() 本身仍返回 float,可能依然带有微小误差。
a = 0.1 + 0.2

# 方法 A: 字符串格式化 (推荐用于展示)
print(f"{a:.2f}")  # 输出: 0.30

# 方法 B: round 函数
print(round(a, 2)) # 输出: 0.3

round函数还是比较简单的,函数第二个参数表示保留几位小数。

f这个字符串格式化稍微复杂一点,它的语法结构如下:

f"{value:[width][.precision][type]}"
  • value:要格式化的数值
  • width:最小总宽度(可省略)
  • .precision:小数点后位数(对 f 类型有效)
  • type:常用有 f(固定小数)、e(科学计数)、g(自动选择)
💡 示例:{a:.2f} 表示“以固定小数形式显示,保留2位小数,不足位数会自动补0”。

方案 2:用于比较 —— math.isclose

永远不要使用 == 来判断两个浮点数是否相等。应该判断它们是否“足够接近”。

import math

a = 0.1 + 0.2
b = 0.3

# 错误做法
if a == b:
    print("相等")
else:
    print("不相等") # 这里会被执行

# 正确做法:使用 math.isclose
# rel_tol 是相对误差,abs_tol 是绝对误差
if math.isclose(a, b, rel_tol=1e-9):
    print("数学上相等") # 这里会被执行

为什么rel_tol是1e-9?

rel_tol=1e-9(即 0.000000001)并不是随意选的,而是基于浮点数精度的工程经验和IEEE 754 双精度浮点数(float64)的特性得出的一个合理默认值。NumPy、SciPy 等科学计算库也常用 1e-91e-8 作为默认容差。Python 官方文档明确说明:1e-9 是“适用于大多数情况的合理默认值”。

什么时候使用abs_tol?

abs_tol 适用于你关心的数值范围非常小(接近零)的情况。比如:比较两个接近0的数。

import math

a = 1e-12
b = 0.0

# 仅用 rel_tol 会失败(因为 max(|a|,|b|) ≈ 0,rel_tol * 0 = 0)
print(math.isclose(a, b, rel_tol=1e-9))        # False ❌

# 必须使用 abs_tol
print(math.isclose(a, b, abs_tol=1e-11))       # True ✅

新手也不用想那么多,以后遇到了自然再了解即可。

方案 3:金融级精确计算 —— decimal 模块 (推荐)

Python 标准库提供了 decimal.Decimal 类,用于十进制的精确运算。这是解决金额计算问题的标准答案。

关键点:创建 Decimal 对象时,必须传入字符串!

from decimal import Decimal

# 错误示范:传入 float,精度已经丢了
d_wrong = Decimal(0.1)
print(f"错误创建: {d_wrong}")
# 输出: 0.100000000000000005551115123125...

# 正确示范:传入字符串
d1 = Decimal('0.1')
d2 = Decimal('0.2')
d3 = d1 + d2

print(f"正确结果: {d3}")       # 输出: 0.3
print(f"类型: {type(d3)}")    # <class 'decimal.Decimal'>

# Decimal 可以方便地进行四舍五入
# 设置精度保留2位小数,ROUND_HALF_UP 是标准的四舍五入
from decimal import ROUND_HALF_UP
price = Decimal('1.125')
print(price.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)) # 1.13

方案 4:分数运算 —— fractions 模块

如果你需要处理 $1/3$ 这种在十进制和二进制都是无限循环的数,可以使用分数模块。

from fractions import Fraction

f1 = Fraction(1, 3)  # 1/3
f2 = Fraction(1, 6)  # 1/6

result = f1 + f2
print(f"分数计算结果: {result}") # 输出: 1/2
print(float(result))            # 输出: 0.5

总结

  1. 整型 (int):在 Python 3 中拥有无限精度,大胆使用,不用担心溢出。
  2. 浮点型 (float):基于 IEEE 754 标准,运算速度快,但存在二进制存储带来的精度误差
  3. 避坑指南

    • 比较:永远不要用 == 判断浮点数,请使用 math.isclose()
    • 计算:涉及到钱或高精度需求,请务必使用 decimal 模块,并且记得传字符串给 Decimal。

5 Python的变量与内存模型

作者 木灵鱼儿
2026年1月21日 10:11

什么是变量?(便利贴 vs 盒子)

在传统的静态语言(如 C 语言)中,变量通常被比喻为一个盒子:你申请一个盒子,给它贴上名字,然后把数据放进去。

但在 Python 中,变量更像是一个便利贴(标签)

  • 对象(Object):是内存中实实在在存储数据的地方(比如数字 10,字符串 "Hello")。
  • 变量(Variable):只是一个名字,你把这个名字“贴”到了某个对象上。

核心概念: 变量本身不存储数据,它只是指向了存储数据的内存地址。

变量的基本用法

Python 是动态类型语言,这意味着你不需要显式声明变量的类型(如 int a),直接赋值即可使用。

基础赋值

# 将 "score" 这个标签贴在数字 100 上
score = 100

# 将 "username" 这个标签贴在字符串 "Alice" 上
username = "Alice"

print(score)      # 输出: 100
print(username)   # 输出: Alice

动态类型特性

同一个变量名可以随时被贴到不同类型的对象上:

var = 123       # var 现在指向整数
print(var)

var = "Python"  # var 现在指向字符串(类型变了,但在Python中是合法的)
print(var)

深入理解:内存引用与 id() 函数

这是理解 Python 变量逻辑最关键的一步。为了验证“变量是引用”这一说法,我们需要使用 Python 的内置函数 id()

  • id(object):返回对象的“身份”,在 CPython 实现中,这通常就是对象在内存中的地址
  • Python 是一种编程语言,它是一套语法规范和语义规则,但是它本身不能直接运行代码。为了执行 Python 代码,我们需要一个解释器(Interpreter)。CPython 是最常用的 Python 解释器的实现,它使用 C 语言编写,并且在 CPython 中,id() 函数返回的是对象在内存中的地址。
  • CPython 是 Python 语言的官方参考实现,由 Python 创始人 Guido van Rossum 主导开发,用 C 语言 编写,因此得名 C + Python = CPython。当你从 python.org 下载并安装 Python 时,你安装的就是 CPython。

示例:查看变量的内存地址

x = 10
print(f"x的值: {x}, x的内存地址: {id(x)}")

# 让我们看看发生了什么
y = 10
print(f"y的值: {y}, y的内存地址: {id(y)}")

运行结果(示例):

x的值: 10, x的内存地址: 4304675344
y的值: 10, y的内存地址: 4304675344

分析:
你会发现 xyid 是一样的!

  • Python 内存中创建了一个整数对象 10
  • x = 10:把标签 x 贴在对象 10 上。
  • y = 10:Python 检测到内存中已经有小整数 10 了(由于 Python 的小整数缓存机制),它直接把标签 y 也贴到了同一个对象 10 上。
  • 结论: 变量 xy 指向了同一个内存块。

示例:重新赋值的真相

如果我们修改 x 的值,会发生什么?

x = 10
print(f"初始 x 地址: {id(x)}")

x = 20
print(f"修改后 x 地址: {id(x)}")

分析:
你会发现地址变了

  • 不是把内存中 10 改成了 20
  • 而是在内存中新建(或找到)了一个对象 20,然后把标签 x10 身上撕下来,贴到了 20 身上。
  • 原来的对象 10 如果没有其他变量引用它,稍后会被垃圾回收机制回收。

变量的传递(引用传递)

理解了“便利贴”模型,变量之间的赋值逻辑就很好理解了。

场景一:不可变对象(数字、字符串、元组)

a = 100
b = a  # 把 b 贴到 a 当前指向的对象上

print(f"a的地址: {id(a)}")
print(f"b的地址: {id(b)}") # 地址相同

a = 200 # a 换到了新对象 200 上,b 仍然贴在 100 上
print(f"a的值: {a}, b的值: {b}")

结果: b 依然是 100。因为改变 a 只是移动了 a 这个标签,不影响 b 这个标签。

场景二:可变对象(列表、字典)—— 新手最大的坑

对于列表这种“可变对象”,情况会变得有趣:

list_1 = [1, 2, 3]
list_2 = list_1  # list_2 和 list_1 指向同一个列表对象

print(f"list_1 地址: {id(list_1)}")
print(f"list_2 地址: {id(list_2)}") # 地址完全一样

# 修改 list_1 的内容
list_1.append(4)

print(f"list_1: {list_1}")
print(f"list_2: {list_2}") # 注意:list_2 也变了!

分析:

  • list_1list_2 贴在同一个“箱子”(列表)上。
  • list_1.append(4)往箱子里扔了一个新数据,并没有换箱子(地址没变)。
  • 所以,通过 list_2 往里看,也能看到新增加的数据。

变量表达式赋值

Python 提供了非常优雅的赋值方式,可以让代码更简洁。

1. 链式赋值

将同一个值赋给多个变量:

a = b = c = 0
print(a, b, c)  # 输出: 0 0 0

注意:如果是可变对象(如列表),它们都会指向同一个对象,修改其中一个会影响全部。

2. 解包赋值 (Unpacking)

这是 Python 的杀手级特性,可以同时给多个变量赋值:

# 对应位置赋值
name, age = "Bob", 25
print(name)  # Bob
print(age)   # 25

# 交换变量值(无需中间变量)
x = 5
y = 10
x, y = y, x
print(x, y)  # 输出: 10 5

3. 星号解包

当变量数量和元素数量不匹配时,可以使用 * 收集剩余元素:

data = [1, 2, 3, 4, 5]
head, *middle, tail = data

print(head)   # 1
print(middle) # [2, 3, 4]  <- 变成了一个列表
print(tail)   # 5

变量的命名规范

编写 Python 代码时,遵守 PEP 8(Python 官方编码风格指南)是非常重要的。

命名规则(强制)

  1. 只能包含字母、数字和下划线 (_)。
  2. 不能以数字开头
  3. 区分大小写 (Ageage 是两个变量)。
  4. 不能使用 Python 的关键字(如 if, class, def, return 等)。

命名风格(约定俗成)

  • 普通变量/函数名:使用 Snake Case(蛇形命名法),即全小写,单词间用下划线连接。

    • user_name, total_score, is_active
    • userName (Java风格), UserName, u_n
  • 常量:虽然 Python 没有真正的常量,但约定全大写代表常量。

    • PI, MAX_CONNECTIONS, DEFAULT_COLOR
  • 类名:使用 Camel Case(大驼峰命名法)

    • UserProfile, CarEngine
  • 私有变量(惯例):以单下划线开头,表示不希望被外部访问。

    • _internal_var

代码示例

# 好的命名
student_count = 50
max_score = 100

# 不好的命名
a = 50          # 意义不明
StudentCount = 50 # 像类名,容易混淆
❌
❌