变量与数据类型:数字、字符串、布尔、空值

一句话概述

变量是存放数据的"标签盒子",数据类型则决定了盒子里面能放什么东西以及能对它做什么操作。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,常用于初始化变量、表示函数无返回值、或表示"数据缺失"。

大白话 TrueFalse 就是"是"和"否"——就像开关的"开"和"关"。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输入索引、切片、格式化
boolTrue/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 比较时始终用 isx is Nonex is not None),因为 None 是单例;②与 True/False 比较时直接用 if x: 而非 if x == True:;③其他情况通常用 ==is== 稍快(只是比较 id),但大多数场景下语义正确性比微小的性能差异重要。

Q5: NoneFalse0"" 有什么区别?

它们都是 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. 4243
  • B. 4242
  • 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:]