RSS
菜单
全部文章快讯开发科技深度热点

变量与数据类型(Python 从精通到入门 · 22)

内容摘要

变量不是盒子,而是贴在值上面的名字标签。搞清楚这一点,你就不会再被"赋值"和"改值"搞晕。

变量不是盒子,而是贴在值上面的名字标签。搞清楚这一点,你就不会再被"赋值"和"改值"搞晕。

前面的五篇,我们从元类一路聊到推导式,全是"先有对象,再谈语法"。现在到了入门篇,我们从最基础的地方重新出发。别担心,你已经在前面见过这些概念了,这一篇只是把它们讲得更慢、更细。

你将学到

  • 变量在 Python 里到底是什么(名字绑定,不是盒子)
  • 动态类型意味着什么,以及它给你的自由与坑
  • 五种最常用的类型:int、float、str、bool、None
  • 用 type() 和 isinstance() 检查类型
  • 变量命名规范、赋值、多重赋值、交换
  • 为什么有的值能改、有的值不能改(可变 vs 不可变)
  • f-string:把变量塞进文本里的现代写法

前置知识

建议先读 上一篇:推导式与函数式工具,或者干脆把这一篇当作新起点。

变量是什么:名字绑定

很多教材会说"变量是一个盒子,你把值放进去"。这个比喻在 C 语言里勉强成立,但在 Python 里会误导你。

Python 的真相是:值(对象)先存在,变量只是贴在它上面的一个名字标签。

x = 42

这行代码做的是:创建一个整数对象 42,然后让名字 x 指向它。以后你说 x,Python 就顺着标签去找那个对象。

x = 42
y = x        # 让 y 也指向同一个 42
x = 100      # 让 x 改指 100,y 不受影响
print(x, y)  # 输出: 100 42

注意:x = 100 改的是"x 这个名字指向谁",不是"把 42 这个值改成 100"。42 还是 42。

用一个办法验证"标签"这个说法——用 id() 看对象的内存地址:

a = 42
b = a
print(id(a) == id(b))  # 输出: True,两个名字指向同一个对象
a = 100
print(id(a) == id(b))  # 输出: False,a 换对象了,b 没动

动态类型:名字没有类型,对象才有

在 Python 里,变量本身没有类型。同一个名字,这一秒指向整数,下一秒可以指向字符串:

v = 10          # v 现在指向一个 int
v = "十"        # v 现在指向一个 str,完全合法
v = [1, 2, 3]   # 又指向一个 list
print(v)        # 输出: [1, 2, 3]

这叫动态类型。好处是写起来灵活,坏处是错误可能在运行时才暴露。所以后面进阶篇会教你用类型注解 + mypy 来"提前抓错"。

类型属于对象,不属于名字。 记住这句话,很多疑惑会自动消失。

五种最常用的类型

int:整数

a = 7
b = -3
c = 1000000000000000000   # Python 的整数可以任意大,不会溢出
print(type(a))            # 输出: <class 'int'>

float:浮点数

pi = 3.14159
tiny = 1.5e-3       # 科学计数法,等于 0.0015
print(pi + 1)       # 输出: 4.14159
print(type(pi))     # 输出: <class 'float'>
print(0.1 + 0.2)    # 输出: 0.30000000000000004(浮点数精度问题,详见常见坑)

str:字符串

name = "Python"
greeting = '你好'
print(type(name))   # 输出: <class 'str'>
print(len(name))    # 输出: 6

bool:布尔值

只有两个值:True 和 False。注意首字母大写。

is_ready = True
is_done = False
print(type(is_ready))  # 输出: <class 'bool'>
print(3 > 2)           # 输出: True

None:空值

表示"什么都没有"。它是 NoneType 类型的唯一一个值。

result = None
print(type(result))    # 输出: <class 'NoneType'>
print(result is None)  # 输出: True(判断 None 用 is,不用 ==)

type() 与 isinstance()

查类型有两种方式,用途不同。

n = 42

# type():精确判断"是不是恰好是这个类型"
print(type(n))                  # 输出: <class 'int'>
print(type(n) is int)           # 输出: True

# isinstance():判断"是不是这个类型,或者它的子类"
print(isinstance(n, int))       # 输出: True
print(isinstance(True, int))    # 输出: True(bool 是 int 的子类!)

实践建议:做类型判断时优先用 isinstance(),因为它认子类。type(x) == int 会漏掉 bool 和 int 的自定义子类。

value = True
print(type(value) is int)       # 输出: False(bool 不等于 int)
print(isinstance(value, int))   # 输出: True(bool 是 int 的子类)

变量命名规范

规则(写了会报错):

  • 只能包含字母、数字、下划线 _
  • 不能以数字开头
  • 不能是 Python 关键字(如 if、class、for)

