📊 阅读时长:18分钟 | 关键词:Python数据类型、Number、String、类型转换、不可变对象、f-string

引言:数据类型是 Python 的地基

如果你问一个有经验的 Python 开发者"新手最容易在哪儿翻车",答案大概率是:数据类型搞混了

我见过太多这样的代码:

age = input("请输入年龄:")   # 用户输入 25
print("明年你" + age + 1 + "岁")   # TypeError! 字符串不能加数字

也见过这样的困惑:

x = "12.5"
print(int(x))   # 为什么报错?明明是个数字啊?

这些问题看起来是"语法错误",但根因都是同一个:没有理解 Python 的数据类型系统

Python 有六种标准数据类型:

分类 包含类型 可变性
数字类型 int(整数)、float(浮点数)、bool(布尔)、complex(复数) 全部不可变
序列类型 str(字符串)、list(列表)、tuple(元组) str/tuple 不可变,list 可变
映射类型 dict(字典) 可变
集合类型 set(集合) 可变

今天这篇文章,我们先把前两个搞定——数字和字符串。它们看似简单,但藏着很多坑。搞懂了它们,你就理解了 Python 最核心的设计哲学:不可变性

一、数字类型(Number):Python 的四种数字

Python 的数字分为四类。日常开发中最常用的是前三种,复数在科学计算中才会遇到。

1.1 整数(int)—— 没有大小限制的数字
a = 789       # 正整数
b = -789      # 负整数
c = 0         # 零
d = 0b1010    # 二进制,等于十进制 10(前缀 0b)
e = 0o17      # 八进制,等于十进制 15(前缀 0o)
f = 0xFF      # 十六进制,等于十进制 255(前缀 0x)

Python 3 的整数有一个非常棒的特性:没有大小限制。理论上你可以表示一个 1000 位的整数,只要内存够用。这一点和 C、Java 等语言完全不同——它们的 int 类型是有上限的(比如 32 位整数的最大值约 21 亿),溢出后会变成负数或者报错。

# Python 3 中这个完全没问题
big_number = 2 ** 1000
print(big_number)
# 输出:1071508607186267320948425049060001810561404811705...
# (一个 302 位的数)

不同进制的表示和转换

进制 前缀 示例 转十进制
二进制 0b 0b1010 int('1010', 2) → 10
八进制 0o 0o17 int('17', 8) → 15
十进制 255
十六进制 0x 0xFF int('FF', 16) → 255

内置函数 bin()oct()hex() 可以将十进制转为对应进制的字符串:

print(bin(10))    # '0b1010'
print(oct(15))    # '0o17'
print(hex(255))   # '0xff'
1.2 浮点数(float)—— 带小数点的数字
a = 7.89       # 常规小数
b = 8.0        # 整数形式的小数
c = 3e4        # 科学计数法,3 × 10⁴ = 30000.0
d = 3E4        # 大小写 e 均可
e = 1.5e-3     # 1.5 × 10⁻³ = 0.0015

⚠️ 浮点数的精度陷阱:这是一个几乎所有编程语言都存在的问题,不是 Python 的 Bug。

print(0.1 + 0.2)        # 0.30000000000000004 ???
print(0.1 + 0.2 == 0.3)  # False !!!

为什么?因为计算机用二进制存储小数,而有些十进制小数在二进制中是无限循环小数——就像 1/3 在十进制中是 0.33333… 一样。0.1 在二进制中是一个无限循环小数,存储时被截断,所以产生了微小的误差。

解决方案:涉及精确小数运算(比如金融计算)时,使用 decimal 模块:

from decimal import Decimal

print(Decimal('0.1') + Decimal('0.2'))   # 0.3  ✓ 精确
print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3'))  # True  ✓

在这里插入图片描述

1.3 布尔型(bool)—— 只有 True 和 False

布尔型是最简单的数据类型,只有两个值:TrueFalse。但它们有一个隐藏的秘密:

