4  变量和常用类型

本章我们将会介绍Python中最基础、也最重要的构件:变量常用数据类型

完成本章学习后,你将能够:

学完这一章,你最少就能把Python当作一个非常强大的计算器超级记事本来使用。这是后续学习更复杂数据分析任务的基础。

再次强调: 编程时,请务必确保所有的语法符号(如冒号 :、引号 ""''、括号 ()、等号 = 等)都必须是英文符号,在英文状态下输入。这是初学者最常见的错误来源之一!

4.1 变量

前面说过,Python(或者其他编程语言)中的变量,和你数学课上的x, y, z是同类的概念。

正如前面的例子,Python使用等号=来为一个变量赋值

#%% 赋值与重新赋值
a = 1
a = 2
print(a)
2

假如这个变量a一开始不存在,那么赋值的同时,也会把这个变量创造出来。

所谓变量名,本质上只是一个“标签”。Python处理赋值a = 1时,可以这样理解:

  1. 创建数据: Python先在内存中创建了一个地方,存放了数字1这个数据。
  2. 贴上标签: 然后,它把a这个名字(我们叫它变量名)像一张标签一样,贴到了存放1的那个地方。现在,你说a,Python就知道你指的是1。
  3. 更换标签: 当你执行a = 2时,Python又在内存中创建了一个地方存放2。接着,它把a这张标签从1那里撕下来,贴到了2这个新地方。原来的1如果没其他标签了,之后可能会被Python清理掉(这部分我们暂时不用深究)。
  4. 使用标签: 当我们写print(a)时,Python就去看a标签现在贴在哪里,然后把那个地方的数据(现在是2)拿出来显示。”

4.2 给变量起名字:命名规则与约定

在Python中给变量起名字也需要遵守一些规则和约定:

必须遵守的规则:

  1. 字符限制: 变量名只能包含字母(大写A-Z, 小写a-z)、数字(0-9)和下划线 (_)。不能包含空格、@$- 等特殊符号。
  2. 开头限制: 变量名不能以数字开头
  3. 大小写敏感: 变量名是区分大小写的。例如,age, Age, AGE 是三个不同的变量。

示例:

  • 合法的变量名: student_id, price1, user_name, _temporary_var (以下划线开头是合法的)
  • 非法的变量名:
    • 1st_place (以数字开头)
    • student id (包含空格)
    • customer-name (包含连字符 -,应使用下划线 _)
    • price$ (包含特殊符号 $)

可能遇到的错误:

如果你使用了非法的变量名,Python通常在你尝试给它赋值时就会报错,最常见的是 SyntaxError: invalid syntax (语法错误:无效语法)。

推荐的约定(好习惯):

  1. 见名知意: 使用有描述性的名字,让人能猜到变量里存的是什么数据。比如用 user_name 而不是 un,用 final_score 而不是 fs
  2. 使用小写和下划线 (Snake Case): 这是Python社区广泛接受的风格(称为“蛇形命名法”)。即变量名全部用小写字母,如果由多个单词组成,单词之间用下划线 _ 分隔。例如:interest_rate, customer_address
  3. 避免使用Python关键字: 不要用Python语言本身有特殊含义的词(关键字)作为变量名。常见的关键字有:if, else, for, while, def, class, import, return, True, False, None, and, or, not 等等。如果你不小心用了关键字做变量名,也会得到 SyntaxError

4.2.1 删除一个变量

使用del语句

#%% 删除变量
a = 1
print(a) # 先确认a存在且值为1
del a
print(a) # 再次引用会出错
1
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3186], line 5
      3 print(a) # 先确认a存在且值为1
      4 del a
----> 5 print(a) # 再次引用会出错

NameError: name 'a' is not defined

因为变量a已经被我们删除了,所以你再次引用a的时候,Python会告诉你,

NameError: name 'a' is not defined

4.2.2 常见错误name not defined

NameError: name 'xxx' is not defined

这个错误要告诉你,xxx这个东西,python找不到。这可能是:

  1. 打错字。例如你要引用一个名字叫apple,但输入成了appla。vscode会有提示。
  2. 代码存放在其他地方,没有import,后面会讲。
  3. 引用了一个不存在的变量。