约定(不报错,但请遵守):

user_name = "小明"     # ✅ 小写下划线(snake_case),推荐
userName = "小明"      # ⚠️ 能跑但不是 Python 风格
MAX_SIZE = 100         # ✅ 常量用全大写
_x = 1                 # ✅ 前导下划线表示"内部使用"
# ❌ 这些名字虽然合法,但会让你自己和同事难受
x = 3
a1 = 4
data = 5               # 含义不明的短名
list = [1, 2]          # 覆盖了内置的 list 函数!
# ✅ 名字就是最好的注释
width = 3
height = 4
area = width * height

赋值、多重赋值、交换

# 普通赋值
a = 1

# 多重赋值:一次给多个名字赋值
x, y, z = 1, 2, 3
print(x, y, z)   # 输出: 1 2 3

# 同一个值赋给多个名字
p = q = 0
print(p, q)      # 输出: 0 0

# 交换两个变量(Python 独门绝技,无需临时变量)
a, b = 10, 20
a, b = b, a
print(a, b)      # 输出: 20 10

上面那句交换,背后的原理就是前面讲的"名字标签":先算出右边的元组 (b, a),再依次贴回左边的名字。

不可变 vs 可变(初识)

同样是"改值",行为却不同:

# int 是不可变的:看起来"改了",其实是换了新对象
n = 10
print(id(n))   # 输出: 某个地址
n = n + 1
print(id(n))   # 输出: 另一个地址(n 换了新对象)

# list 是可变的:原地修改,对象本身没换
nums = [1, 2, 3]
print(id(nums))     # 输出: 某个地址
nums.append(4)
print(id(nums))     # 输出: 同一个地址(对象没换,内容变了)
print(nums)         # 输出: [1, 2, 3, 4]

记住:不可变类型(int、float、str、tuple、bool、None)一改就换新对象;可变类型(list、dict、set)可以原地改。 这个区别会在后面讲字典和集合时反复用到。

f-string 初体验

把变量拼进字符串,最现代的写法是 f-string(前缀 f,Python 3.6+):

name = "小明"
age = 18

# ✅ 推荐:可读性最好
print(f"我叫{name},今年{age}岁")     # 输出: 我叫小明,今年18岁

# 花括号里可以直接写表达式
print(f"明年我{age + 1}岁")           # 输出: 明年我19岁
print(f"名字长度:{len(name)}")       # 输出: 名字长度:2

对比一下老写法,你就明白好处了:

# 老写法一:字符串拼接(类型不对就报错)
print("我叫" + name + ",今年" + str(age) + "岁")

# 老写法二:% 格式化
print("我叫%s,今年%d岁" % (name, age))

f-string 的更多格式化技巧,我们会在 26-字符串与格式化 里专门展开。

常见坑

坑一:浮点数不精确

# ❌ 用 == 直接比较浮点数
print(0.1 + 0.2 == 0.3)   # 输出: False(它们是 0.30000000000000004)

# ✅ 用一个极小误差比较,或改用 decimal
import math
print(math.isclose(0.1 + 0.2, 0.3))   # 输出: True

坑二:判断 None 用 is 而不是 ==

x = None

# ❌ == 可能被自定义的 __eq__ 影响,语义上也不够准确
if x == None:
    pass

# ✅ is 判断的是"是不是同一个对象",这才是你想要的
if x is None:
    pass

坑三:变量名覆盖内置函数

# ❌ list 是内置函数,被覆盖后再也调不到它了
list = [1, 2, 3]

# ✅ 换个名字
numbers = [1, 2, 3]

坑四:字符串和数字相加

age = 18

# ❌ TypeError: can only concatenate str (not "int") to str
# print("年龄:" + age)

# ✅ 用 f-string 或先转成 str
print(f"年龄:{age}")
print("年龄:" + str(age))

小结

  • 变量是贴在对象上的名字标签,赋值 = 改标签指向,不是改值本身。
  • 名字没有类型,类型属于对象;这就是"动态类型"。
  • 五种常用类型:int、float、str、bool、None。
  • 查类型用 type()(精确)或 isinstance()(认子类,更推荐)。
  • 命名用 snake_case,常量全大写,别覆盖内置名。
  • 多重赋值和 a, b = b, a 交换是 Python 的招牌写法。
  • 不可变类型一改就换新对象;可变类型能原地改。
  • f-string 是拼字符串的首选:f"你好{name}"。

延伸阅读

  • Python 官方文档:内置类型
  • PEP 8 —— 官方的命名与风格指南

上一篇:推导式与函数式工具 · 下一篇:运算符与表达式

— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《变量与数据类型(Python 从精通到入门 · 22)》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。