拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python数据类型全解析:从类型判断到强制转换与pandas实践

如果你刚开始学 Python最应该先掌握的其实不是某个框架而是一套“类型意识”。原因很简单你写出来的报错、排查、改数据绕来绕去基本都落在数据类型上。TypeError: can only concatenate str (not int) to str、ValueError: invalid literal for int()、TypeError: int object is not iterable这些提示都是程序在告诉你变量对应的真实类型和你以为的类型不一致。写爬虫、做数据分析、写量化策略、调接口第一次翻车通常就是在数据类型这边。这里要先纠正一个常见认知Python 是动态语言但“动态”不代表“没有类型”。每个对象在运行时都有自己的真实类型变量只是一个名字。名字本身没有类型限制但名字指向的对象有。如果你把字符串当成数字去运算把列表当成字典去取值程序不会按你的主观意愿执行只会抛出类型相关异常。所以学习 Python 基础语法的第一阶段目标就是搞懂有哪些内置类型、哪些类型可变、怎么判断类型、怎么安全转换类型。这篇文章将要完成的内容很具体先准备 Python 运行环境然后逐个看int、float、str、bool、list、tuple、dict、set、NoneType的类型特征再讲清type和isinstance的区别给出完整的强制转换示例最后落到常见报错、pandas 数据类型转换、批量接口数据清洗和量化场景中的数值精度问题。你可以直接按顺序跑代码也可以把本文当手册遇到类型问题回来查。建议收藏备用。1. Python 数据类型知识点速览先把最关键的信息放在前面方便快速判断你有没有必要继续看。知识点结论内置类型int、float、complex、str、bool、list、tuple、dict、set、frozenset、bytes、NoneType可变类型list、dict、set、bytearray以及自定义对象不可变类型int、float、complex、str、bool、tuple、frozenset、bytes、NoneType类型判断推荐isinstance()需要精确判断时再用type()强制转换int()、float()、str()、list()、tuple()、dict()、set()布尔陷阱bool 是 int 的子类True 本质是 1False 本质是 0函数返回值没有 return 时返回 None真实类型是 NoneType接口返回字段JSON 里的数字可能变成字符串空值可能变成 None不能直接参与运算Pandas 类型用 df.dtypes 查看用 astype、to_numeric、to_datetime 转换批量使用场景对每条记录做安全转换避免直接 int(input) 导致中断这张表解决的是“你要不要继续读”的问题。如果你的日常工作是处理 API 返回、清洗 CSV/Excel、拆分字段、把字符串变成数字表里的前几行已经点到了重点。后文会逐步展开每一类具体内容并给出可直接执行的代码。还需要提前说明一个高频误判很多初学者用type(x) int判断整数这在某些场景会出错。因为bool类型是int的子类True在数值比较中等同于 1所以type(True) bool是 True但type(True) int是 False而isinstance(True, int)是 True。如果你的逻辑里把“真值”当成整数去处理可能会得到看起来诡异的结果。在第 5 章会专门展开。2. Python 本地开发环境准备2.1 安装 Python 与配置 PATH如果你还没有 Python 环境先去 Python 官网下载安装包。新项目建议安装较新的稳定版本但不必追求最新大版本当前比较稳妥的选择是 Python 3.10 到 3.12。Windows 安装时需要注意安装向导第一页底部有 “Add Python to PATH” 勾选项必须勾上否则后面在终端里执行python命令会提示找不到命令。安装完成后打开 PowerShell、CMD 或 Linux/macOS 的终端执行下面的验证命令python --version pip --version python -c print(type(3))如果能看到类似Python 3.12.x、pip 24.x的输出说明 Python 已经进入 PATH。第三条命令会输出class int这说明3这个对象的真实类型是int。这一步对后续理解非常关键即使是在终端里写的一个数字字面量也有确定的类型。如果python --version提示“不是内部或外部命令”优先检查环境变量。Windows 可以在“系统属性 - 高级 - 环境变量”里找到 Path把 Python 安装目录和Scripts子目录加进去。很多环境问题不是代码写错而是解释器路径没配好。2.2 用 VSCode 配置 Python 调试环境写 Python 调试类型问题我建议直接使用 Visual Studio Code。安装 Python 扩展后按CtrlShiftP选择 “Python: Select Interpreter”指向刚才安装的 Python 版本。这样编辑器和终端会使用同一个解释器避免出现“这里能运行那里不能运行”的混乱。在交互式环境里可以直接观察到类型问题。比如下面这个简单例子python -c a [1, 2]; print(a[1] * 2)这段代码会输出22而不是你可能期待的4。原因就是a[1]是字符串2字符串乘以整数 2 的结果是22。如果换成本地 Python 脚本输出依然如此。这种“代码不报错但结果完全不对”的情况往往比显式报错更难排查根源就在类型没有提前确认。VSCode 的调试面板能逐步观察变量。把光标放在变量上会显示当前值的类型进入调试模式后可以在“变量”窗口里看到a是List[str | int]、a[1]是str。做 Python 类型学习阶段这种可视化方式比猜变量类型要高效得多。3. Python 基础数据类型详解3.1 数字类型int、float、complex整数int在 Python 中没有传统语言那样的 32 位或 64 位上限理论上可以表示非常大的整数只要内存放得下。做过大数据量题目的人通常会觉得这一特性很舒服因为不用担心2**63溢出。但需要注意除法/永远返回float即使两个整数能够整除。如果想得到整数结果要使用整除//取余数使用%。a 10 b 3 print(a / b) # 3.3333333333333335 print(a // b) # 3 print(a % b) # 1 print(type(2 ** 63)) # class int浮点数float遵循 IEEE 754 表示适合大多数科学计算和日常数值处理但不适合金额、精确度量等场景。0.1 0.2不会等于0.3这在 Python 中也是常态。要避免精度坑可以使用decimal.Decimal或者把金额换算成最小单位后使用整数运算。复杂数值类型complex就是复数例如1 2j主要用于数学、信号处理方向普通开发场景使用频率很低。当你在接口返回或 CSV 文件里看到3这样的内容它只是长得像数字真实类型是str。把字符串参与算术运算前必须先做类型转换。3.2 字符串str字符串是不可变对象所有看起来“修改字符串”的操作实际上都是创建了新字符串。单引号、双引号和三引号都可以定义字符串三引号经常用于跨行文本或文档字符串。Python 没有单独的字符类型通过索引从字符串中取出的每一个元素仍然是长度为 1 的字符串。name Python print(name.upper()) # PYTHON print(name.replace(Py, 数据)) # 数据类型 print(type(100)) # class str print(100 * 3) # 100100100这里最容易被忽视的是input()函数的返回值。用户输入的任何内容哪怕看起来是数字都会被读成字符串。做一个小程序时如果你直接拿input()的返回值去做加法得到的结果通常不是数学上的求和而是字符串拼接。因此凡是外部输入、文件读取、接口返回的“数字字段”必须先验证真实类型再决定是否转换。3.3 布尔类型bool布尔类型只有两个值True和False。从 Python 内部实现看bool是int的子类所以True其实就是 1False其实就是 0。这种设计让布尔值能参与算术运算但也会带来隐蔽问题。print(True 1) # 2 print(isinstance(True, int)) # True print(bool()) # False print(bool(False)) # Truebool(False)的结果是 True因为一个非空字符串在布尔判断中总是真字符串内容是什么并不重要。这个行为经常被忽略如果接口返回False字符串你直接把它当成逻辑假来用程序会走进错误分支。正确做法是先做字符串判断例如value False或value.lower() false再转换为布尔值。3.4 列表list列表是 Python 中最常用的可变序列。它可以存放不同类型的对象例如[1, 2, 3.0, True]但这种“随意”会带来后续处理压力遍历时必须逐项判断类型。列表支持索引、切片、追加、插入、删除、排序等操作使用方法比较丰富。items [1, 2, 3.0] items.append(4) print(items) # [1, 2, 3.0, 4] print(items[1] * 3) # 222上面第二行输出为222因为items[1]是字符串。同一个列表里可能有多种类型直接做运算前必须先确认数据类型。列表中存放的是对象的引用而不是对象的拷贝。如果你用b a复制列表再通过b.append()添加内容原列表a也会改变。需要真正独立副本时使用list.copy()或copy.deepcopy(a)。3.5 元组tuple元组与列表最大的区别在于不可变。元组一旦创建不能添加、删除或替换元素这种特性适合表示结构固定的一组数据例如坐标(x, y)、RGB 颜色值、函数返回的多个结果。元组可以作为字典的键而列表不可以。point (10, 20) x, y point print(x, y) # 10 20 print(type(point)) # class tuple元组的“不可变”是指元组这个容器不能增删元素不代表元组内部的对象不能变化。如果一个元组中包含列表例如data (1, [2, 3])执行data[1].append(4)是可以成功的。因为列表本身是可变的元组只是保存了列表的引用并没有限制列表内容变化。理解这一点能避免很多后续困惑。3.6 字典dict字典存储键值对键必须唯一值可以是任意类型。Python 3.7 之后字典保持插入顺序这使得按顺序遍历输出变得可预期。字典的键必须是不可变对象最常用的是字符串和整数也可以是元组列表和字典不能作为键否则会抛出TypeError: unhashable type。user {name: 张三, age: 18} user[age] int(user[age]) print(user)接口返回的 JSON 对象在 Python 中通常被解析为字典字段值可能是字符串、数字、布尔、列表、嵌套字典或 None。处理这种字典时最稳妥的操作是先取出字段再确认类型最后决定是否转换。不要假设user[age]一定是整数有些接口会把年龄、金额、状态码全部序列化为字符串。你需要在转换前做兼容处理否则一条脏数据就能让整个脚本中断。3.7 集合set集合用于存放不重复的不可变对象天然支持交集、并集、差集等运算。对一个列表去重时使用set()非常方便但列表里的元素必须是可哈希的。如果列表中包含字典或另一个列表直接转 set 会报错。values [1, 2, 2, 3, 3, 3] unique set(values) print(unique) # {1, 2, 3} print(type(unique)) # class set集合的输出顺序不保证与插入顺序一致因为它底层基于哈希表。若想保留去重后的顺序可以改用循环和辅助集合来维护顺序。比如遍历原列表第一次出现的元素才加入新列表这样既去重又保持了原顺序。集合中的元素也要求不可变如果想把集合放入另一个集合作为元素需要先转成frozenset。3.8 NoneType 与 NoneNone是 Python 中表示“空”或“没有值”的唯一对象它的类型是NoneType。函数如果没有写return默认返回None。接口字段缺失、数据库查询无结果、变量未被赋值等情况也经常出现None。def nothing(): pass result nothing() print(result) # None print(type(result)) # class NoneTypeNone和空字符串、数字 0、空列表[]都不一样。在布尔判断中它们都为假但在业务含义上完全不同。比如接口返回订单金额None可能表示没有填写0.0可能表示免单可能表示字段为空字符串。如果你统一用if not value去过滤会把这三种情况全部丢掉造成严重的数据丢失。正确做法是先判断value is None再分别处理其他空值。4. 可变与不可变对象理解可变和不可变是 Python 数据类型中最关键的进阶点。它直接决定函数参数传递后的行为。不可变对象包括整数、浮点数、字符串、元组等可变对象包括列表、字典、集合等。先看下面的对比def add_one(x): x 1 a 1 add_one(a) print(a) # 1 def append_one(items): items.append(1) my_list [] append_one(my_list) print(my_list) # [1]第一个例子中整数不可变。传入函数的x实际上是原值1的另一个引用函数内x 1创建了新对象并让局部变量指向新对象外部a没有被影响。第二个例子中列表可变。函数内通过items.append(1)直接修改了列表对象本身外部my_list也会看到这个变化。这个差异在实际项目里非常重要。如果你不希望外部数据被修改传列表、字典进函数前要做副本如果你希望函数能够更新某个大对象直接传可变对象就能生效。另一个高风险点是函数默认参数。不要写成def func(data[])因为默认参数在函数定义时只会创建一次多次调用会共享同一个列表。应该写成def func(dataNone)在函数内部再判断“如果 data 是 None初始化为空列表”。5. 类型判断type 与 isinstance判断一个变量属于什么类型有两个常用函数type()和isinstance()。新手经常只使用type()但在做父类、子类判断时容易出问题。value True print(type(value)) # class bool print(type(value) bool) # True print(type(value) int) # False print(isinstance(value, int)) # True由于bool是int的子类isinstance(True, int)返回 True。如果你只想接受“真正的整数”而不包含布尔值可以加一个前置判断def is_pure_int(value): return isinstance(value, int) and not isinstance(value, bool)如果要判断一个值是否属于多个类型中的任意一个可以给isinstance传元组。例如def is_number(value): return isinstance(value, (int, float)) and not isinstance(value, bool)type()更多用于调试和日志输出或者在需要精确判断当前实际类型时使用。大部分业务代码应该优先使用isinstance()因为它支持继承关系。比如自定义类继承int后isinstance依然能识别。类型判断不应该写满整个代码更合理的做法是在数据入口做一次校验之后的数据处理按已经确认的类型进行。6. 数据类型强制转换与常见陷阱6.1 字符串与数值的转换把字符串转换为整数、浮点数是数据处理中出现频率最高的操作。int()只能解析格式规范的整数字符串比如42、-7不能直接解析带小数点的字符串。int(3.99)会返回 3这是向零取整不是四舍五入。print(int(42)) print(int(3.99)) print(int(-7))如果一个字符串是3.14直接执行int(3.14)会抛出ValueError因为整数转换不允许小数格式。可先把字符串转float()再取整price 3.14 value int(float(price)) print(value) # 3float()可以解析3.14、1e3等合法浮点字符串但遇到、abc时同样会报错。在批量处理外部数据时强烈不建议直接调用int(input())或int(row[age])因为任意一条非法数据都会让整个程序中断。应该先封装一个安全转换函数失败时返回默认值或记录错误。6.2 容器类型转换list()、tuple()、dict()、set()可以用来创建或转换容器类型。把字符串转列表会把每个字符拆开把元组转列表会得到一个新列表把字典转列表默认取键把列表转字典要求每个元素都是可迭代的并且每个子元素长度恰好是 2。print(list(hello)) # [h, e, l, l, o] print(tuple([1, 2, 3])) # (1, 2, 3) print(dict([(name, Tom), (age, 20)])) # {name: Tom, age: 20} print(set([1, 2, 2, 3])) # {1, 2, 3}需要注意这些转换操作不会修改原对象而是返回一个新对象。如果你执行a list(a)实际上是创建了新的列表并重新赋值给变量。容器内部的元素本身没有改变转换只是换了外层容器类型。列表转集合后元素顺序可能变化因为集合不保证顺序。6.3 安全转换函数模板在处理 API 返回、Excel 导入、爬虫抓取的数据时字段类型非常不统一。写一个safe_int和safe_float能让批量处理稳定得多def safe_int(value, default0): if value is None or isinstance(value, bool): return default try: return int(value) except (ValueError, TypeError): return default def safe_float(value, default0.0): if value is None or isinstance(value, bool): return default try: return float(value) except (ValueError, TypeError): return default这个模板的含义很明确空值返回默认值布尔值不参与数值转换其他值尝试转换失败也返回默认值。它不会告诉你哪一行数据出了问题适合在对数据完整性要求不高的场景快速清洗。如果你需要保留错误信息可以让safe_int返回一个标记或者把异常记录到日志里避免静默吞掉错误导致后续分析结果失真。bool()的转换也很有陷阱。直接bool(False)返回 True因为非空字符串是真。在把接口里的true、false、1、0转成布尔值时最好用显式判断def safe_bool(value, defaultFalse): if isinstance(value, bool): return value if value in (1, 1, true, True, yes): return True if value in (0, 0, false, False, no): return False return default7. 数据处理中的 Pandas 类型转换7.1 查看 DataFrame 的列类型如果你用 pandas 处理表格数据列类型会比 Python 基础类型更多一层概念。pandas 里常见的 dtype 有object、int64、float64、bool、datetime64[ns]和category。object本质上就是字符串或混合类型最常出现在 Excel/CSV 导入后的列中。import pandas as pd df pd.DataFrame({ price: [10.5, 20, NA], count: [1, 2, 3], date: [2024-01-01, 2024-01-02, 2024-01-03], }) print(df.dtypes) print(df.info())从df.info()的输出可以看到每列有多少个非空值以及内存占用。很多加工后的表格出现“明明看起来是数字却不能求平均”的问题原因就是列类型是object。处理第一步不是直接计算而是先看 dtype再决定是否调用pd.to_numeric。7.2 astype 与 to_numeric 的配合astype()是 pandas 中最常见的类型转换方法但它不适合把包含非数字内容的列强制转换为数值因为非数字内容会触发错误。遇到NA、这类缺失值推荐使用pd.to_numeric(..., errorscoerce)。coerce会把无法解析的内容变成NaN然后你就能用dropna()或fillna()来处理空值。df[price_num] pd.to_numeric(df[price], errorscoerce) print(df[price_num]) df[price_num].fillna(0, inplaceTrue) df[count_str] df[count].astype(str)如果你想把整数列转成字符串可以直接astype(str)。但把一个含有NaN的浮点列转成整数会报错因为整数类型不能表示缺失值。正确做法是先填充缺失值再转整数df[count_clean] df[count].fillna(0).astype(int)批量清洗多列时可以写一个循环把指定列统一转成数值for col in [price, amount, tax]: if col in df.columns: df[col] pd.to_numeric(df[col], errorscoerce)这样即使其中一列全部是文本程序也不会中断。要注意errorscoerce会把非法字符串变成 NaN所以清洗后一定要检查数据损失情况不要只看最终结果。7.3 日期和分类类型转换日期字符串转 datetime 类型是数据分析里另一个高频场景。pd.to_datetime()能识别常见的日期格式df[date] pd.to_datetime(df[date], errorscoerce) print(df[date].dt.year)转换成功后就可以用.dt访问器提取年、月、日、星期等信息。errorscoerce同样会把无法解析的日期变成NaT。如果你的日期格式不是标准 ISO 格式可以先查看原始数据再手动指定format例如format%Y/%m/%d这样解析效率更高也不容易误判。对于取值种类很少的列比如性别、地区、产品类别可以转成category类型减少内存占用df[region] df[region].astype(category)分类类型在某些分组和聚合场景下速度更快但在和外部系统交互时如果 pandas 版本不同分类类型可能会带来兼容性问题。因此导出到 Excel 或 CSV 前通常还应该转回普通字符串。8. 常见数据类型报错与排查方法数据类型问题最常见的表现不是逻辑错误而是运行时异常。这里列出一张高频排错表遇到类似报错时可以直接对照。问题现象可能原因排查方式解决方案TypeError: can only concatenate str (not int) to str字符串和数字用 连接打印变量和取类型将数字转成 str或把字符串转成数字ValueError: invalid literal for int() with base 10int() 收到非数字字符串检查字段内容和是不是 None先 float() 再 int或使用 safe_intTypeError: int object is not iterable对整数使用 for 循环查看输入是 int 还是 list包装为列表或修正数据结构TypeError: list object is not callable变量曾经命名为 list覆盖了内置类型查找变量定义重命名变量不要在模块顶层叫 list 或 dictTypeError: unhashable type: list列表放入集合或作为字典键查看被放入对象的类型改为元组或使用 frozensetAttributeError: NoneType object has no attribute xxx函数没有 return 或接口返回 None检查函数边界打印返回值判断 return 结果是否 None 后再操作PandasValueError: cannot convert float NaN to integer含 NaN 的列直接 astype(int)查看 df.isna() 的结果fillna 后再转整数启动 python 命令找不到Python 未加入 PATHwhere python或检查系统变量重新安装并勾选 Add Python to PATH运行时异常只是表面现象。排查思路应该是先打印类型再打印值和长度然后检查数据源。不要直接靠猜测修改代码。对于外部数据建议在进入核心逻辑前做一次完整的数据类型检查把异常数据集中记下来而不是让程序在第一条脏数据上崩溃。9. 批量任务与 API 场景中的数据类型处理9.1 清理接口返回的字段接口返回的 JSON 字段经常不稳定。比如某条记录里id是整数另一条记录里id是字符串某条记录price是浮点数另一条price是空字符串。直接用原始字段参与运算很容易触发异常。可以写一个通用处理函数import requests def safe_int(value, default0): if value is None or isinstance(value, bool): return default try: return int(value) except (ValueError, TypeError): return default def safe_float(value, default0.0): if value is None or isinstance(value, bool): return default try: return float(value) except (ValueError, TypeError): return default rows [ {id: 101, name: 显卡, price: 2999.00}, {id: None, name: CPU, price: NotNumber}, {id: 103, name: 内存, price: 520}, ] cleaned [ { id: safe_int(item.get(id)), name: item.get(name), price: safe_float(item.get(price)), } for item in rows ] print(cleaned)如果换成真实接口通常会在一个列表推导式或 for 循环中对多条记录做相同处理。批量任务前建议先跑 3 到 5 条数据验证输出格式再放开处理全部数据。这样做的好处是不会因为某条脏数据导致整个批量任务跑了一个小时后才中断。9.2 批量循环中的失败重试与日志批量处理大量数据时类型转换失败并不可怕可怕的是没有日志。可以在safe_int或safe_float中增加错误记录import logging logging.basicConfig(levellogging.WARNING) def safe_float_with_log(value, default0.0): if value is None or isinstance(value, bool): return default try: return float(value) except (ValueError, TypeError) as e: logging.warning(转换失败: value%r, error%s, value, e) return default如果调用远程 API批量请求时还要控制并发频率并在失败时使用指数退避重试。不要把远程接口当成本地函数直接百万次循环调用否则容易触发限流也不符合服务访问规范。只请求你有权访问的数据并遵守目标服务的频率限制和使用条款。9.3 金融量化场景中的数值精度量化策略里经常要处理价格、仓位、手续费这类数据使用float会有精度问题。0.1 0.2不等于0.3在计算累计收益时误差可能被放大。建议使用Decimal或整数最小单位。看一个简单示例from decimal import Decimal price Decimal(0.1) quantity Decimal(3) total price * quantity print(total) # 0.3在实际应用中要从数据源头就开始使用高精度类型不要已经转成 float 之后再补救。如果数据量很大也可以把价格转换为整数“分”参与计算最后再除以 100。使用 Decimal 时要注意不要和 float 混淆运算否则可能重新引入精度问题。10. 总结与下一步Python 数据类型看着基础但它直接决定了代码是否稳。文章中从环境准备、内置类型、可变性、类型判断、强制转换、pandas 转换到接口和批量任务清洗已经围绕“先识别类型再处理数据”的思路全部展开。你想最先验证的功能应该是一个简单的字符串转整数实例因为这一步能立刻看到类型到底如何影响运算结果。最容易踩的坑是bool(False)为 True、字符串2参与乘法得到22、把含 NaN 的列直接转整数导致报错。接下来可以继续练习写一个读 CSV 文件的脚本把价格列统一转成浮点数把日期列统一转成 datetime并统计转换失败的行或者写一个接口清洗函数对 API 返回的多条记录做安全类型转换。数据类型知识会和异常处理、数据结构、pandas 工具结合得越来越深先动手跑一遍再回来看这里的问题清单会理解得更快。建议把本文收藏作为手边参考遇到类型相关报错时直接对照解决。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门