稍微解释下第3点:

  1. 一个重新启动的Python交互环境,可以理解为一片白纸(其实不是完全是)。
  2. 第一次执行a = 1语句之后,a这个变量才会存在。
  3. .py文件中,有a=1这个语句,如果你不执行一次,内存中也不会有a

这表示,你只是打开一个.py文件,其实交互环境里面还是什么都没有, 这个时候print(a),就会提示找不到a

这个错误非常常见,尤其是刚开始学的时候。遇到它,首先检查拼写,其次想想这行代码之前的相关赋值代码是否真的被运行过了。

4.2.3 变量类型和动态语言

Python是一个“动态语言”,即Python的变量的类型是在运行过程中决定,或者说可以在运行中改变:你对这个变量赋什么值,这个变量就是什么类型。

查看变量类型的函数是type()

例如

#%% 动态类型
a = 1 
print(type(a))

a = 'apple' # 这里为a赋值了一个字符串
print(type(a))
<class 'int'>
<class 'str'>

显然,a先是一个整型int<class 'int'>,然后变成了一个字符串str<class 'str'>。 这和我们的赋值顺序是一样的。类型后面会详细说

注意:Python的变量类型是动态确定的。变量的类型不一定能从名字看出来,这是出错的一大来源。

4.3 数值

Python 3.x以后,数值类型有2种,整型int,和浮点型float

顾名思义,整型可以理解为整数:

#%% 整型
a = 1
print(type(a))
<class 'int'>

而浮点型则可以理解为小数:

#%% 浮点型
a = 1.23
print(type(a))
<class 'float'>

特别地,a = 1.0是什么类型?

a = 1.0
print(type(a))
<class 'float'>

显然,a是浮点型:只要你赋值的时候有小数点。 这可能是因为:

  1. 这个变量客观上是个小数,只是“恰好”是1而已。
  2. 或者这个数被四舍五入,比如本来是1.0000001之类。

4.3.1 数值的运算

  1. 常见的操作包括加减乘除+, -, *, /等等,此处不再重复。

特别地,除法永远返回浮点类型:

a = 4 / 2
print(a)
print(type(a))
2.0
<class 'float'>
  1. 整除是//。若除数是整型,则返回整型;若除数是浮点型,则返回浮点型
5 // 2
2
5 // 2.0
2.0
  1. 取余%
5 % 2
1
  1. 乘方 \(2^3\)
2 ** 3
8

4.3.2 小练习

求二元一次方程的根:

\[ 2x^2 + 5x - 3 = 0 \]

提示:求根公式是 \[ x = \frac{ -b \pm \sqrt{b^2 - 4ac} }{2a} \]

答案是(-3, 0.5)

4.4 字符串String

创建字符串,可以使用单引号、双引号、三单引号和三双引号。其中三引号可以多行定义字符串。

  1. 字符串:可以使用单引号、双引号
#%% 字符串
a = 'apple' # 或者:a = "apple"
print(a)
apple

通常情况下,用单引号或双引号效果一样。选择哪个看个人习惯或团队规范。一个常见的做法是,如果字符串内部包含单引号,外部就用双引号,反之亦然,这样可以避免使用转义字符。” 例如:message = "He said 'Hello!'", reply = 'She replied "Hi!"'

  1. 多行字符串:可以使用三个单引号,或者三个双引号。
a = '''Hello 
Python
'''
print(a)
Hello 
Python

特别的,多行字符串也可以充当多行的注释:只要你不把字符串赋予一个变量, 这串字符串就对你的程序逻辑没什么影响,这就成了另一种注释。

常用于在模块(.py)文件的开头或者函数(后面会说)的开头,但实际上任何地方都可以。

4.4.1 字符串的常用操作

  1. 连接字符串 +
a = 'Hello'
b = 'Python'
print(a + b)
HelloPython

注:可以连加:a + b + c + d

  1. 其他常用操作
a = 'Hello Python'

print('lo' in a) # in: 是否存在

print(a.find('th') )# find():查找位置,从0开始计数,如果找不到会返回 -1。这里会输出 8 。
print(a.find('world')) # 输出 -1 (因为找不到)

print(a.replace('Python','Bob')) # replace():替换