print(True == 1)     # True
print(False == 0)    # True
print(True + 1)      # 2  —— True 可以当作 1 参与运算!
print(False + 1)     # 1  —— False 可以当作 0!
print(isinstance(True, int))   # True —— bool 是 int 的子类!

bool 实际上是 int 的子类True 等于 1,False 等于 0。这是 Python 的历史遗留设计,知道就行,日常开发中别这么写——会让人困惑。

布尔运算

# and: 两边都为 True 才返回 True
print(True and True)    # True
print(True and False)   # False

# or: 至少一边为 True 就返回 True
print(True or False)    # True
print(False or False)   # False

# not: 取反
print(not True)         # False
print(not False)        # True

布尔运算在条件判断中大量使用,我们会在后续的"条件语句"文章中详细展开。

1.4 复数(complex)—— 科学计算的利器
a = 3 + 4j     # 实部 3,虚部 4
b = 1j         # 注意:虚部系数不能省略,1j 正确,j 错误
c = 2 + 0j     # 纯实数也可以用复数表示

print(a.real)   # 3.0 —— 获取实部
print(a.imag)   # 4.0 —— 获取虚部
print(a + b)    # (3+5j) —— 复数加法

复数在信号处理、量子计算、电路分析等领域大量使用。日常开发中基本用不到,了解即可。

二、类型转换:Python 的"变形术"

Python 提供了几个内置函数用于类型转换。每个函数都有自己的规则,不遵守规则就会报错。

2.1 int() —— 转为整数
# 从浮点数转换:直接截断,不是四舍五入!
print(int(3.99))       # 3
print(int(-3.99))      # -3

# 从布尔值转换
print(int(True))       # 1
print(int(False))      # 0

# 从字符串转换:字符串必须是"纯整数"形式
print(int('12'))       # 12  ✓
print(int('-12'))      # -12  ✓
print(int(' 12 '))     # 12  ✓(自动去除两端空格)
print(int('12.1'))     # ValueError!  ✗ 不能有小数点
print(int('12.0'))     # ValueError!  ✗ 即使小数部分是 0 也不行
print(int('abc'))      # ValueError!  ✗ 非数字字符

int() 的核心规则:字符串参数必须看起来像一个整数,不能有小数点、不能有字母。如果你确实需要把 '12.1' 转为整数,分两步走:

print(int(float('12.1')))   # 12 —— 先转 float,再转 int
2.2 float() —— 转为浮点数
# 从整数转换
print(float(12))       # 12.0

# 从字符串转换:比 int() 宽容得多
print(float('12'))     # 12.0  ✓
print(float('12.1'))   # 12.1  ✓
print(float('12.0'))   # 12.0  ✓
print(float('3e4'))    # 30000.0  ✓(科学计数法也能识别)

# 从布尔值转换
print(float(True))     # 1.0
print(float(False))    # 0.0
2.3 str() —— 转为字符串
print(str(123))        # '123'
print(str(3.14))       # '3.14'
print(str(True))       # 'True'
print(str([1, 2, 3]))  # '[1, 2, 3]'

str() 几乎可以把任何东西转为字符串,很少出错。

2.4 bool() —— 转为布尔值(这个必须记住!)

这是一个面试高频考点,也是日常开发中容易踩坑的地方。

# ===== 以下全部为 False =====
bool(0)           # 整数 0
bool(0.0)         # 浮点 0
bool(0j)          # 复数 0
bool(False)       # 布尔 False
bool('')          # 空字符串
bool([])          # 空列表
bool(())          # 空元组
bool({})          # 空字典
bool(set())       # 空集合
bool(None)        # None 对象
bool(range(0))    # 空 range

# ===== 以下反而是 True!注意陷阱! =====
bool(' ')         # True —— 空格不是空字符串!
bool('0')         # True —— 这是字符串 '0',不是数字 0
bool('False')     # True —— 这是字符串,不是布尔值!
bool('None')      # True —— 同上
bool([0])         # True —— 列表里有元素(虽然元素是 0)
bool({0})         # True —— 集合里有元素

