变量与数据类型:数字、字符串、布尔、空值
一句话概述
变量是存放数据的"标签盒子",数据类型则决定了盒子里面能放什么东西以及能对它做什么操作。Python 有四大基础数据类型:数字(int/float)用于数学计算,字符串(str)用于处理文本,布尔(bool)用于逻辑判断,空值(None)用于表示"没有"的状态。Python 的最大特色是动态类型——变量不用事先声明类型,赋值的那一刻自动确定类型,这让代码写起来像说话一样自然。理解这些基础数据类型是学习 Python 的必经之门,也是后续操作列表、字典、文件、AI 数据处理的根基。
💡 核心要点:①变量是数据的"标签",通过赋值语句创建,动态确定类型 ②数字类型分整数 int 和浮点数 float,注意除法 / 始终返回 float ③字符串是不可变的字符序列,支持索引、切片、格式化 ④布尔类型只有 True 和 False,是条件判断和循环控制的基石 ⑤None 是 Python 的"空",不等于 0、空字符串或 False
教学与演示
一、变量——给数据贴上标签
是什么:变量是内存中存储数据的一块空间的"名字标签"。在 Python 中,你不需要像 C/Java 那样先声明变量的类型——直接写 x = 10,Python 就会在内存中创建整数 10,然后把 x 这个标签贴上去。之后通过 x 这个名字,你就能访问和操作这块数据。
大白话 变量就像快递柜的"取件码"——数据是放在柜子里的快递,变量名就是取件码。你不需要知道快递在哪个柜子(内存地址),只要记住取件码(变量名)就能取到快递。而且 Python 的柜子是智能的——你放什么(数字、文字、列表),它自动知道是什么类型,不用你事先说明。
为什么:变量让程序有了"记忆"能力。没有变量,程序就像金鱼——每次操作完就忘了之前的结果。有了变量,你可以保存中间结果、累计数据、传递参数。Python 的动态类型设计让变量使用极其方便——你可以先让 x = 10,再让 x = "hello",Python 完全接受这种"变脸"操作,这在静态类型语言中是不允许的。
大白话 想象没有变量的世界:你得把每一步的计算结果抄在纸上,而且每一步都得手工传下去——这和没有"粘贴"功能的电脑有什么区别?变量就是自动帮你"粘贴"上次结果的工具。
怎么做:
import numpy as np
# ====== 1. 变量的创建(赋值) ======
# Python 的变量不需要声明类型,赋值即创建
name = "Alice" # 字符串变量:存储姓名
age = 25 # 整数变量:存储年龄
height = 1.68 # 浮点数变量:存储身高(米)
is_student = True # 布尔变量:存储学生身份标识
print("姓名:", name) # 输出: Alice
print("年龄:", age) # 输出: 25
print("身高:", height, "米") # 输出: 1.68 米
print("是否学生:", is_student) # 输出: True
# ====== 2. 变量名命名规则 ======
# 合法的变量名:字母/下划线开头,可含数字
my_var = 100 # ✅ 下划线分隔(推荐 snake_case)
myVar = 200 # ✅ 驼峰命名(也合法)
_private = 300 # ✅ 下划线开头(约定为私有变量)
var2 = 400 # ✅ 包含数字
中文变量 = 500 # ✅ Python 3 支持中文变量名(但不推荐)
# 不合法的变量名(注释掉以免报错):
# 2var = 100 ❌ 不能以数字开头
# my-var = 100 ❌ 不能包含连字符
# if = 100 ❌ 不能使用关键字
# ====== 3. 变量的动态类型特性 ======
x = 10 # x 现在是整数 int
print("x =", x, "→ 类型:", type(x).__name__) # int
x = "hello" # x 变成了字符串 str
print("x =", x, "→ 类型:", type(x).__name__) # str
x = 3.14 # x 又变成了浮点数 float
print("x =", x, "→ 类型:", type(x).__name__) # float
# Python 完全接受这种"变脸"!在 Java/C++ 中这是不允许的。
# ====== 4. 多重赋值(解包赋值) ======
# 同时给多个变量赋值
a, b, c = 1, 2, 3 # a=1, b=2, c=3
print(f"a={a}, b={b}, c={c}") # f-string 格式化输出
# 交换两个变量的值(Python 特色,不需要临时变量!)
a, b = b, a # a 和 b 交换值
print(f"交换后: a={a}, b={b}") # a=2, b=1
# ====== 5. 变量的"引用"本质(id 函数) ======
# Python 中的变量是对象的"引用"(标签)
x = 100
y = x # y 也指向同一个 100 对象
print("x 的 id:", id(x)) # 内存地址(每次运行可能不同)
print("y 的 id:", id(y)) # 与 x 相同!
print("x is y:", x is y) # True — 指向同一对象
# 但对于不可变类型(如数字),修改会创建新对象
x = x + 1 # 创建新的 101 对象,x 指向它
print("x 的 id:", id(x)) # 变了!
print("y 的 id:", id(y)) # 没变!y 还指向 100
print("x =", x, ", y =", y) # x=101, y=100
# ====== 6. 用 type() 检查变量类型 ======
# type() 是最常用的类型检查函数
print("\n=== 类型检查 ===")
print("type(42):", type(42)) # <class 'int'>
print("type(3.14):", type(3.14)) # <class 'float'>
print("type('hello'):", type('hello')) # <class 'str'>
print("type(True):", type(True)) # <class 'bool'>
print("type(None):", type(None)) # <class 'NoneType'>
print("type([1,2,3]):", type([1,2,3])) # <class 'list'>什么用:在 AI 中,变量无处不在。训练模型时,你会把数据存储在变量中(X_train, y_train),把模型参数放在变量中(weights, bias),把超参数写在变量中(learning_rate = 0.001)。动态类型让 AI 开发中常见的"先处理数据、再转成数组、最后喂给模型"的流程非常顺畅——相同的变量名可以在不同阶段指向不同类型的对象。
二、数字类型——整数与浮点数
是什么:Python 的数字类型主要分两种:int(整数)和 float(浮点数,即带小数点的数)。int 可以表示任意大小的整数(Python 3 的整数没有大小上限),float 是双精度浮点数(约 15-17 位有效数字,遵循 IEEE 754 标准)。此外还有 complex(复数),但在基础阶段用得较少。
大白话 整数就是"1、2、3"这种不带小数点的数——Python 的整数很厉害,不像某些语言最多只能到 21 亿,Python 的整数可以无限大(只要你内存够)。浮点数就是"3.14、-0.001、1.5e10"这种带小数点的数——但它们有个"小毛病":有些小数没法精确表示(比如 0.1+0.2≠0.3),这是所有计算机的通病,不是 Python 的锅。
为什么:数字是一切计算的基础。AI 本质上是数字游戏——模型的参数是数字(权重和偏置),输入数据经过层层数字运算变成输出预测。理解数字类型的行为,尤其是浮点数的精度问题,能帮你避免很多隐蔽的 bug——比如判断两个浮点数是否相等时,永远不要用 ==,而要用 abs(a - b) < 1e-8 这种近似比较。
大白话 整数就是"一个个数出来的"——3 个苹果、5 个人、100 行数据。浮点数就是"量出来的"——身高 1.68 米、温度 36.5 度、模型准确率 0.987。AI 中浮点数用得最多,因为权重、梯度、损失都是连续的小数。
怎么做:
import numpy as np
# ====== 1. 整数 int 的基本操作 ======
# 整数的四则运算
a = 17 # 被除数
b = 5 # 除数
print("=== 整数运算 ===")
print("17 + 5 =", a + b) # 22 — 加法
print("17 - 5 =", a - b) # 12 — 减法
print("17 * 5 =", a * b) # 85 — 乘法
print("17 / 5 =", a / b) # 3.4 — 真除法(注意:结果是 float!)
print("17 // 5 =", a // b) # 3 — 整除(向下取整,扔掉小数部分)
print("17 % 5 =", a % b) # 2 — 取余(17=5×3+2)
print("17 ** 5 =", a ** b) # 1419857 — 幂运算(17的5次方)
# Python 3 的整数没有大小限制
big_num = 2 ** 1000 # 2的1000次方,约301位数
print("\n2的1000次方有多少位?", len(str(big_num))) # 302位!
# ====== 2. 浮点数 float 的基本操作 ======
# 浮点数的四则运算与整数类似
x = 3.14
y = 2.0
print("\n=== 浮点数运算 ===")
print("3.14 + 2.0 =", x + y) # 5.14
print("3.14 * 2.0 =", x * y) # 6.28
print("3.14 / 2.0 =", x / y) # 1.57
# ====== 3. 浮点数的"精度陷阱"(重要!) ======
print("\n=== 浮点数精度问题 ===")
print("0.1 + 0.2 =", 0.1 + 0.2) # 输出: 0.30000000000000004 不是0.3!
# 原因:0.1 和 0.2 在二进制中是无限循环小数,不能精确存储
# 正确的浮点数比较方式
a = 0.1 + 0.2
b = 0.3
print("a == b 直接用 == :", a == b) # False!
print("a ≈ b 用近似比较:", abs(a - b) < 1e-10) # True!
# 永远用近似比较判断浮点数是否相等!
# ====== 4. 科学计数法 ======
# 极大或极小的数用科学计数法表示
avogadro = 6.022e23 # 6.022 × 10²³(阿伏伽德罗常数)
electron_mass = 9.109e-31 # 9.109 × 10⁻³¹(电子质量)
print("\n=== 科学计数法 ===")
print("6.022e23 =", avogadro) # 6.022e+23
print("9.109e-31 =", electron_mass) # 9.109e-31
# ====== 5. 类型转换 ======
# 字符串转数字
s_num = "42"
s_float = "3.14"
print("\n=== 类型转换 ===")
print("字符串 '42' → 整数:", int(s_num)) # 42
print("字符串 '3.14' → 浮点数:", float(s_float)) # 3.14
# 浮点数转整数(截断,不是四舍五入!)
print("int(3.14) =", int(3.14)) # 3 — 直接砍掉小数部分
print("int(-3.14) =", int(-3.14)) # -3 — 向零取整
print("int(3.99) =", int(3.99)) # 3 — 不是四舍五入!
# round() 用于真正的四舍五入
print("round(3.14) =", round(3.14)) # 3
print("round(3.5) =", round(3.5)) # 4
print("round(3.99) =", round(3.99)) # 4
# ====== 6. 数学函数(使用 math 模块) ======
import math
print("\n=== 数学函数 ===")
print("abs(-10) =", abs(-10)) # 10 — 绝对值
print("max(1, 5, 3, 9) =", max(1, 5, 3, 9)) # 9 — 最大值
print("min(1, 5, 3, 9) =", min(1, 5, 3, 9)) # 1 — 最小值
print("pow(2, 10) =", pow(2, 10)) # 1024 — 2的10次方(同2**10)
print("math.sqrt(16) =", math.sqrt(16)) # 4.0 — 平方根
print("math.log(100) =", math.log(100)) # 4.605... — 自然对数
print("math.log10(100) =", math.log10(100)) # 2.0 — 以10为底的对数
print("math.sin(math.pi/2) =", math.sin(math.pi/2)) # 1.0 — 正弦
print("math.pi =", math.pi) # 3.14159...什么用:在 AI 中,数字运算无处不在。损失函数的值是浮点数,梯度是浮点数,权重初始化的值是随机浮点数,批量大小(batch_size)是整数,训练轮数(epochs)是整数。浮点数精度问题在 AI 中尤为关键——TensorFlow 和 PyTorch 默认使用 float32(单精度),比 Python 默认的 float64 精度更低,但计算更快。理解 0.1 + 0.2 ≠ 0.3 的原理,能让你在调试梯度异常、NaN 等问题时多一份警觉。
三、字符串——文本的容器
是什么:字符串(str)是 Python 中表示文本的数据类型,由一系列 Unicode 字符组成。用单引号 '...'、双引号 "..." 或三引号 '''...''' / """...""" 包裹。字符串是不可变的——一旦创建,不能修改其中的某个字符,只能创建新字符串。
大白话 字符串就是"一段文字"。Python 三种引号各有用途:单引号和双引号没区别(爱用哪个用哪个),三引号可以写多行文字(比如一大段描述)。字符串的"不可变"意味着你不能像改列表那样改字符串的某个字符——比如 s[0] = 'X' 会报错——但这反而是个好事,因为它让字符串更安全、更容易被缓存和优化。
为什么:字符串是程序和用户沟通的桥梁——print 输出靠字符串,input 输入返回字符串,文件读写是字符串,API 返回的 JSON 数据也是字符串。在 AI 中,文本处理(NLP)的第一步就是把原始文本变成字符串,然后分词(tokenize)、编码成数字向量。不理解字符串的基本操作,就无法处理任何文本数据。
大白话 你不会对着计算机说二进制,计算机也不会直接理解你写的文章——字符串就是你们之间的"翻译层"。你用字符串写提示词(prompt),模型用字符串返回回答。NLP 模型读进来的是字符串("今天天气真好"),吐出去的也是字符串("正面情感")。
怎么做:
import numpy as np
# ====== 1. 字符串的创建方式 ======
s1 = 'hello' # 单引号
s2 = "world" # 双引号(效果一样)
s3 = '''这是一段
多行文字''' # 三引号保留换行
s4 = """也可以写多行""" # 三双引号
print("单引号:", s1) # hello
print("双引号:", s2) # world
print("三引号多行:", s3) # 两行文字
print("三双引号:", s4)
# ====== 2. 字符串的索引(下标访问) ======
text = "Python编程"
# 索引: 0123456789... 正索引从0开始
# ...-4-3-2-1 负索引从-1开始(倒数第一个)
print("\n=== 字符串索引 ===")
print("text =", repr(text)) # 'Python编程'(repr显示原始格式)
print("text[0] =", text[0]) # 'P' — 第1个字符
print("text[1] =", text[1]) # 'y' — 第2个字符
print("text[-1] =", text[-1]) # '程' — 倒数第1个字符
print("text[-2] =", text[-2]) # '编' — 倒数第2个字符
# ====== 3. 字符串切片(提取子串) ======
# 语法:text[start:end:step]
# start包含,end不包含(左闭右开)
print("\n=== 字符串切片 ===")
print("text[0:6] =", text[0:6]) # 'Python' — 索引0到5(不含6)
print("text[:6] =", text[:6]) # 'Python' — 从开头到索引5
print("text[6:] =", text[6:]) # '编程' — 从索引6到末尾
print("text[:] =", text[:]) # 'Python编程' — 整个副本
print("text[::2] =", text[::2]) # 'Pto编' — 每隔一个取字符
print("text[::-1] =", text[::-1]) # '程编nohtyP' — 反转字符串!
# ====== 4. 字符串的常用方法 ======
s = " Hello, World! "
print("\n=== 字符串常用方法 ===")
print("原始:", repr(s)) # ' Hello, World! '
# 大小写转换
print("upper():", s.upper()) # ' HELLO, WORLD! ' — 全大写
print("lower():", s.lower()) # ' hello, world! ' — 全小写
print("title():", s.title()) # ' Hello, World! ' — 首字母大写
# 去除空白
print("strip():", repr(s.strip())) # 'Hello, World!' — 去掉两端空白
print("lstrip():", repr(s.lstrip())) # 'Hello, World! ' — 去掉左端空白
print("rstrip():", repr(s.rstrip())) # ' Hello, World!' — 去掉右端空白
# 查找与替换
print("find('World'):", s.find('World')) # 9 — 子串首次出现的位置(从0开始)
print("find('abc'):", s.find('abc')) # -1 — 没找到返回-1
print("replace('World', 'Python'):", s.replace('World', 'Python').strip())
# 'Hello, Python!'
# 判断
print("'123'.isdigit():", '123'.isdigit()) # True — 全是数字?
print("'abc'.isalpha():", 'abc'.isalpha()) # True — 全是字母?
print("'abc123'.isalnum():", 'abc123'.isalnum())# True — 全是字母或数字?
# 拆分与连接(非常常用!)
print("\n=== 拆分与连接 ===")
csv_line = "苹果,香蕉,橘子,西瓜"
fruits = csv_line.split(',') # 按逗号分割成列表
print("split(','):", fruits) # ['苹果', '香蕉', '橘子', '西瓜']
joined = ' | '.join(fruits) # 用竖线把水果连起来
print("' | '.join():", joined) # '苹果 | 香蕉 | 橘子 | 西瓜'
# ====== 5. 字符串格式化(3种方式) ======
name = "小明"
age = 18
score = 95.5
print("\n=== 字符串格式化 ===")
# 方式1:f-string(Python 3.6+,最推荐!)
print(f"{name}今年{age}岁,考了{score}分") # 小明今年18岁,考了95.5分
print(f"百分比:{score/100:.1%}") # 百分比:95.5%(: .1%格式化)
# 方式2:format() 方法
print("{}今年{}岁,考了{}分".format(name, age, score))
# 方式3:% 格式化(老式,不推荐但常见于老代码)
print("%s今年%d岁,考了%.1f分" % (name, age, score))
# f-string 中格式控制
pi = 3.1415926535
print(f"π ≈ {pi:.2f}") # 3.14 — 保留2位小数
print(f"π ≈ {pi:.4f}") # 3.1416 — 保留4位小数
print(f"{name:*^20}") # *******小明******* — 居中,*填充
# ====== 6. 字符串是不可变的 ======
# 以下操作会报错(已注释):
# s[0] = 'h' ❌ TypeError: 'str' object does not support item assignment
# 想要修改字符串,只能创建新字符串
s = "hello"
s = 'H' + s[1:] # 创建新的字符串 'Hello'
print("\n修改后的字符串:", s) # Hello什么用:在 AI 的 NLP 领域,字符串是一切文本数据的基础形式。分词(tokenization)前你需要用 .strip() 清理文本、用 .lower() 统一大小写、用 .split() 分割句子;数据预处理中,CSV 文件的每一行是字符串、API 返回的 JSON 是字符串;模型推理后生成的结果也是字符串。另外,f-string 在 AI 开发中极其常用——打印训练日志(print(f"Epoch {epoch}, Loss: {loss:.4f}"))、格式化输出预测结果,都离不开它。
四、布尔类型与 None——判断与空
是什么:布尔类型(bool)只有两个值:True(真)和 False(假),首字母必须大写。它是条件判断(if)、循环控制(while)和逻辑运算的核心。None 是 Python 中表示"没有"或"空"的特殊常量,类型是 NoneType,常用于初始化变量、表示函数无返回值、或表示"数据缺失"。
大白话True和False就是"是"和"否"——就像开关的"开"和"关"。None是一个特殊的"占位符"——比如你有一个变量但还不知道该放什么值,就用None先占个座;或者函数不需要返回任何东西时,Python 自动返回None。注意None不是 0、不是 False、不是空字符串——它就是它自己,测量"什么都没有"。
为什么:布尔值让程序有了"判断力"——程序不是机械地从头执行到尾,而是能根据不同条件走不同的分支。这在 AI 中至关重要:判断模型是否收敛(if loss < threshold: break)、检查数据是否合法(if value is None: skip)、条件数据增强(if random.random() > 0.5: flip_image())。
大白话 没有布尔类型,程序就是一条"直路"——永远从头跑到尾,没法拐弯。有了布尔类型,程序变成了"有岔路的地图"——看到红灯(False)往左拐,看到绿灯(True)往右拐。AI 中的决策树、if-else 逻辑都依赖它。
怎么做:
import numpy as np
# ====== 1. 布尔值的基本操作 ======
print("=== 布尔值 ===")
is_sunny = True # 布尔变量:今天是晴天
is_raining = False # 布尔变量:今天不下雨
print("is_sunny =", is_sunny) # True
print("is_raining =", is_raining) # False
print("type(True):", type(True)) # <class 'bool'>
# 布尔值可以参与算术运算(True=1, False=0)
print("True + True =", True + True) # 2
print("True * 5 =", True * 5) # 5
print("False * 100 =", False * 100) # 0
# ====== 2. 比较运算符(产生布尔值) ======
print("\n=== 比较运算 ===")
a, b = 10, 3
print(f"{a} == {b}:", a == b) # False — 等于
print(f"{a} != {b}:", a != b) # True — 不等于
print(f"{a} > {b}:", a > b) # True — 大于
print(f"{a} < {b}:", a < b) # False — 小于
print(f"{a} >= {b}:", a >= b) # True — 大于等于
print(f"{a} <= {b}:", a <= b) # False — 小于等于
# 字符串也可以比较(按字典序/Unicode 码点)
print("'apple' < 'banana':", 'apple' < 'banana') # True(a < b)
# ====== 3. 逻辑运算符(and, or, not) ======
print("\n=== 逻辑运算 ===")
x, y = True, False
print("True and False:", x and y) # False — 与:全真才真
print("True or False:", x or y) # True — 或:一真即真
print("not True:", not x) # False — 非:取反
print("not False:", not y) # True
# 短路求值(short-circuit evaluation)
# and:左边为 False 就不看右边了
# or:左边为 True 就不看右边了
def side_effect():
"""带副作用的函数(实际开发中可能打印日志等)"""
print(" -> 第二个操作数被求值了!")
return True
print("\n=== 短路求值演示 ===")
print("False and ...: ", False and side_effect()) # 不打印"被求值"
print("True or ...: ", True or side_effect()) # 不打印"被求值"
print("True and ...: ", True and side_effect()) # 打印!
print("False or ...: ", False or side_effect()) # 打印!
# ====== 4. 真值测试(Truthy 和 Falsy) ======
# Python 中,任何对象都可以用在布尔上下文中
# 以下值被认为是 False(Falsy):
falsy_values = [False, 0, 0.0, '', "", None, [], {}, set()]
print("\n=== Falsy 值测试 ===")
for val in falsy_values:
result = "Falsy ❌" if not val else "Truthy ✅"
print(f" bool({repr(val):15s}) = {bool(val)} → {result}")
# 其他所有值都被认为是 True(Truthy)
truthy_values = [True, 1, -1, 3.14, 'hello', [0], {0: 0}]
print("\n=== Truthy 值测试 ===")
for val in truthy_values:
result = "Falsy ❌" if not val else "Truthy ✅"
print(f" bool({repr(val):15s}) = {bool(val)} → {result}")
# ====== 5. None 的使用 ======
print("\n=== None(空值) ===")
nothing = None
print("nothing =", nothing) # None
print("type(None):", type(None)) # <class 'NoneType'>
print("None == False:", None == False) # False — None 不等于 False
print("None == 0:", None == 0) # False — None 不等于 0
print("None is None:", None is None) # True — 用 is 判断是否为 None
# None 的常见用途
def maybe_return(flag):
"""模拟可能不返回有用值的函数"""
if flag:
return "有用的数据"
# 没有 return 时,函数默认返回 None
result1 = maybe_return(True)
result2 = maybe_return(False)
print(f"有返回值: {result1}") # '有用的数据'
print(f"无返回值: {result2}") # None
# 判断变量是否为 None(用 is,不要用 ==!)
if result2 is None:
print("result2 是 None,说明函数没有返回有效值")
# 使用 None 作为默认参数(常见的 AI 代码模式)
def train_model(data, epochs=10, callback=None):
"""模拟训练模型,callback 是可选的回调函数"""
loss = 0.0
for epoch in range(epochs):
loss = 1.0 / (epoch + 1) # 模拟损失下降
if callback is not None: # 如果传入了回调,就调用它
callback(epoch, loss)
return loss
# 不传 callback(默认 None)
train_model(None, epochs=3)
# 传入一个回调函数
def my_logger(epoch, loss):
print(f" [回调] Epoch {epoch}: loss = {loss:.3f}")
print("训练过程(有回调):")
train_model(None, epochs=3, callback=my_logger)什么用:在 AI 中,布尔值的应用贯穿始终。数据预处理中判断缺失值(pd.isna() 返回布尔值)、数据过滤(df[df['score'] > 80])、条件数据增强;模型训练中判断是否早停(if patience_count > threshold: break);推理中的阈值判断(if confidence > 0.5: predict = 'cat')。None 则常用于表示可选参数(如 model.eval() 和 model.train() 的切换)、处理缺失数据(用 None 标记缺失值)、以及作为"未设置"状态的标记。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| 变量 | 数据的命名标签 | 存储数据、模型参数、中间结果 | 赋值、引用、作用域 |
| int | 任意精度的整数 | 批大小、epoch数、索引 | float、bool |
| float | 双精度浮点数 | 权重、梯度、损失值 | int、NaN、inf |
| str | 不可变Unicode字符序列 | 文本数据处理、NLP输入 | 索引、切片、格式化 |
| bool | True/False逻辑值 | 条件判断、早停、数据过滤 | 比较运算符、逻辑运算符 |
| None | 表示"空"或"缺失" | 可选参数、缺失值标记 | is运算符、NoneType |
| 动态类型 | 变量类型运行时确定 | 灵活的数据处理管道 | 类型提示、鸭子类型 |
| f-string | 字符串格式化语法 | 训练日志、结果输出 | format()、%格式化 |
| 不可变性 | 创建后不可修改 | 字符串哈希、字典键 | tuple、frozenset |
| 真值测试 | 对象在布尔上下文的真假 | 数据过滤、条件判断 | Falsy、Truthy |
重点答疑
Q1: Python 的变量和 C/Java 的变量有什么本质区别?
Python 变量是"标签"(引用),而 C/Java 变量是"盒子"。当你写 a = 10 时,Java 在内存中分配一个 int 大小的空间放 10,a 就是那个空间;而 Python 在内存中创建一个整数对象 10,a 只是一个指向它的引用。这解释了为什么 Python 可以 a = 10 然后立刻 a = "hello"——你只是把标签 a 从一个对象(10)撕下来贴到另一个对象("hello")上。这种设计让 Python 非常灵活,但代价是每个变量访问都需要一次"间接引用",比 C 的变量稍慢。
Q2: 为什么 0.1 + 0.2 不等于 0.3?AI 中怎么处理?
这是 IEEE 754 浮点数标准的固有限制。十进制的小数(如 0.1)在二进制中是无限循环小数(就像十进制的 1/3 = 0.333...),计算机只能用有限位数存储,因此产生了舍入误差。在 AI 中,这种误差通常可以忽略(float32 的误差约 1e-7 量级),但在某些情况下会累积放大。处理方法:①对比浮点数用 np.isclose(a, b, rtol=1e-5) 而非 ==;②损失函数计算时 PyTorch/TensorFlow 内部已经处理了数值稳定性;③如果精度极重要(如金融计算),改用 decimal.Decimal 或整数(以分为单位)。
Q3: 字符串的"不可变"是什么意思?为什么设计成这样?
不可变意味着一旦字符串创建,你不能修改它的某个字符——s = "hello"; s[0] = 'H' 会报 TypeError。但你可以创建新字符串:s = 'H' + s[1:]。设计成不可变的好处:①安全——字符串可以作为字典的键(dict 要求键不可变);②高效——Python 可以缓存/复用相同的字符串(字符串驻留 intern);③线程安全——不可变对象天然不怕并发修改。唯一的"缺点"是频繁修改字符串时需要创建新对象,但实际开发中这很少是性能瓶颈。
Q4: is 和 == 有什么区别?什么时候用哪个?
is 判断两个变量是否指向同一个对象(身份相等),== 判断两个变量的值是否相等。a = [1, 2]; b = [1, 2] 则 a == b 是 True(值相同),但 a is b 是 False(两个不同的列表对象)。实际使用原则:①与 None 比较时始终用 is(x is None 或 x is not None),因为 None 是单例;②与 True/False 比较时直接用 if x: 而非 if x == True:;③其他情况通常用 ==。is 比 == 稍快(只是比较 id),但大多数场景下语义正确性比微小的性能差异重要。
Q5: None 和 False、0、"" 有什么区别?
它们都是 Falsy 值(bool() 返回 False),但语义完全不同。False 是一个布尔值,表示"否";0 是一个整数,表示"零";"" 是空字符串,表示"没有文字";None 是 NoneType 的唯一值,表示"没有数据"或"未定义"。在 AI 中,0 是一个正常的数值(可能是某个像素值或梯度值),None 表示"没有这个数据"(比如某个可选参数没有传入)。区分它们的关键是:你想表达的是"假/零/空字符串"还是"不存在"。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| variable | /ˈveriəbl/ | 变量,存储数据的命名标签 |
| integer | /ˈɪntɪdʒər/ | 整数,不带小数点的数 |
| float | /floʊt/ | 浮点数,带小数点的数 |
| string | /strɪŋ/ | 字符串,文本序列 |
| boolean | /ˈbuːliən/ | 布尔类型,True或False |
| NoneType | /nʌn taɪp/ | 空值类型,表示"无" |
| immutable | /ɪˈmjuːtəbl/ | 不可变的,创建后不能修改 |
| dynamic typing | /daɪˈnæmɪk ˈtaɪpɪŋ/ | 动态类型,运行时确定类型 |
| assignment | /əˈsaɪnmənt/ | 赋值,用等号给变量指定值 |
| concatenation | /kənˌkætəˈneɪʃən/ | 拼接,把字符串连在一起 |
| index | /ˈɪndeks/ | 索引,通过位置访问元素 |
| slice | /slaɪs/ | 切片,提取序列的一部分 |
| f-string | /ef strɪŋ/ | 格式化字符串字面量 |
| literal | /ˈlɪtərəl/ | 字面量,代码中直接写的值 |
面试练习
Q1 [单选] 以下代码的输出是什么?x = 5; y = x; x = 10; print(y)
- A. 10
- B. 5
- C. None
- D. 报错
解答:y = x时 y 指向 x 当时的值 5(整数对象)。之后x = 10只是把 x 标签贴到新对象 10 上,不影响 y 指向的 5。所以 y 仍然是 5。
Q2 [单选] type(3.0) 的结果是什么?
- A.
<class 'int'> - B.
<class 'float'> - C.
<class 'number'> - D.
<class 'double'>
解答:3.0带小数点,Python 自动识别为 float。3才是 int。Python 没有单独的 double 类型,float 就是双精度(64位)。
Q3 [单选] "Python"[2:5] 的结果是什么?
- A.
"yth" - B.
"tho" - C.
"th" - D.
"Pyth"
解答:索引从0开始:P=0, y=1, t=2, h=3, o=4, n=5。[2:5] 从索引2(t)开始到索引5(n)之前,即 t、h、o → "tho"。切片左闭右开。
Q4 [多选] 以下哪些是 Python 中的 Falsy 值?
- A.
0 - B.
""(空字符串) - C.
[](空列表) - D.
None
解答:A、B、C、D 全是 Falsy 值。Python 中 Falsy 值包括:False、0、0.0、空字符串、空列表、空字典、空集合、None。
Q5 [单选] 判断两个字符串 s1 和 s2 的内容是否相同,应该用?
- A.
s1 is s2 - B.
s1 == s2 - C.
s1.equals(s2) - D.
s1 - s2 == 0
解答:==比较值是否相等。is比较是否同一对象(即使两个字符串内容相同,is也可能返回 False)。C 是 Java 语法。D 字符串不支持减法。
Q6 [多选] 关于 Python 的变量命名,以下哪些是正确的?
- A.
my_variable = 10 - B.
_private = 20 - C.
var2 = 30 - D.
2var = 40
解答:A(snake_case 命名)、B(下划线开头)、C(数字可出现在变量名中但不是开头)都合法。D 不合法——变量名不能以数字开头。
Q7 [单选] int("42") 和 int(42.9) 的结果分别是?
- A.
42和43 - B.
42和42 - C.
"42"和42 - D. 报错 和
43
解答:int("42")把字符串"42"转为整数 42。int(42.9)截断小数部分得到 42(不是四舍五入!)。四舍五入用round(42.9)得到 43。
Q8 [单选] 以下哪个正确判断了变量 x 是否为 None?
- A.
if x == None: - B.
if x = None: - C.
if x is None: - D.
if x is not None:
解答:判断 None 必须用is(或is not),因为 None 是单例。B 是赋值语句非比较。D 判断的是"不是 None"。C 是正确的"是否为 None"判断。
Q9 [多选] 关于 f-string,以下说法正确的是?
- A. f-string 以字母 f 开头,花括号内可以放表达式
- B. f-string 支持格式控制,如
f"{pi:.2f}" - C. f-string 只能用于 Python 3.8 及以上版本
- D. f-string 比
format()方法更简洁直观
解答:A、B、D 正确。C 错误——f-string 从 Python 3.6 开始支持,不是 3.8。f-string 是目前最推荐的字符串格式化方式。
Q10 [多选] 以下关于字符串操作的描述正确的是?
- A. 字符串可以用
+拼接:"Hello " + "World"得到"Hello World" - B. 字符串可以用
*重复:"Hi" * 3得到"HiHiHi" - C. 可以用
s[0] = 'X'修改字符串的第一个字符 - D.
len("Python")返回 6
解答:A、B、D 正确。C 错误——字符串是不可变的,s[0] = 'X'会报 TypeError。正确的方法是创建新字符串:s = 'X' + s[1:]。