print(a.lower()) # 转为小写:lower()

print(a.upper()) # 转为大写:upper()

print(" apple pie ".strip()) # 去除头尾的不可见字符(包括空格)
True
8
-1
Hello Bob
hello python
HELLO PYTHON
apple pie
  1. 切片:截取字符串的一部分

后面讲列表List会详细介绍

4.4.2 显示特殊字符:转义字符\

  1. “换行\n
print("Hello\nPython")
Hello
Python
  1. 显示反斜杠、单引号、双引号等等

这些字符,本身已经是Python语法的一部分,要放在字符串中显示,需要转义,即在这个符号之前加反斜杠,如你要显示双引号,则可以使用\"

print('反斜杠\\') # 反斜杠 \\
print('\"双引号\"') # 双引号 \'
print('\'单引号\'') # 单引号 \"
反斜杠\
"双引号"
'单引号'

如果一下子看不清楚,应该如何书写:

  1. 作为字符串最外侧的单引号,或者双引号,必须对称
a = ' '
  1. 在单引号,或者双引号内,写入你要的文字
a = 'HelloWorld'
  1. 把转义字符看成一个整体,插入其中,如\n
a = 'Hello\nWorld'
print(a)
Hello
World
  1. 插入斜杠等,也是一样

4.4.3 字符串格式化

我们往往需要把一个变量插入一行字符中,例如我们想显示变量ab的值

#%% 简单加法
a = 1
b = 2
c = a + b
print(a)
print(b)
print(c)
1
2
3

会得到:

1
2
3

但问题是,你只看结果,其实分不清哪个是a,哪个是b,哪个是c。所以,我们更想要的是一句话,如

a的值是: 1
b的值是: 2
c的值是: 3

所以要用到字符串格式化,把变量和字符串混合。

#%% 简单加法
a = 1
b = 2
c = a + b
print('a的值是:{}\nb的值是:{}'.format(a,b))
a的值是:1
b的值是:2

解释一下:

  1. 首先,'a的值是:{}\nb的值是:{}',是一个字符串对象(object),注意两边的单引号。
  2. Str.format(),是字符串类型的一个方法(method),也可以称之为“成员函数”:函数名+小括号。
  3. 一个对象的方法,粗略地理解是:someone.do_something(),某样东西做了一件什么事。
  4. Str.format(),这个方法即一个“字符串格式化了自己”。具体的做法,是把format()的参数,这里是ab按顺序填进原字符串中的大括号{}中。
  5. 注意,我们使用了换行符\n

实际上,把字符串对象赋值给变量,如msg,那么msg就成了字符串类型(或者说指向了一个字符串对象),所以也可以这么做:

msg = 'a的值是:{}\nb的值是:{}'
print(msg.format(a,b))
a的值是:1
b的值是:2

还可以按参数的顺序(第一个元素是0):

msg = 'c的值是:{2}\na的值是:{0}\nb的值是:{1}'
print(msg.format(a,b,c))
c的值是:3
a的值是:1
b的值是:2

还有更简洁的办法”f-string”(需要python3.6版本或以上)

  1. 变量的开头(单引号或者双引号之前),加f,形成f'',即所谓”f-string”。
  2. 这种字符串在打印的时候,python会自动把对应的变量填充进去。
  3. 在中括号里直接填变量名
msg = f'a的值是:{a}\nb的值是:{b}'
print(msg)
a的值是:1
b的值是:2

实际上,你要在中括号里放其他python语句,例如其中做运算,也可以

msg = f'a + b的值是{a + b}'
print(msg)
a + b的值是3

如果只是要显示一个变量的名称,还有更简单的办法(需要Python 3.8或以上)

f{变量名=}

print(f'{a=}')
a=1

简单的数字格式化:变量后加{变量名:格式}

如只显示2位小数:0.2f

pi = 3.1415926535897
print(f"圆周率(保留2位小数)是{pi:0.2f}")
圆周率(保留2位小数)是3.14

如以百分数形式显示:%,保留2位小数是:.2%

z = 0.25
print(f"z是{z:.2%}")
z是25.00%

