变量与基本数据类型

变量是编程的第一块砖。写程序其实就是”用变量装数据,让数据流动”的过程。上一篇 第一个 Python 程序 里我们已经悄悄用过变量了(name = "咖飞")。这一篇我们要正式讲清楚:Python 的变量到底是什么?有哪些基本数据类型?为什么 Python 是”动态类型”?以及一堆新手容易翻车的细节。学完这一篇,你写的代码就能装东西了。

一、变量到底是什么

在 C 语言里,变量是”一块贴了标签的内存”;在 Python 里,思维要反过来——变量是贴在对象上的标签

1
name = "咖飞"

这一行代码干了三件事:

  1. 内存里创建了一个字符串对象 "咖飞"
  2. 定义了一个叫 name 的名字;
  3. name 指向那个对象。

所以 Python 的赋值不是”把值放进变量”,而是”把标签贴到对象上”。这个模型很重要,理解它可以省掉后面 80% 的困惑。

来看一段能证明这个观点的代码:

1
2
3
4
a = [1, 2, 3]
b = a # b 和 a 贴在同一个列表对象上
b.append(4)
print(a) # 输出 [1, 2, 3, 4],因为 a 和 b 指的是同一个对象

如果按”变量是盒子”的模型,你会觉得 b = a 是复制一份,改 b 不影响 a。但按”变量是标签”的模型,就一目了然:两个标签贴在同一件行李上,动一个另一个当然跟着变。

二、变量命名规则与惯例

Python 变量命名规则:

  • 由字母、数字、下划线组成;
  • 不能以数字开头;
  • 不能与关键字冲突(如 ifforclassTrue 等);
  • 区分大小写ageAge 是两个不同的变量;
  • 支持 Unicode,名字 = "咖飞" 也合法,但不推荐。

命名风格惯例(PEP 8):

  • 普通变量、函数:snake_case(小写下划线),如 user_name
  • 常量:UPPER_CASE,如 MAX_RETRY
  • 类名:PascalCase,如 UserAccount
  • “私有”变量:前缀单下划线,如 _internal
  • 避免用单字符(除了 ij 之类循环变量);
  • 变量名要有意义——d = 86400 不如 seconds_per_day = 86400
1
2
3
4
5
6
7
8
9
# 好
user_age = 18
MAX_RETRY = 3
class UserAccount: ...

# 不好
a = 18
x1 = 3
class user_account: ...

三、基本数据类型总览

Python 内置基本类型有 6 大类

类型 关键字 举例 说明
整数 int 42, -7, 0, 10_000 任意精度整数
浮点数 float 3.14, 1e5, -0.5 双精度浮点
字符串 str "hi", '咖飞', """...""" 不可变的字符序列
布尔 bool True, False int 的子类
空值 None None 表示”没有值”
复数 complex 1+2j 一般用不到,本文只提一下

先只讲前 5 个,复数遇到再说。

四、int:整数

Python 3 的整数是”任意精度”,不用担心溢出:

1
2
3
big = 2 ** 128
print(big) # 340282366920938463463374607431768211456
print(type(big)) # <class 'int'>

支持不同进制字面量:

1
2
3
4
5
dec = 255         # 十进制
hex_num = 0xFF # 十六进制
oct_num = 0o377 # 八进制
bin_num = 0b1111_1111 # 二进制
print(dec, hex_num, oct_num, bin_num) # 全部输出 255

Python 3.6+ 允许下划线做视觉分隔:

1
2
population = 1_400_000_000   # 十四亿
print(population) # 1400000000,下划线只是给人看的

五、float:浮点数

浮点数用双精度 IEEE 754,注意精度问题:

1
2
print(0.1 + 0.2)             # 0.30000000000000004,不是 0.3!
print(0.1 + 0.2 == 0.3) # False

这不是 Python 的 bug,而是所有基于 IEEE 754 的语言的通病。结论:不要用 == 比较浮点数。要用容差:

1
2
import math
math.isclose(0.1 + 0.2, 0.3) # True

或者对财务/金额场景,用 decimal 模块:

1
2
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 0.3

科学计数法也可以:

1
2
c = 3e8        # 3 * 10^8,光速
print(c) # 300000000.0

六、str:字符串

字符串是 Python 里最常用的类型之一:

1
2
3
4
5
6
7
8
9
s1 = "双引号"
s2 = '单引号'
s3 = """三引号
可以跨行
"""
s4 = '''三个单引号,也可以'''

print(len(s1)) # 3,字符个数
print(s3.strip()) # 去掉首尾空白

字符串支持”加法”(拼接)和”乘法”(重复):

1
2
print("ab" + "cd")      # abcd
print("=" * 20) # ====================

字符串是不可变的——你不能修改一个已有字符串的某一位:

1
2
3
s = "hello"
s[0] = "H" # TypeError: 'str' object does not support item assignment
s = "H" + s[1:] # 正确做法:造一个新字符串

