变量和赋值

本文各处的阅读理解练习旨在帮助你练习使用文章中的知识点。练习题的答案可以在本页结尾找到。

变量的意义在于帮助我们编写灵活易重用的代码。假设我们希望编写代码来记录学生的考试成绩,这过程背后的逻辑不管是记录小明的92分还是小丽的94分都应该是一样的。为此我们可以使用变量,如 namegrade,来作为这些信息的占位符。在本小节中,我们将会展示如何在Python中定义变量。

在Python中,符号 = 代表着“赋值”(assignment)操作符。变量在 = 左边,而用来赋值的对象在符号的右边:

name = "Brian"  # 为变量 `name` 赋值字符串 "Brian"
grade = 92      # 为变量 `grade` 赋值整数 92

将左右弄混(如 92 = name)会导致语法错误。当变量被某个对象(如一个数字或字符串)赋值时,我们经常会说这变量是该对象的引用(reference)。比如说,变量 name 是字符串 "Brian" 的引用。这意味着,当一个变量被对象赋值后,你可以在代码的其它地方将其作为该对象的代名词(或占位符)使用:

# 演示使用变量
name = "Brian"
grade = 92
failing = False

if grade < 60:
    failing = True

# 在文本文件“student_grades.txt”最后写:
# 名字 | 成绩 | 不及格
with open("student_grades.txt", mode="a") as opened_file:
    opened_file.write("{} | {} | {}".format(name, grade, failing))

变量的合法名字

合法的变量名字可以包括任何字母或数字(a-zA-Z0-9)以及下划线(_);合法的名字不可以由数字打头。

  • var:合法

  • _var2:合法

  • ApplePie_Yum_Yum:合法

  • 2cool不合法(数字打头)

  • I.am.the.best: 不合法(包括了 .

变量名也不能和其它Python保留的特殊名字冲突。因此,下列名字也不能作为变量名:

  • forwhilebreakpasscontinue

  • inisnot

  • ifelseelif

  • defclassreturnyieldraises

  • importfromaswith

  • tryexceptfinally

还有一些unicode符号可以作为Python变量名中的字符,但本文在此不进行讨论。

可变和不可变对象

对象的可变性指它的内态(state)是否能被改变。可变对象的内态可以被修改,而不可变对象则不能。比如说,列表就是可变对象的一种。在创建之后,我们可以更新列表的内容——代替,增加,或删除它的成员。

# 演示列表的可变性
>>> x = [1, 2, 3]
>>> x[0] = -4  # 将 `x` 索引为0的成员修改成 -4
>>> x
[-4, 2, 3]

让我们来看看这里具体发生了什么,我们:

  1. 创建(初始化)了一个列表,其内态为 [1, 2, 3]

  2. 为变量 x 赋值了此列表;x 现在是该列表的一个引用了。

  3. 通过我们的引用变量 x 更新了列表索引为0的成员以储存整数 -4

这并没有创建一个新的列表对象,而仅仅修改了原本的列表。这也是为什么在命令行中打印 x 会显示 [-4, 2, 3] 而不是 [1, 2, 3]

元组则是不可变对象的一种。在创建之后,我们没有任何办法可以改变元组的内态;任何看起来像是在更新元组的代码起始都是在创建一个全新的元组。

# 演示元组的不可变性
>>> x = (1, 2, 3)
>>> x[0] = -4  # 试图将 `x` 索引为0的成员修改为 -4
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-a858573fdc63> in <module>()
      1 x = (1, 2, 3)
----> 2 x[0] = -4  # attempt to replace element-0 of `x` with -4

TypeError: 'tuple' object does not support item assignment

可变和不可变对象类型

以下是Python中常见的一些可变和不可变对象。在我们开始讨论词典(dictionary)和集(set)时这些知识会很有用。

一些不可变对象

一些可变对象

多个变量引用一个可变对象

你可以向变量赋值给其它已经存在的变量。这将会导致这些变量都引用同一个对象:

# 演示引用同一个对象的变量行为
>>> list1 = [1, 2, 3]  #  `list1` 引用 [1, 2, 3]
>>> list2 = list1      #  `list2` 和 `list1` 现在都引用 [1, 2, 3]

>>> print(list1)
[1, 2, 3]

>>> print(list2)
[1, 2, 3]

具体来讲,这两个变量都引用这列表的同一个实例(instance)。这意味着如果列表有变化,所有引用它的变量都会反映这个变化:

>>> list1.append(4)  # 在 [1, 2, 3] 结尾附加4
>>> print(list1)
[1, 2, 3, 4]

我们可以看到 list2 还在引用相同的,更新过的列表,所以它和 list1 同步:

>>> print(list2)
[1, 2, 3, 4]

一般来讲,为变量 b 赋值变量 a 会导致它们引用系统内存中相同的对象,而为变量 c 赋值变量 ab 会导致有第三个变量引用同样的对象。然后,该对象的任何变化(也就是mutation)会在所有引用它的变量中反映(ab,和c)。

当然,为两个变量赋值内容一样但不同的列表意味着一个列表的变化不会影响另外那个:

>>> list1 = [1, 2, 3]  #  `list1` 引用 [1, 2, 3]
>>> list2 = [1, 2, 3]  #  `list2` 引用*另外*一个列表,其值为 [1, 2, 3]

>>> list1.append(4)  # 在 [1, 2, 3] 结尾附加4
>>> print(list1)
[1, 2, 3, 4]

>>> print(list2)     # `list2` 依然引用它自己的列表
[1, 2, 3]

阅读理解:切片列表是否会创建一个列表的引用?

设变量 x,赋值为一个列表,和变量 y,赋值为 x 的一个“切片”。xy 是否引用同样的列表?也就是说,当你更新 xy 的子序列时,变化是否会在两个变量中都显示?请编写一些简单的代码来查明此问题。

阅读理解:理解引用

根据我们对可变和不可变对象的讨论,预测下列情况中 y 的最终值:

>>> x = 3
>>> y = x

# 和 `x = x * 3` 等值
>>> x *= 3
>>> x
9

>>> y
???

阅读理解答案

切片列表是否会创建一个列表的引用?:解

根据以下的行为,我们可以得出切片列表并不创建一个列表的新引用。反而,切片列表会创建对应列表子序列的新列表:

>>> x = [0, 1, 2, 3]

>>> y = x[:2]
>>> y      # `y` 是否和 `x` 引用同一个列表?
[0, 1]

>>> x[0] = -1  # 更新 `x` 引用的列表中的某个成员
>>> x
[-1, 1, 2, 3]

>>> y      # `y` 引用的列表并没有反映这个更新
[0, 1]

理解引用:解

整数不可变,因此 x 肯定是引用一个全新的对象(9),而 y 还在引用 3