4.4.4 小练习

  1. 建立2个变量:nameid,分别赋值为你的姓名和学号。打印一个字符串,显示”学号:<你的学号>,姓名:<你的姓名>。”

  2. 圆的半径R=5,计算圆的面积area,并打印这句话,并保留2位小数:“半径为<圆的半径>的圆的面积是<圆的面积>”

4.5 与用户交互:获取输入 input()

如果想要获得用户的输入,那么可以采用 input() 函数。

例1:获取名字并问候

input() 会暂停程序,显示括号里的提示信息(可选),等待用户输入并按回车。用户输入的内容会作为字符串返回。

# 下面这行会等待你输入名字
name = input("请输入你的名字: ") 
# input返回的是字符串,可以直接和字符串 "+" 拼接
print("你好, " + name + "!") 
# 或者使用 f-string (推荐)
print(f"你好, {name}!")

例2:获取出生年份计算大致年龄

关键点:input() 返回的永远是字符串 (str) 类型! 即使用户输入的是数字,它也是字符串形式。如果想用这个输入进行数学计算,必须先转换类型。

birth_year_str = input("请输入你的出生年份: ") 
# input返回的是字符串 'xxxx', 不能直接做减法
# 需要用 int() 将它转换成整数
birth_year_int = int(birth_year_str) 

current_year = 2024 # 假设当前是2024年
age = current_year - birth_year_int

# 使用 f-string 输出结果
print(f"你今年大约 {age} 岁。") 

在这个例子中,如果直接用 birth_year_str 去减,Python会报错 TypeError。我们必须使用 int() 将表示年份的字符串(如 '2003')转换成真正的整数(如 2003),然后才能进行减法运算。

总结:

  • input() 用于从用户获取输入,可以带提示信息。
  • 最重要:它返回的值永远是字符串!
  • 如果需要对用户的输入进行数学运算,通常需要使用 int()float() 进行类型转换。这个问题后面我们还会更详细地讲解。。

4.6 布尔型 (Boolean) 与比较

布尔型(Boolean,常简写为 bool)是编程中表示逻辑真假的数据类型。它非常简单,只有两个可能的值:

  • True (真)
  • False (假)

请特别注意: TrueFalse 的首字母必须大写。

布尔值主要用在哪里呢?它们是程序进行决策的基础。比如,判断一个订单是否满足包邮条件:“如果订单金额大于100元,则包邮”——这里的“订单金额大于100元”就是一个需要判断真假的条件。要创建这些条件,我们最常使用的是比较运算符

4.6.1 产生布尔值:比较运算符

比较运算符用于比较两个值,然后返回一个布尔值 (TrueFalse) 来表示比较结果是否成立。

以下是Python中常用的比较运算符:

  • == : 等于 (判断两边的值是否相等)

    print(100 == 100)   # True,因为 100 等于 100
    print(100 == 99)    # False,因为 100 不等于 99
    print('apple' == 'apple') # True,字符串内容相同
    print('Apple' == 'apple') # False,大小写不同
    True
    False
    True
    False
  • != : 不等于 (判断两边的值是否不相等)

    print(100 != 99)    # True,因为 100 确实不等于 99
    print(100 != 100)   # False,因为 100 等于 100
    True
    False
  • > : 大于

    print(101 > 100)  # True
    print(100 > 100)  # False
    True
    False
  • < : 小于

    print(99 < 100)   # True
    print(100 < 100)  # False
    True
    False
  • >= : 大于等于

    print(100 >= 100) # True
    print(101 >= 100) # True
    print(99 >= 100)  # False
    True
    True
    False
  • <= : 小于等于

    print(100 <= 100) # True
    print(99 <= 100)  # True
    print(101 <= 100) # False
    True
    True
    False

这些比较运算的结果,永远是一个布尔值 (TrueFalse)。

4.6.2 极其重要:== (比较) 与 = (赋值) 的区别

这是初学者最容易犯的错误之一!

  • = (单个等号) 是 赋值运算符。它的作用是把右边的值 赋给 左边的变量。例如:order_amount = 150 是告诉Python,“把 150 这个值存到 order_amount 这个变量里”。
  • == (两个等号) 是 等于比较运算符。它的作用是 判断 两边的值是否相等,并返回 TrueFalse。例如:order_amount == 100 是问Python,“order_amount 变量里的值是不是等于 100?”

