编程语言研究书库

变量与数据类型 — 程序开始有记忆

变量与数据类型 — 程序开始有记忆

这一章给程序装上记忆。 前面两章里,程序每一行都是一次性的; 这一章起,你可以把一个值存下来、起个名字、之后反复用。 名字怎么起、值有哪几种、一种怎么变成另一种——三件小事,但它们是后面所有章的地基。

1. 这一章在全书链条里的位置

第 02 章打通了「进」和「出」两个口,但程序还是「金鱼记忆」:算出来的东西用完即弃。 变量是第一个让程序跨行记住东西的机制, branching(第 07 章)和函数(第 09 章) 全都建立在它上面。同时这一章引入的 int / float / str / bool 四种类型, 是后面数据类型章节(第 11–14 章)的四块基石。

2. 顶层全景:一个值的一生

  起名字   x = 5          → 名字 x 指向内存里的 5
  拿来用   print(x * 2)   → 每次用 x,解释器顺着引用找到 5
  换指向   x = 'hello'    → x 不再指向 5,改指向 'hello';5 本身没变
  问身份   type(x)        → 告诉你 x 当前指向的值是哪种类型(答案随换指向而变)

  图说:变量是门牌,不是房子。换值是换门牌指向的房子,不是改建房子。

3. 核心原理一:变量是引用,不是盒子

原书对变量的定义只有一句:用来存储和引用程序中的值1。 没有变量,你就得把同一个值在代码里反复硬写(硬编码:把数字直接写死在代码里), 改一个数要改几十处,程序又僵又难维护2。

真正要紧的是紧接着那句:Python 的变量是引用(reference)——它指向内存里 实际存放值的位置。给变量赋新值时,改变的是「指向哪里」,而不是「把原来的值改掉」3。

「盒子」和「门牌」的差别在哪里?看主线例子:

主线例子:同一个 x,四种身份

跟着原书的练习序列,让一个变量 x 从头到尾变身四次,每一步都拿 type() 验明正身4:

x = 5              # 第 1 次赋值
print('x:', x, type(x))
# 输出:x: 5 <class 'int'>

x = 5.0            # 第 2 次赋值
print('x:', x, type(x))
# 输出:x: 5.0 <class 'float'>

x = '5'            # 第 3 次赋值
print('x:', x, type(x))
# 输出:x: 5 <class 'str'>

x = '5.0'          # 第 4 次赋值
print('x:', x, type(x))
# 输出:x: 5.0 <class 'str'>

逐帧看发生了什么:

  ① x = 5      名字 x 指向内存里的整数 5        type(x) → int
  ② x = 5.0    x 改指向浮点数 5.0;整数 5 原地未动  type(x) → float
  ③ x = '5'    x 改指向字符串 '5';5.0 原地未动      type(x) → str
  ④ x = '5.0'  x 又改指向字符串 '5.0'             type(x) → str

  图说:屏幕上看着像 x 被「改写」,实际是 x 四次指向了四个不同的值。
  注意 ③④ 输出里的 5 / 5.0 没有引号显示差异——肉眼分不清 '5' 和 5,
  这就是为什么必须靠 type() 验明正身,这也是本练习的设计意图[^4]。

两个肉眼陷阱原书特意让你踩一遍:'5'(字符串)和 5(整数)显示出来都是 5; '5.0' 和 5.0 显示出来都是 5.0。区别只在类型——而类型决定它能做什么 (5 + 5 是 10,'5' + '5' 是 '55',下一章详讲)4。

练习的后半段还多走一步:把 x 的当前值复制给新变量(int_x = x 等), 然后继续改 x。最后的全家福揭示了引用的另一面相:

  x: 5.0 <class 'float'>     ← x 已经走到第 4 站
  int_x: 5 <class 'int'>     ← int_x 停在复制那一刻的 5
  float_x: 5.0 <class 'float'>
  str1_x: 5 <class 'str'>

  图说:复制是「那一刻各指向同一个值」;之后 x 再换指向,别人不受影响。

4. 核心原理二:起名字 — 两套规则,一套硬一套软

原书把命名规则分成两层,这个分法值得整章带走:语法层(不遵守就报错) 和惯例层(不遵守能跑,但会被同行皱眉)5。

语法层,四条,全是死规矩6:

规矩例子
只能以字母或下划线 _ 开头4square ❌(数字开头)
其余位置只能有字母、数字、下划线five! ❌(感叹号不合法)
区分大小写name 和 Name 是两个变量
不能是保留关键字if、else、while 这些被语言本身占用的词 ❌

