1.1 变量、数据类型与运算


1.1 变量、数据类型与运算

本节摘要:变量和数据类型是 Python 存储与处理数据的基本单位。本节讲清楚 Python 的动态类型机制、四种最常用的内置类型(整型、浮点、布尔、字符串)各自的用途与坑,以及算术、比较、逻辑三类运算符怎么搭配使用。通过"成绩等级判定"和"简易记账"两道分层练习,把类型选择和运算组合的判断力练出来。

学习目标

阅读完本节,你应当能够:

  1. 解释 Python 动态类型的含义,说出它和静态类型的区别
  2. 为一个具体问题选择合适的数据类型,并说明理由
  3. 区分整除、取模、幂运算的用法,避免常见运算陷阱
  4. 用比较和逻辑运算符组合出复杂的判断条件
  5. 独立完成一道涉及类型转换与运算的练习题

问题与直觉

先想一个最朴素的问题:当你在 Python 里写下 x = 10,然后再写 x = "hello",程序不会报错,x 的类型悄悄从整数变成了字符串。这在 Java 或 C 里是不可想象的——那些语言要求你提前声明 int x,之后 x 就只能装整数。

这就是 Python 的动态类型:变量本身没有类型,类型属于它指向的对象。变量更像一个"标签",贴在哪个对象上,它就临时"是"什么类型。

这个设计的好处很直观——写起来轻快,不用纠结类型声明;代价也很明显——某些错误不到运行时不会暴露。比如你本想把两个数字相加,结果其中一个是从输入读进来的字符串,运行时才会炸出 TypeError。理解动态类型,是为了学会在它给你便利的同时,主动规避它带来的隐患。

数据类型的选择也是同理。Python 内置了十几种类型,但你日常写小程序,真正高频使用的就四种:整数、浮点、布尔、字符串。选哪个、为什么选、用的时候要注意什么——这些判断力是本节要通过练习帮你建立的。

核心原理

2.1 四种常用类型一览

类型 关键字 典型用途 常见坑
整型 int 计数、索引、循环变量 理论上无大小限制,但别拿它存身份证号(可能丢前导零)
浮点型 float 小数计算、科学计算 0.1 + 0.2 != 0.3,二进制无法精确表示
布尔型 bool 条件判断、标志位 True == 1,与整数混用时容易出逻辑错
字符串 str 文本、输入输出 不可变,每次"修改"都在创建新对象

浮点精度问题是初学者最容易踩的坑。看一个例子:

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

这不是 Python 的 bug,而是所有用二进制浮点表示小数的语言的通病。涉及金额、需要精确比较的场景,要么放大成整数(用"分"而不是"元"),要么用 decimal 模块。

2.2 类型转换的规则

不同类型之间能不能自动转换,Python 有一套规则:能"安全"转换的会隐式发生,不能的就要显式写出来。

从输入读进来的数据默认是字符串,这是无数初学者报错的源头。age = input() 之后 age"20",要参与计算必须 age = int(age)

2.3 运算符三类

算术运算符里,有三个新手最容易用混:

print(7 / 2) # 3.5 普通除法,结果永远是浮点 print(7 // 2) # 3 整除,向下取整 print(7 % 2) # 1 取模,求余数 print(2 ** 10) # 1024 幂运算

比较运算符返回布尔值,== 比值、is 比身份(是否同一对象),新手常把二者混用。判断两个数相等的绝大多数场景都该用 ==

逻辑运算符 and / or / not 用来组合条件,它们支持短路求值a and b 中如果 a 已经是假,就不会再算 b。这个特性常被用来简化代码,比如 x != 0 and 10/x > 1,先判断非零再除,避免除零错误。

工程实践要点

3.1 类型选择的取舍

# 场景:存一个人的年龄,用于计算和比较 age = 25 # int:要参与计算、比较,用整数 # 场景:存手机号 phone = "13800138000" # str:手机号不会用来算术,且要保留格式 # 场景:存一个开关状态 is_active = True # bool:明确表示真假,不要用 0/1