千万不要混淆! 在需要判断相等的地方错用了单个等号 = 会导致逻辑错误(有时甚至是 SyntaxError)。

4.6.3 简单布尔运算(逻辑运算)

当我们有多个布尔条件时,可以用逻辑运算符 and, or, not 将它们组合起来:

  1. and:只有当 and 两边的条件True 时,整个表达式的结果才为 True;否则为 False。(可以理解为“并且”)

    order_total = 120  # 订单金额
    is_member = True   # 是否是会员
    
    # 条件:订单金额大于100 并且 是会员,才享受特殊折扣
    qualify_special_discount = (order_total > 100) and (is_member == True) 
    print(f"订单金额 {order_total}, 是会员: {is_member}. 符合特殊折扣? {qualify_special_discount}") # True and True -> True
    
    qualify_example2 = (order_total < 100) and (is_member == True) # 金额不够,即使是会员也不行
    print(f"金额不够但会员? {qualify_example2}") # False and True -> False
    订单金额 120, 是会员: True. 符合特殊折扣? True
    金额不够但会员? False
  2. or:只要 or 两边的条件至少有一个True 时,整个表达式的结果就为 True;只有两边都为 False 时,结果才为 False。(可以理解为“或者”)

    has_coupon = False  # 没有使用优惠券
    points_redeemed = True # 使用了积分兑换
    
    # 条件:使用了优惠券 或者 使用了积分兑换,订单总额可以减免
    order_can_reduce = has_coupon or points_redeemed
    print(f"使用优惠券: {has_coupon}, 使用积分: {points_redeemed}. 订单可减免? {order_can_reduce}") # False or True -> True
    
    order_cannot_reduce = has_coupon or False # 既没用券,也没用积分
    print(f"没用券也没用积分? {order_cannot_reduce}") # False or False -> False
    使用优惠券: False, 使用积分: True. 订单可减免? True
    没用券也没用积分? False
  3. not:对单个布尔值进行取反not True 结果是 Falsenot False 结果是 True

    item_in_stock = True # 商品有库存
    
    # 如果商品不是没有库存 (即,有库存)
    can_purchase = not (item_in_stock == False) # not False -> True
    # 更简洁的写法是直接判断 True
    can_purchase_simple = item_in_stock == True # True
    # 或者更简洁 (因为 item_in_stock 本身就是布尔值)
    can_purchase_simplest = item_in_stock # True
    
    print(f"商品有库存: {item_in_stock}. 可以购买? {can_purchase_simplest}") 
    
    print(f"not True 的结果是: {not True}")   # False
    print(f"not False 的结果是: {not False}")  # True
    商品有库存: True. 可以购买? True
    not True 的结果是: False
    not False 的结果是: True

    (在 not 的例子中,展示了多种等价的判断方式,强调了可以直接使用布尔变量)

4.6.4 运算符优先级与括号

逻辑运算符也有执行的先后顺序:not > and > or。 比较运算符 (>, == 等) 的优先级通常高于逻辑运算符。

但是,为了避免混淆和确保代码按你的意图执行,强烈建议使用括号 () 来明确指定运算顺序! 就像在数学中一样。

例如,在 (order_total > 100) and (is_member == True) 中,括号确保了先进行 >== 的比较,得到两个布尔值后,再进行 and 运算。

4.7 None类型

空值,一切皆非。粗略地可以理解为一个“占位符”,例如一个不返回任何值的函数,以后遇到会再解释。

当你定义了一个变量,但暂时还没想好给它赋什么有意义的值时,可以先赋值为 None

a = None
print(a)
None

4.8 简单类型转换

  1. 字符串和数值

字符串:可以表示词语、句子,可以拼接,组合 数字:可以运算

数字可以拼接吗?字符串可以做运算吗?

先转换一下类型

birth = "2001"

age = 2021 - birth
print(age)
TypeError: unsupported operand type(s) for -: 'int' and 'str'
NameError: name 'age' is not defined

一般而言,类型转换的函数,就是目标类型的名字。

把某个变量(如字符串str)转整型int,用函数int(),转为浮点是float()