惯例层,五条,全是「专业人怎么写」6:

  1. 名字要有描述性,让人看出它装什么;
  2. 要短,但短到看不懂就过头;
  3. 全小写,单词间用下划线——first_name,而不是 FirstName7;
  4. 不用缩写,除非业界都认(比如 id);
  5. 别以下划线开头,除非你懂那个形式的特殊用途8。

书里的练习给了一组名字让你分类:比如 b、Cat、Ten_points、lIama3、gj 都算 「能跑但不好」——它们或大小写不对、或不知所云;_seven_day 合法但撞了第 5 条惯例; 只有 application_id、six_dice 这样的双双过关9。

记住这个判据:报错是解释器在拦你;违反惯例是未来的同事(和三个月后的你)在叹气。

5. 核心原理三:四种基本类型与动态类型

原书列了 Python 的四种内建数据类型10:

类型名字例子装什么
整数int1, 2, 3, -3不带小数的数
浮点数float3.14, -0.5带小数的数(所谓「浮点」是计算机存小数的方式,名字怪但不用纠结)
字符串str'hello', "hello"文字
布尔值boolTrue, False是/否两个值(注意首字母大写),第 07 章分支会重度使用

然后是一个 Python 的标志性设计:动态类型(dynamic typing)—— 变量的类型不是事先声明死的,而是由它当前指向的值实时决定: 你赋整数它就是 int,过两行赋字符串它就变成 str,运行期间随时可变11。

  x = 5     → x 是 int(值说了算)
  x = '5'   → x 变成 str(还是值说了算)
  # 全程没有人「声明」x 是什么类型;类型是值的属性,不是变量的属性

原书拿静态类型语言作对照:那些语言里变量的类型固定,赋错类型直接编译报错11。 (补充,不在书里:C、Java、Rust 都是静态类型;JavaScript 和 Python 一样是动态类型。 这个差别是「编程语言研究」里类型系统的入门第一问。)

type() 是配套的内建函数:给它任何值或变量,它返回那个值的类型, 练习里 print('x:', x, type(x)) 的写法就是「值和身份一起打印」的标配动作12。

6. 核心原理四:类型转换 — 拆掉第 02 章那堵墙

还记得第 02 章的墙吗:input() 永远返回字符串,'18' + 1 报错。 这一章的工具 int()、float()、str() 就是拆墙的13:

int('5')     # 字符串 '5'  → 整数 5
float('5.5') # 字符串 '5.5' → 浮点数 5.5
str(5)       # 整数 5      → 字符串 '5'
float(5)     # 整数 5      → 浮点数 5.0
int(5.5)     # 浮点数 5.5  → 整数 5    ← 注意!

三个「会咬人」的细节,原书逐一点名14:

  1. int() 转浮点数是直接砍掉小数部分(取整到零的方向):int(5.5) 得 5, int(0.1) 得 0,不是四舍五入。原书原话:它返回的是「地板值」而不是实际值15。
  2. 乱转的字符串会报 ValueError:int('5.5') 报错——字符串里含小数点的内容 不能直接转整数;float('a') 同样报错16。想转 int,得先 float('5.5') 再 int()。
  3. 转换产生的是新值,不改动原值:str(x) 交给你一个字符串,x 自己还是原来的类型。

原书最后给了一张四类型对照表(定义、构造器、可变不可变、适用场景、优缺点), 把四个类型一次性摆平17。其中一个词先混个脸熟:immutable(不可变)—— 「值一旦造出来就不能被修改,只能造新的」。这张表里的四种类型全部不可变; 第 11 章的列表会第一个打破这条。

把主线例子补完:input 的那堵墙,拆了

age = input('How old are you? ')   # 用户敲 18,age 是字符串 '18'
age = int(age)                     # 就地转掉:age 变成整数 18
print('Next year you will be', age + 1)
# 用户输入 18 时输出:Next year you will be 19

(输入 18 取自原书第 02 章练习题18。)这三行就是「问数字、算数字」的标准起手式, 你会在无数入门程序里见到它。