⚠️ 常见坑:拿字符串存"看起来像数字但不需要计算"的数据——手机号、身份证号、学号、邮政编码。这些一旦用 int 存,前导零会丢失,而且超长数字(如 18 位身份证)在某些语言里会溢出,Python 虽不溢出但语义也不对。

3.2 整除的"向下取整"陷阱

Python 的 // 是向下取整,不是"向零取整"。这在正数上没区别,但遇到负数会让人意外:

print(-7 // 2) # -4,不是 -3!因为 -3.5 向下取整是 -4 print(7 // -2) # -4

如果你想要的是"截断小数部分"(向零取整),要用 int(a / b)

3.3 复合赋值与链式比较

Python 支持一些简洁写法,用好了能让代码更清楚:

x = 10 x += 5 # 等价 x = x + 5 x *= 2 # x 变成 30 # 链式比较,可读性比 1 <= x and x <= 100 高 if 1 <= x <= 100: print("在范围内")

💡 关键直觉:好的代码不是"能用最少的字符写完",而是"读的人一眼能懂意图"。链式比较 1 <= x <= 100 之所以好,是因为它直接对应了数学上的区间写法,读者无需在脑子里翻译。

分层练习

基础题:成绩等级判定

题面:给定一个 0 到 100 的分数(可以硬编码一个变量),输出对应等级:90 及以上为"优秀",80 到 89 为"良好",70 到 79 为"中等",60 到 69 为"及格",60 以下为"不及格"。

思路点拨:这是典型的多分支判断题。注意区间的边界——"80 到 89"是包含 80 还是 81?先用一个数想清楚再写。另外,分数是从外部读入的话,记得做类型转换。

参考骨架

score = 85 # 可换成 input() 读取 # TODO: 用 if/elif/else 判断并输出等级 if score >= 90: grade = "优秀" elif score >= 80: grade = "良好" # 继续补全... print(f"成绩 {score} 的等级是:{grade}")

进阶题:简易记账器

题面:写一段程序,模拟记录几笔收支。初始余额为 0,依次处理三笔:收入 100、支出 30、收入 50。最后输出当前余额。要求用浮点存金额,输出保留两位小数。

思路点拨:这题练的是变量更新和浮点格式化。注意余额是反复赋值,用 +=-= 最自然。输出保留两位小数用 f-string 的 :.2f

参考骨架

balance = 0.0 balance += 100 # 收入 balance -= 30 # 支出 balance += 50 # 收入 print(f"当前余额:{balance:.2f} 元") # 120.00

💡 思考延伸:如果金额是从用户输入逐笔读进来的,每笔是字符串,你需要先转成 float。但万一用户输了个非数字呢?这就引出第 2 章要讲的异常处理——现在先记住这个隐患。

挑战题:判断闰年

题面:给定一个年份(整数),判断它是否为闰年。闰年规则:能被 4 整除但不能被 100 整除,或者能被 400 整除。

思路点拨:这题考的是逻辑运算符的组合与取模运算。把规则翻译成布尔表达式是关键——注意 andor 的优先级,and 高于 or,但加括号更清晰。

参考骨架

year = 2024 is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) print(f"{year} 年是闰年:{is_leap}")

⚠️ 常见坑:很多人写成 year % 4 == 0 就完事,忘了百年不闰、四百年再闰的特例。1900 不是闰年,2000 是闰年——用这两个边界值测一下你的判断。

追问与答疑

为什么 0.1 + 0.2 != 0.3,但 0.25 + 0.25 == 0.5 成立? 因为 0.25 恰好是 2 的负数次幂(二进制里是 0.01),能被二进制精确表示;而 0.1 和 0.2 在二进制里是无限循环小数,只能存近似值。判断依据就这么简单:分母是 2 的整数次幂的小数精确,其余都有误差。这也解释了为什么实践中用"放大到整数"的方案屡试不爽——把金额乘一百变成整数分,所有比较都回到精确世界。

is== 我该什么时候关心区别? 日常判断值相等用 ==,两个例外值得知道。一是判断 None:惯用 x is None,因为 None 是单例。二是小整数缓存带来的错觉:a = 256; b = 256a is b 为 True,但把 256 换成 257 就可能变 False——不同版本实现细节不同,拿 is 比值的代码就是埋在地里的雷。练习里可以亲自试一下 256 和 257,见过一次就再也不会犯。