birth = "2001"
age = 2021 - int(birth)
print(age)

age = 2021 - float(birth)
print(age)
20
20.0

显然反过来转换也是可以的,把某个变量转为字符串str()

print('Your age is ' + str(age))
Your age is 20.0

需要注意的是,不是所有字符串都能成功转换成数字。比如,int('hello') 就会报错 ValueError,因为 'hello' 无法被理解为整数。只有当字符串的内容确实能表示一个数字时,转换才能成功(例如 int('123')float('3.14'))。

  1. 布尔型

(有逻辑学基础,可以快速过)

特别地,布尔型中,True可视为1False可视为0。 因此我们可以把数字的运算套用在布尔型上。

r red("注意"):要保持代码的清晰性,一般不建议使用布尔型进行运算,除非你很明白自己在做什么。

a = True # True可视为1
print(a + 1)

b = False # False可视为0
print(b - 1)
2
-1

做条件判断的时候,0会判定为False,非0会判断为True,这个我们后面说条件语句的时候会说。

if -1:
  print('hello')
hello

4.9 Python的类型转换和类型错误

Python是一个“动态类型+强类型”语言

  1. 动态类型:

变量名运行时绑定,变量名只是一个可以撕掉和重新粘贴的标签。你为某个变赋值什么类型,这个变量就是什么类型,在运行时可以随你的赋值代码而改变。

  1. 强类型:

一般情况下,Python不会为你自动转换类型(不会“隐式类型转换”)。

如一个很热门的语言JavaScript,大家现在上网看到的多数网站,其页面都是js语言写的。

在js中,一个字符”0”加一个数字1,js会自动(隐式地)把后者转换为字符串,然后进行拼接。

"0" + 1; // "01"

这其实对你的代码质量(如类型的检验)提出了更高要求,比如你的本意可能是要2个数字相加。

但这段代码会在你毫无知觉的情况下,一直运行下去,导致你可能要在无数代码执行过后,才发现问题。

在Python中,则会报错TypeError错误。

'0' + 1
TypeError: must be str, not int

显然,这说的是一个str,只能和另一个str相加(串联),而不能是一个int。 这个时候你应该用“显式”的类型转换。

所以,当你遇到 TypeError 时,意味着你尝试对不同类型的数据做了它们不支持的操作(比如数字和文字相加)。解决方法通常是:检查涉及的变量类型(用 type()),然后使用 int(), float(), str() 等函数进行显式的类型转换,让它们的类型匹配操作的要求。

4.10 本章小结

在本章中,我们学习了构建程序的基本元素:

核心要点回顾:

  • 变量是数据的“标签”,使用 = 赋值来创建和更新。
  • Python是动态类型语言,变量类型随赋值改变,可用 type() 查看。
  • 常见基础类型:
    • int (整数) 和 float (浮点数/小数) 用于数值计算。
    • str (字符串) 用于文本,由引号包围,支持 + 连接和多种方法(如.format(), f-string)。
    • bool (布尔型) 只有 TrueFalse,由比较运算符 (==, !=, >, <, >=, <=)产生,用于逻辑判断。
    • None 代表空值。
  • 比较运算符用于比较值并返回布尔结果 (注意区分 ===)。
  • 逻辑运算符 (and, or, not) 用于组合布尔条件。
  • input() 函数用于获取用户输入,永远返回字符串 (str)
  • 需要进行数学运算时,常常需要用 int()float() 将字符串显式转换为数值类型。
  • Python是强类型语言,通常不会自动转换不兼容的类型。
  • 变量命名需遵守规则(字母/数字/下划线,不以数字开头)并遵循约定(小写下划线 snake_case,有意义)。
  • 代码中的注释 (#) 用于解释代码,提高可读性。
  • 所有标点符号必须是英文

常见错误提示 (本章相关):

  • NameError: 尝试使用一个未定义(或拼写错误)的变量名。
  • TypeError: 尝试对不兼容的数据类型进行操作(如 str + int)。
  • SyntaxError: 代码不符合Python语法规则(如使用了非法变量名、中文符号、忘记引号等)。
  • ValueError: 尝试将一个无法解释为目标类型的字符串进行转换(如 int('hello'))。