记忆口诀"空"的东西是 False,"非空"的东西是 True。 但要注意:' '(空格)不空,'None'(字符串)不空。

在这里插入图片描述

2.5 类型转换总结表
转换目标 函数 规则要点 常见错误
整数 int(x) 字符串必须是无小数点的数字 int('12.1') → ValueError
浮点数 float(x) 字符串可以含小数点或科学计数法 精度问题 0.1+0.2≠0.3
字符串 str(x) 几乎万能 很少出错
布尔值 bool(x) 空=假,非空=真 bool(' ') 是 True!

三、type() 和 isinstance():两个类型检查工具

当你不确定一个变量是什么类型时,有两个工具可以用:

3.1 type() —— 查看类型
print(type(123))         # <class 'int'>
print(type(3.14))        # <class 'float'>
print(type('hello'))     # <class 'str'>
print(type(True))        # <class 'bool'>
print(type(None))        # <class 'NoneType'>
print(type([1, 2, 3]))   # <class 'list'>
3.2 isinstance() —— 判断是否属于某类型
x = 123

# type() 方式(不推荐用于判断)
if type(x) == int:
    print("x 是整数")

# isinstance() 方式(推荐!)
if isinstance(x, int):
    print("x 是整数")

# isinstance() 的优势:能识别子类
print(isinstance(True, int))   # True —— bool 是 int 的子类
print(type(True) == int)       # False —— type() 不认子类

推荐使用 isinstance(),因为它能正确处理继承关系。

四、字符串(String):不可变的字符序列

字符串是 Python 中使用频率最高的数据类型之一。学好字符串操作,能解决你 30% 以上的日常编程问题。

4.1 字符串的四种定义方式
s1 = '单行字符串(单引号)'
s2 = "单行字符串(双引号)"
s3 = '''这是多行字符串
可以跨越多行
不需要换行符'''
s4 = """这也是多行字符串
功能和三个单引号一样"""

单引号和双引号在 Python 中没有区别,选择你喜欢的就行。一个实用技巧:当字符串内容包含引号时,用另一种引号包裹可以避免转义:

text1 = "He said 'Hello'"       # 内容含单引号,外层用双引号
text2 = 'He said "Hello"'       # 内容含双引号,外层用单引号
text3 = "He said \"Hello\""     # 或者用转义符 \
4.2 字符串的不可变性 —— 最核心的概念

这是一个会让很多初学者困惑的特性:

s = 'hello'
s[1] = 'a'   # TypeError: 'str' object does not support item assignment

字符串一旦创建,就不能修改其中的某个字符。 这不是 Bug,而是设计特性。不可变对象的好处包括:

  • 安全性:不会在不知情的情况下被修改
  • 可哈希:可以作为字典的键(因为值不会变)
  • 性能优化:Python 可以对不可变对象做内部优化(如字符串驻留)

那如果我想"修改"字符串怎么办?答案是:创建一个新的字符串

s = 'hello'
s = s[:1] + 'a' + s[2:]   # 'hallo'
# 这实际上创建了一个全新的字符串对象,然后让 s 指向它

可以用 id() 函数验证:

s = 'hello'
print(id(s))       # 4378419440(某个内存地址)

s = s + ' world'
print(id(s))       # 4378518832(不同的地址!新对象)
4.3 索引和切片 —— 字符串操作的基本功

索引:用方括号 [] 访问单个字符。索引从 0 开始,负数索引从末尾开始。

s = 'Python'

# 正索引:  0    1    2    3    4    5
# 字符串:  P    y    t    h    o    n
# 负索引: -6   -5   -4   -3   -2   -1

print(s[0])      # 'P' —— 第一个字符
print(s[3])      # 'h' —— 第四个字符
print(s[-1])     # 'n' —— 最后一个字符
print(s[-3])     # 'h' —— 倒数第三个字符
print(s[10])     # IndexError: string index out of range —— 超出范围!

在这里插入图片描述

切片:用 [start:end:step] 获取子字符串。