7. 作者的判断与证据

  • 「变量是引用而非盒子」是 Python 的官方心智模型,书里的表述(更新引用、 原值不动)与语言文档一致3——属于转述事实,不是作者个人主张。
  • 「单引号是惯例」在命名一节落实为「全小写+下划线」,这是 Python 官方风格指南 PEP 8 的规定,书里没有提 PEP 8 这个名字,但五条惯例与其吻合7。 (补充,不在书里:PEP 8 是 Python 社区的风格规范文档,由 Python 作者本人参与撰写。)
  • 练习设计上的一个刻意:让 '5'/5 和 '5.0'/5.0 在屏幕上长得一样, 逼你用 type() 分辨4——这是把「显示值 ≠ 类型」这条铁律做成了一次体检。

8. 边界与局限

  • bool 几乎没展开。 原书只在类型表里给了 True/False 两个值10, 布尔值真正的用武之地(比较、逻辑运算、分支)在第 04、07 章——这一章见到它 认个脸即可。
  • int() 截断这件事,知道皮毛不够。 int(-5.5) 得 -5(往零方向截), 要「真四舍五入」得用 round() 或先 math.floor()——书里没提这两个名字。
  • 没有讲复合类型。 四种都是「单值」类型;装一堆值的列表、字典分别在第 11、14 章。
  • 「变量是引用」在这里只讲了半截。 对于不可变的 int/str,引用与否你感觉不到差别; 等第 11 章可变对象(列表)登场,「引用 vs 复制」才会变成真正的坑。这一章埋了伏笔。

9. 可带走的

  1. 变量是引用:赋值是「改指向」,原来的值原地不动3。
  2. 显示出来的样子 ≠ 类型;'5' 和 5 肉眼同形,用 type() 分辨4。
  3. 命名两套规则:语法层四条(不报错的底线),惯例层五条(专业的底线)6。
  4. 惯例的默认写法:全小写、下划线分词(first_name)7。
  5. 四种基本类型:int、float、str、bool;类型由当前指向的值决定(动态类型)1011。
  6. int('5')、float('5.5')、str(5) 三个转换器拆 input() 的墙13。
  7. int() 砍小数不四舍五入:int(5.5) 是 515。
  8. int('5.5')、float('a') 这类乱转会报 ValueError16。
  9. 四种基本类型全部不可变(immutable)——值造出来不能改,只能造新的17。

10. 原文地图

主题原书章原文位置
变量的定义与「引用」心智模型CHAPTER 3: Variablestext/12-ch03-chapter-3-variables.txt:13(搜「store and reference values」) · text/12-ch03-chapter-3-variables.txt:17(搜「work as references」)
x = 5 与更新引用同上text/12-ch03-chapter-3-variables.txt:24(搜「x = 5」)
命名规则的硬/软两层同上text/12-ch03-chapter-3-variables.txt:41(搜「two levels of naming rules」)
语法层四条同上text/12-ch03-chapter-3-variables.txt:45(搜「Syntax rules are」)
惯例层五条与单下划线禁忌同上text/12-ch03-chapter-3-variables.txt:52(搜「Convention rules are」) · text/12-ch03-chapter-3-variables.txt:61(搜「start with an underscore」)
命名分类练习同上text/12-ch03-chapter-3-variables.txt:68(搜「mark them with」)
四种基本数据类型同上text/12-ch03-chapter-3-variables.txt:88(搜「built-in data types」)
动态类型与静态类型对照同上text/12-ch03-chapter-3-variables.txt:98(搜「dynamic typing」) · text/12-ch03-chapter-3-variables.txt:101(搜「statically typed」)
x 四次变身的练习序列同上text/12-ch03-chapter-3-variables.txt:106(搜「assign x with an int 5」) · text/12-ch03-chapter-3-variables.txt:166(搜「all the variables you created」)
type() 的用法同上text/12-ch03-chapter-3-variables.txt:105(搜「type() will return」)
类型转换与三个会咬人的细节同上text/12-ch03-chapter-3-variables.txt:183(搜「convert a value from one data type」) · text/12-ch03-chapter-3-variables.txt:217(搜「Be careful if you convert」)
int() 取地板值同上text/12-ch03-chapter-3-variables.txt:220(搜「floor value」)
四类型对照表同上text/12-ch03-chapter-3-variables.txt:223(搜「Comparison of basic Python data types」)