关于字符串还有更多内容,我们会在 字符串基础与常用方法 单独讲。

七、bool:布尔

TrueFalse,注意首字母大写。它们其实是 int 的子类:

1
2
3
print(True + True)     # 2,True 相当于 1
print(False * 100) # 0
print(isinstance(True, int)) # True

Python 里的真值判断很宽松,以下都被视为”假”:

  • False
  • None
  • 00.00j
  • 空字符串 ""、空列表 []、空字典 {}、空集合 set()、空元组 ()

其他一切都是”真”。所以:

1
2
3
4
5
6
if items:               # items 非空就为真,简洁又 Pythonic
print("非空")

# 不推荐这样写:
if len(items) > 0: # 冗长
print("非空")

八、None:什么都不是

None 表示”没有值”,函数没有 return 时默认返回 None

1
2
3
4
5
6
def do_nothing():
pass

result = do_nothing()
print(result) # None
print(result is None) # True

判断 Noneis,不要用 ==is 判断”是不是同一个对象”,None 全局只有一个;== 是”值相等”,可能被自定义类重写而产生意外行为。

九、类型转换

Python 支持显式类型转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 数字与字符串
s = "42"
n = int(s) # 42
f = float(s) # 42.0
str_n = str(3.14) # "3.14"

# 布尔与数字
print(int(True)) # 1
print(bool(0)) # False
print(bool("")) # False
print(bool("False")) # True,因为字符串非空即真!

# 二进制、十六进制字符串
print(int("ff", 16)) # 255
print(int("1010", 2)) # 10
print(hex(255)) # '0xff'
print(bin(10)) # '0b1010'

陷阱int("3.14") 会报错,因为 "3.14" 不是合法整数字符串。要先转成 float 再转 int:int(float("3.14")) 得到 3

十、typeisinstance

想知道一个变量是什么类型:

1
2
3
4
x = 42
print(type(x)) # <class 'int'>
print(type(x) == int) # True
print(isinstance(x, int)) # True,推荐用这个

isinstancetype == ... 更好,因为它支持继承关系(比如 isinstance(True, int)True)。

十一、动态类型 vs 静态类型

Python 是动态类型语言:

1
2
3
x = 1          # 现在 x 是 int
x = "hello" # 现在 x 是 str
x = [1, 2, 3] # 现在 x 是 list

这三行代码都合法。同一个变量名可以贴到不同类型的对象上,非常灵活,但也带来风险:代码一多,你不知道某个变量此刻到底是什么类型。因此 Python 3.5+ 引入了 类型注解

1
2
name: str = "咖飞"
age: int = 3

注解不改变运行时行为(Python 不会真的检查类型),只是给 IDE 和静态检查工具 mypy 看的。养成写类型注解的习惯,能省下大量调试时间。

十二、常见陷阱

陷阱 1:可变默认参数(本篇预告)

1
2
3
def add_item(item, items=[]):    # 危险!
items.append(item)
return items

items=[] 只在函数定义时执行一次,多次调用会共享同一个列表。这个坑我们在 参数详解 里会详细讲。

陷阱 2:is== 分不清

1
2
3
4
5
6
7
a = 256
b = 256
print(a is b) # True,因为小整数被 CPython 缓存了

a = 257
b = 257
print(a is b) # 可能是 False!CPython 只缓存 -5 到 256

结论:判断值相等永远用 ==,判断”是不是同一个对象”才用 is(一般只用于 is None)。

陷阱 3:数字前导 0

在 Python 3 里 x = 007 会报语法错误,因为过去八进制字面量是 0 开头,容易和十进制混淆,现在必须写 0o7

陷阱 4:整数除法

1
2
3
print(5 / 2)     # 2.5(float 结果)
print(5 // 2) # 2(整除)
print(5 % 2) # 1(取余)

Python 2 的 / 是整除,Python 3 改成了浮点除法。所有网上 Python 2 时代的代码到 3 里都要小心这一条。

陷阱 5:字符串数字直接相加

1
2
3
n = input("年龄:")
print(n + 1) # TypeError!n 是字符串
print(int(n) + 1) # 正确

input() 永远返回字符串。

十三、小结与延伸阅读

  • 变量是”贴在对象上的标签”,不是”存放值的盒子”;
  • 命名遵循 PEP 8:snake_case、UPPER_CASE、PascalCase;
  • 6 大基本类型:int、float、str、bool、None、complex;
  • Python 是动态类型,但配合 type hints 可以获得静态检查;
  • 浮点数不精确,0.1 + 0.2 != 0.3
  • is 判断对象身份,== 判断值相等,除非判 None 否则用 ==
  • input() 返回字符串,需要手动转类型。

延伸阅读:

下一篇 运算符与表达式 我们要用这些变量做点计算了。