011、类型转换的艺术:int、float、str、bytes 的互相转换与边界处理

发布时间:2026/6/22 18:19:31
011、类型转换的艺术:int、float、str、bytes 的互相转换与边界处理 011、类型转换的艺术int、float、str、bytes 的互相转换与边界处理上周五晚上十一点我盯着屏幕上的报错日志头皮发麻。一个生产环境的API突然返回了500排查后发现是某个用户上传的Excel里一个看似正常的数字字段在读取时被解析成了科学计数法字符串然后下游的加密模块直接调用了bytes()去转换——结果炸了。那个字段的值是1.23456789e10而加密模块期望的是纯数字字符串。这种类型转换的坑我踩过不下十次今天干脆把int、float、str、bytes这四兄弟之间的互相转换和边界情况一次性说清楚。int ↔ float你以为的精度其实都是假象先看最基础的。int()和float()互相转换看起来简单但精度丢失的问题经常被忽略。# 这里踩过坑float转int直接截断小数部分不是四舍五入price19.99print(int(price))# 输出19不是20# 别这样写除非你明确知道要截断如果你需要四舍五入老老实实用round()。但round()也有自己的脾气——银行家舍入法四舍六入五成双不是所有语言都这样但Python是。print(round(2.5))# 输出2不是3因为2是偶数print(round(3.5))# 输出4因为4是偶数另一个坑大整数转float会丢失精度。Python的int可以无限大但float只有53位有效数字。big_int2**531print(float(big_int))# 输出9007199254740992.0不是9007199254740993.0# 精度丢失了而且不会报错这才是最可怕的str ↔ int/float用户输入永远是魔鬼字符串转数字是日常高频操作但用户输入的数据永远比你想象的脏。# 别这样写直接int(input())用户输入abc直接崩user_input 42 print(int(user_input))# 这个可以int会忽略前后空格# 但下面这些都会炸# int(42.0) # ValueError# int(0x1A) # ValueError除非指定base16# int(1,234) # ValueError逗号不行处理用户输入时我习惯用try-except包裹或者先做清洗。特别是从CSV、Excel读数据时数字可能带着千分位逗号、货币符号、甚至不可见字符。defsafe_int(value,default0):生产环境用的安全转换踩过太多坑了ifvalueisNone:returndefault# 先转字符串去掉常见干扰字符cleanedstr(value).strip().replace(,,).replace($,).replace(¥,)try:returnint(cleaned)except(ValueError,TypeError):try:returnint(float(cleaned))# 处理42.0这种情况except(ValueError,TypeError):returndefaultfloat转字符串也有讲究。str(0.1)输出0.1看起来没问题但str(1/3)输出0.3333333333333333这16位小数是float的极限精度。如果你需要控制小数位数用格式化字符串。price1234.5678# 别这样写str(price) 元 # 输出1234.5678元不专业# 应该这样print(f{price:.2f}元)# 输出1234.57元bytes ↔ str编码问题能让你怀疑人生bytes和str的转换是Python 3里最容易出bug的地方没有之一。Python 2里str和bytes混用的时代已经过去了但很多人还是搞不清什么时候该decode什么时候该encode。text你好世界# str转bytesencodebyte_datatext.encode(utf-8)# 推荐跨平台兼容print(byte_data)# b\xe4\xbd\xa0\xe5\xa5\xbd...# bytes转strdecodedecodedbyte_data.decode(utf-8)print(decoded)# 你好世界坑在哪里编码不一致。你从Windows的GBK文件读出来的bytes用UTF-8去decode直接报错。# 这里踩过坑从旧系统接口拿到的数据是gbk编码gbk_bytesb\xc4\xe3\xba\xc3# 你好的gbk编码try:print(gbk_bytes.decode(utf-8))# UnicodeDecodeErrorexceptUnicodeDecodeError:print(gbk_bytes.decode(gbk))# 正确输出你好更隐蔽的坑str()和bytes()直接调用时的行为。# 别这样写str(bhello) # 输出bhello不是hello# 应该用 decode()print(str(bhello,encodingutf-8))# 输出hello# bytes()的坑更大print(bytes(10))# 输出b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0010个空字节# 不是b10如果你想要b10应该用bytes(10, encodingutf-8)bytes ↔ int/float网络协议和二进制文件的必修课这部分是很多Python开发者忽略的但如果你做网络编程、文件格式解析、或者加密相关的工作这是基本功。# int转bytes用to_bytes方法number123456# 别这样写bytes(number) # 得到123456个空字节不是你要的byte_datanumber.to_bytes(4,byteorderbig)# 4字节大端序print(byte_data)# b\x00\x01\xe2# bytes转int用from_bytesrecoveredint.from_bytes(byte_data,byteorderbig)print(recovered)# 123456边界情况负数怎么办to_bytes默认不支持负数需要指定signedTrue。negative-123456try:negative.to_bytes(4,byteorderbig)# OverflowErrorexceptOverflowError:byte_datanegative.to_bytes(4,byteorderbig,signedTrue)print(byte_data)# b\xff\xfe\x1d\xc0recoveredint.from_bytes(byte_data,byteorderbig,signedTrue)print(recovered)# -123456float转bytes更麻烦因为float没有直接的to_bytes方法。需要用struct模块。importstruct# float转bytespi3.14159byte_datastruct.pack(!f,pi)# !表示网络字节序大端f表示floatprint(byte_data)# 4字节的二进制表示# bytes转floatrecoveredstruct.unpack(!f,byte_data)[0]print(recovered)# 3.14159但可能有精度损失注意struct.pack的f是C语言的float4字节d是double8字节。Python的float对应的是double。混合转换的实战场景真实项目中你很少只做一对一的转换。比如从网络接收数据可能是bytes里面包含数字需要先decode成str再转成int或float。# 模拟从socket接收的数据raw_datab{temperature: 25.6, humidity: 68%}# 别这样写直接eval(raw_data) # 安全风险importjson decodedraw_data.decode(utf-8)datajson.loads(decoded)tempfloat(data[temperature])# 25.6humidityint(data[humidity].rstrip(%))# 68另一个常见场景把数字转成固定长度的bytes用于协议通信。defpack_int32(value):把int转成4字节大端bytes支持负数returnvalue.to_bytes(4,byteorderbig,signedTrue)defunpack_int32(data):从4字节bytes恢复intreturnint.from_bytes(data,byteorderbig,signedTrue)# 测试边界值print(pack_int32(2147483647))# 最大正int32print(pack_int32(-2147483648))# 最小负int32个人经验性建议永远不要相信输入数据的类型。从文件、网络、用户输入来的数据先转成你期望的类型再处理。我见过太多因为CSV里一个数字带空格就崩掉的情况。编码问题要显式处理。不要依赖默认编码open()文件时指定encodingutf-8encode()和decode()时明确指定编码。默认编码在不同操作系统上不一样Windows是gbkLinux是utf-8这个坑我踩了三年才养成习惯。大数转换要警惕精度。如果你的业务涉及超过2^53的整数比如某些ID、时间戳不要用float用int或者decimal.Decimal。float转int时先确认你真的需要截断而不是四舍五入。bytes和str的转换要成对出现。每次encode都要有对应的decode反之亦然。我习惯在函数或类的边界做转换内部统一用一种类型。比如所有网络层用bytes业务层用str在接口处转换。写测试用例覆盖边界值。0、负数、最大值、最小值、空字符串、None、特殊字符如emoji这些都要测。我见过一个线上bug就是因为用户昵称里有个emoji导致某个老系统的GBK编码转换失败。类型转换看起来是基础但往往是线上bug的重灾区。每次遇到这类问题我都会在代码里加注释提醒自己和其他人“这里踩过坑”。希望这篇文章能帮你少踩几个坑。