s = 'Python'

print(s[0:3])     # 'Pyt' —— 索引 0,1,2(左闭右开,不含索引3)
print(s[:3])      # 'Pyt' —— 省略 start,默认从 0 开始
print(s[2:])      # 'thon' —— 省略 end,默认到末尾
print(s[:])       # 'Python' —— 省略两者,获取整个字符串
print(s[::2])     # 'Pto' —— 步长 2,每隔一个取一个
print(s[::-1])    # 'nohtyP' —— 步长 -1,反转字符串!

切片的黄金规则

  • [start:end] 包含 start,不包含 end(左闭右开)
  • start 省略默认为 0,end 省略默认为长度
  • step 省略默认为 1
  • step 为负数时,从右向左取
# 实用切片技巧
s = 'Hello World'

print(s[:5])       # 'Hello' —— 取前5个字符
print(s[-5:])      # 'World' —— 取后5个字符
print(s[::-1])     # 'dlroW olleH' —— 反转字符串
4.4 字符串常用方法大全

字符串有 40 多个内置方法,你不需要全部记住。以下是最常用的,按使用场景分类:

大小写转换

s = 'Hello World'

print(s.upper())        # 'HELLO WORLD' —— 全大写
print(s.lower())        # 'hello world' —— 全小写
print(s.capitalize())   # 'Hello world' —— 首字母大写
print(s.title())        # 'Hello World' —— 每个单词首字母大写
print(s.swapcase())     # 'hELLO wORLD' —— 大小写翻转

去除空白字符

s = '  Hello World  \n'

print(s.strip())         # 'Hello World' —— 去除两端空白
print(s.lstrip())        # 'Hello World  \n' —— 去除左侧空白
print(s.rstrip())        # '  Hello World' —— 去除右侧空白

查找和替换

s = 'Hello World, Hello Python'

print(s.find('Hello'))       # 0 —— 返回第一次出现的位置,找不到返回 -1
print(s.find('Hello', 5))    # 13 —— 从索引 5 开始找
print(s.rfind('Hello'))      # 13 —— 从右边开始找
print(s.index('Hello'))      # 0 —— 和 find 类似,但找不到会报错!
print(s.count('Hello'))      # 2 —— 统计出现次数
print(s.replace('Hello', 'Hi'))       # 'Hi World, Hi Python' —— 全部替换
print(s.replace('Hello', 'Hi', 1))    # 'Hi World, Hello Python' —— 只替换第一个

判断字符串内容

print('123'.isdigit())       # True —— 全是数字
print('abc'.isalpha())       # True —— 全是字母
print('abc123'.isalnum())    # True —— 全是字母或数字
print('  '.isspace())        # True —— 全是空白字符
print('Hello'.startswith('He'))  # True —— 以某字符串开头
print('Hello'.endswith('lo'))    # True —— 以某字符串结尾

拆分和连接

# split() —— 按分隔符拆分,返回列表
s = 'apple,banana,orange'
print(s.split(','))          # ['apple', 'banana', 'orange']

s = 'one  two   three'
print(s.split())             # ['one', 'two', 'three'] —— 默认按空白字符拆分

# join() —— 用分隔符连接列表中的字符串
words = ['Python', 'is', 'awesome']
print(' '.join(words))       # 'Python is awesome'
print('-'.join(words))       # 'Python-is-awesome'
print(', '.join(words))      # 'Python, is, awesome'

split()join() 是一对反操作,在实际开发中使用频率极高。

4.5 字符串格式化:从 % 到 f-string

Python 的字符串格式化经历了三代演变:

第一代:% 格式化(Python 2 风格,不推荐)

name = '小明'
age = 25
print('%s 今年 %d 岁' % (name, age))   # 小明 今年 25 岁

可读性差,类型需要匹配(%s 字符串、%d 整数、%f 浮点数),容易出错。

第二代:str.format()(Python 3 早期,可用但不够简洁)