布尔值和整数到底算不算一种类型? boolint 的子类,True 就是 1、False 就是 0。这带来一些"意外合法"的写法,比如 sum(x > 60 for x in scores) 能直接数出及格人数——生成器产出一串布尔值,求和时被当 0/1 累加。知道这个特性后,很多看起来别扭的计数代码就看得懂了。但它也是坑的来源:if value == True 这种写法,当 value 是 1 时同样成立,语义就混了。需要判断真伪时直接 if value:,需要判断确实是布尔真时用 is True

写代码时该用中文注释还是英文注释? 练习阶段用中文完全没有问题,母语表达能让"为什么这么写"记得更牢,团队协作时也以"大家都能快速读懂"为准。真正要留意的不是语言,而是注释的时机:解释意图的注释(为什么取整、为什么用字符串)值得写,复述代码的注释("给 x 加一")是噪音。等到阅读英文文档成为日常,再自然过渡到英文注释也不迟——顺序不该反过来,先为"看起来专业"牺牲表达效率,得不偿失。

一行代码拆多步写丢人吗? 不丢人,反而是好习惯。把 result = (a + b) * c / 2 拆成先求和、再乘、再除三步,每步起个有意义的变量名,调试时能逐段打印中间值,读的人也能跟着变量名走。性能差异在小数据上可以忽略,可读性收益却是实打实的。等你对表达式有了把握,再自然合并——那时你合并的是"自己确实一眼能懂"的部分,而不是"怕显得啰嗦"的部分。

动态类型是不是意味着不用关心类型了? 恰恰相反。静态语言的编译器替你把关,动态语言里这个责任转移到了你的脑子里。实操上有两个便宜的手段:一是给函数参数加类型提示(第 2 章会练),二是写代码时在每个变量初始化的地方问自己一句"它此刻装的是什么类型、后面会被当成什么用"。这两个习惯成本极低,却能拦下大部分动态类型引发的事故。

补充练习:BMI 计算与分级

题面:给定身高(米)和体重(千克),计算 BMI(体重除以身高的平方),并按区间输出分级:低于 18.5 为"偏瘦",18.5 到 23.9 为"正常",24 到 27.9 为"超重",28 及以上为"肥胖"。输出保留一位小数。

思路点拨:这题综合了浮点运算、区间判断和格式化输出。先算 BMI 存进变量,别在判断条件里重复计算;区间判断从低到高排,边界值(恰好 18.5、24、28)要明确归属哪一档——按题目"低于 18.5"和"24 到 27.9"的措辞,18.5 属正常、24 属超重。输出用 :.1f

参考骨架

height, weight = 1.72, 68 bmi = weight / height ** 2 if bmi < 18.5: level = "偏瘦" elif bmi < 24: level = "正常" elif bmi < 28: level = "超重" else: level = "肥胖" print(f"BMI {bmi:.1f},分级:{level}") # BMI 23.0,分级:正常

写完后做两个延伸:一是把身高体重改成从输入读取,体会类型转换的必要;二是思考如果身高输入为 0 会发生什么(除零错误),这个隐患正是第 2 章异常处理的伏笔。

本节要点回顾

  • Python 是动态类型:变量无类型,类型属于对象;便利是写起来轻快,代价是某些错误要到运行时才暴露。
  • 四种高频类型各有定位:int 算数、float 小数(注意精度)、bool 判断、str 文本;别用错场景。
  • 不要用数字类型存"像数字但不计算"的数据:手机号、身份证号用字符串,避免前导零丢失和语义混乱。
  • 浮点不能精确表示小数:金额等需精确的场景要么放大成整数,要么用 decimal
  • // 是向下取整:负数上和直觉不同,要截断小数请用 int(a / b)
  • 逻辑运算符有短路特性:可用于避免除零、访问空值等隐患。
  • == 比值,is 比身份:判断相等绝大多数情况用 ==

下一节我们把分支和循环加进来,再配上字符串操作,做一个能反复交互的简易计算器。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U