Footnotes

  1. 出处:「CHAPTER 3: Variables」第 13 段(text/12-ch03-chapter-3-variables.txt:13,搜「store and reference values」)。原文:「Variables are used to store and reference values in a program.」 ↩

  2. 出处:「CHAPTER 3: Variables」第 14–16 段(text/12-ch03-chapter-3-variables.txt:14,搜「hardcode values」;:15,搜「store a value once」)。「硬编码」是对 hardcode 的通行译法。 ↩

  3. 出处:「CHAPTER 3: Variables」第 17–20 段(text/12-ch03-chapter-3-variables.txt:17,搜「work as references」;:19,搜「updating the reference」)。「门牌/盒子」的比方是我们加的,原文只给了机制描述。 ↩ ↩2 ↩3

  4. 出处:「CHAPTER 3: Variables」第 106–178 段(text/12-ch03-chapter-3-variables.txt:106,搜「assign x with an int 5」;:166,搜「all the variables you created so far」)。x 的四次赋值、int_x/float_x 等的复制、以及最后的五变量全家福,均逐字依据原书练习的输出。 ↩ ↩2 ↩3 ↩4

  5. 出处:「CHAPTER 3: Variables」第 41–44 段(text/12-ch03-chapter-3-variables.txt:41,搜「two levels of naming rules」;:41,搜「two levels of naming rules」;:43,搜「soft rules following the convention」)。 ↩

  6. 出处:「CHAPTER 3: Variables」第 45–51 段(语法四条)与第 52–58 段(惯例五条),原文条目见 text/12-ch03-chapter-3-variables.txt:45(搜「Syntax rules are」)与 :52(搜「Convention rules are」)。 ↩ ↩2 ↩3

  7. 出处:「CHAPTER 3: Variables」第 55–56 段(text/12-ch03-chapter-3-variables.txt:55,搜「lowercase letters and underscores」)。「first_name instead of FirstName」是原文的例子。「PEP 8」之名不在原书,标为补充(来自通用知识)。 ↩ ↩2 ↩3

  8. 出处:「CHAPTER 3: Variables」第 61–62 段(text/12-ch03-chapter-3-variables.txt:61,搜「should not start with an underscore」)。原文:「unless you understand the special usage of this form」。 ↩

  9. 出处:「CHAPTER 3: Variables」第 68–83 段(text/12-ch03-chapter-3-variables.txt:72,搜「application_id: GOOD」)。全部十二个名字的判词见原文练习区。 ↩

  10. 出处:「CHAPTER 3: Variables」第 88–92 段(text/12-ch03-chapter-3-variables.txt:88,搜「built-in data types include」)。四类及其例子逐字依据原文。 ↩ ↩2 ↩3

  11. 出处:「CHAPTER 3: Variables」第 93–101 段(text/12-ch03-chapter-3-variables.txt:93,搜「determined by the data type of the value」;:98,搜「dynamic typing」;:101,搜「statically typed languages」)。「类型是值的属性,不是变量的属性」是我们的概括。 ↩ ↩2 ↩3

  12. 出处:「CHAPTER 3: Variables」第 103–105 段(text/12-ch03-chapter-3-variables.txt:105,搜「will return the data type」)。 ↩

  13. 出处:「CHAPTER 3: Variables」第 182–185 段(text/12-ch03-chapter-3-variables.txt:183,搜「convert a value from one data type to another」)。 ↩ ↩2

  14. 出处:「CHAPTER 3: Variables」第 217–220 段(text/12-ch03-chapter-3-variables.txt:217,搜「Be careful if you convert values」)。三个细节全部出自原书这段警告。 ↩

  15. 出处:「CHAPTER 3: Variables」第 219–220 段(text/12-ch03-chapter-3-variables.txt:220,搜「floor value」)。原文:「try to convert '5.5' to an int, which returns the floor value (5) instead of the actual value」。「不是四舍五入」的强调与「往零方向」的补充是我们加的。 ↩ ↩2

  16. 出处:「CHAPTER 3: Variables」第 218 段(text/12-ch03-chapter-3-variables.txt:218,搜「convert a string literal ('a') to a float」)。原书此处只说了「you may encounter errors」并举例;错误名 ValueError 是我们补的(通用知识),原书未点名。 ↩ ↩2

  17. 出处:「CHAPTER 3: Variables」第 221–238 段(text/12-ch03-chapter-3-variables.txt:223,搜「Comparison of basic Python data types」)。表格的四列(定义、例子、构造器、可变性、场景、优缺点)逐字依据原文 Table 3.1。 ↩ ↩2

  18. 出处:「CHAPTER 2: Input and Output」第 360–364 段(text/11-ch02-chapter-2-input-and-output.txt:360,搜「How old are you?」)。「拆墙」三行代码是把第 02 章的演示与第 03 章的转换拼起来的标准写法,拼接是我们的组织方式。 ↩

On this page