print('{} 今年 {} 岁'.format(name, age))          # 按位置
print('{0} 今年 {1} 岁'.format(name, age))        # 指定索引
print('{n} 今年 {a} 岁'.format(n=name, a=age))   # 指定名称

比 % 好一点,但还是啰嗦。

第三代:f-string(Python 3.6+,推荐!)

print(f'{name} 今年 {age} 岁')          # 小明 今年 25 岁
print(f'{name} 明年 {age + 1} 岁')      # 小明 明年 26 岁 —— 支持表达式!
print(f'π 约等于 {3.1415926:.2f}')       # π 约等于 3.14 —— 支持格式控制!

f-string 是目前最好的字符串格式化方式,简洁、直观、支持表达式。请忘记 % 和 .format(),直接用 f-string。

格式化方式 语法 可读性 性能 推荐度
% 格式化 '%s %d' % (a, b) ★★☆☆☆ ★★★☆☆ ❌ 不推荐
.format() '{} {}'.format(a, b) ★★★☆☆ ★★★☆☆ ⚠️ 可用但过时
f-string f'{a} {b}' ★★★★★ ★★★★★ ✅ 强烈推荐
4.6 转义字符速查表

当字符串中需要包含特殊字符时,使用反斜杠 \ 转义:

转义序列 含义 示例 输出
\\ 反斜杠本身 print('C:\\Users') C:\Users
\' 单引号 print('It\'s OK') It's OK
\" 双引号 print("He said \"Hi\"") He said "Hi"
\n 换行 print('第一行\n第二行') 两行
\t 制表符(Tab) print('A\tB') A B
\r 回车

如果你不想要转义(比如 Windows 文件路径),在字符串前加 r 创建原始字符串:

print('C:\new\folder')     # C:(换行)ew\folder —— \n 被当成了换行!
print(r'C:\new\folder')    # C:\new\folder —— 原始字符串,不转义

五、动手练习:巩固你的理解

光看不练是学不会编程的。下面几个练习题,建议你在 PyCharm 中亲自敲一遍。

练习 1:类型转换实战

# 用户输入年龄,计算出生年份
age_str = input("请输入你的年龄:")       # 用户输入 "25"
age = int(age_str)                        # 转为整数
current_year = 2026
birth_year = current_year - age
print(f'你大概出生于 {birth_year} 年')

# 思考:如果不做 int() 转换会怎样?试试看。

练习 2:字符串切片

s = 'Python编程很有趣'

# 请写出获取以下内容的切片表达式:
# 1. 前6个字符 → s[:6]
# 2. 后4个字符 → s[-4:]
# 3. 反转整个字符串 → s[::-1]
# 4. 每隔一个字符取一个 → s[::2]

练习 3:字符串方法综合

email = '  User@Example.com  '

# 请用字符串方法做以下处理:
email = email.strip().lower()    # 去空格 + 转小写 → 'user@example.com'
print(email)

# 判断是否是 Gmail 邮箱
print(email.endswith('@gmail.com'))   # False

# 提取用户名(@ 前面的部分)
username = email.split('@')[0]
print(username)    # 'user'

小结

今天这篇文章我们搞定了 Python 六大数据类型中的前两个——数字和字符串。信息量不小,我们来总结一下核心要点:

类型 子类型 可变性 最需要记住的事
数字 int, float, bool, complex 不可变 int('12.1') 报错;boolint 子类;浮点数有精度问题
字符串 str 不可变 索引从 0 开始;切片左闭右开;用 f-string 格式化;split()join() 是最常用的方法

记住一个贯穿全文的词:不可变性。数字和字符串一旦创建就不能修改——任何"修改"操作实际上都是在创建新对象。理解了这一点,你就理解了 Python 数据类型设计的底层哲学。

下一篇文章,我们将进入 Python 里最常用的四个容器类型:列表、元组、字典、集合——它们才是日常编程的主力军,也是"可变性"概念真正发挥威力的地方。


本文是「Python从入门到数据分析」系列的第 2 篇,共 24 篇。关注我,不错过后续更新。

更多推荐