1. 变量与命名程序世界里最基本的容器概念昨天装好环境、跑通print(Hello, world!)之后大家应该已经感受到了一件事写出能运行的程序并不难难的是让程序理解和处理乱七八糟的数据。今天要啃的语言元素说白了就是Python这门语言里最基础的一批零件——变量、数据类型、运算符、表达式、输入输出。这些东西单个拎出来都不难但它们组合在一起的方式决定了你后面写出的代码是能跑还是能看、能改、能维护。我见过太多人在第2天就急着往后冲结果到了函数、类那几章又回头补课。所以今天不妨慢一点把每一个概念都嚼碎。1.1 动态类型的Python到底怎么理解变量先想一个问题x 5这行代码计算机到底做了什么在很多语言里你需要先声明我要一个整数类型的变量x然后才能往里存值。Python不用你直接写x 5解释器会做两件事在内存里开辟一块空间存放数字5然后让名字x指向这个对象。所以Python里的变量更准确的说法是标签reference它本身不保存数据只是贴在数据对象上的一个标签。这就是动态类型的本质同一个变量名今天可以贴数字明天可以贴字符串后天可以贴列表。比如x 5 print(type(x)) # class int x hello print(type(x)) # class str代码完全合法不会报错。这个特性非常灵活但也埋了一个隐患如果代码长了你很难一眼看出某个变量到底应该是什么类型。所以后面很多有经验的人会写类型注解比如x: int 5不过那是后话。第2天你只需要记住一个原则变量的类型由它当前指向的对象决定而不是由名字决定。理解这一点还有个实际好处如果你在交互环境里执行x 5然后又执行x 3Python会抛出TypeError: can only concatenate str (not int) to str——字符串和数字不能直接相加。很多人第一次遇到这个报错会懵其实根本不是语法问题而是你还没搞明白标签贴错了对象。这个我们在后面的类型转换章节再展开。1.2 命名规则与PEP8规范现在养成习惯后面少改代码变量怎么起名是第2天最容易忽略、但长远来看最值钱的知识点。Python的变量名有几个硬性规则只能由字母、数字、下划线组成且不能以数字开头。不能是关键字比如if、for、while、class这些Python会直接报语法错误。大小写敏感Name和name是两个完全不同的变量。除了硬性规则还有一套约定俗成的风格规范来自PEP8。其中和你最相关的几条是变量名用小写字母多个单词用下划线连接这叫snake_case比如user_name、total_count。别用userName这种驼峰式那不是Python社区的审美。常量名用全大写加下划线比如MAX_RETRY 3。变量名要有意义别用a、b、c、temp这种。我知道初学者图快但你可以试想一下一周后回来看自己写的代码a 10到底是年龄还是数量没人知道。命名这件事我很直白地说刚开始你可能觉得刻意、慢但这是回报率极高的前期投资。我自己带过不少初学者最痛苦的就是看他们三天前写的代码——变量名全是x1、x2逻辑再好也救不回来。今天花十分钟记住这几条规则后面省下的时间至少是几小时。1.3 常量约定与魔法数值的隐患Python里没有真正的常量——你定义PI 3.14159之后后续代码仍然可以把PI改成3Python不会拦你。所谓常量只是社区约定全大写就当它不可变全靠自觉。这里引出一个很多新手觉得没啥大不了的问题魔法数值magic number。如果你在代码里直接写if age 18:这个18就是魔法数值。看代码的人包括未来的你会困惑18是什么是年龄限制是优惠门槛还是别的什么规范的写法是ADULT_AGE 18 if age ADULT_AGE: print(成年)这样代码的意图就一目了然了。今天讲这个可能有点早但既然聊语言元素我宁愿你从第一周就建立这种意识。写代码不只是写给计算机看更是写给人看的。2. 数据类型全景语言元素的质数表如果说变量是存放数据的容器那类型就是容器的种类。Python内置的数据类型有很多但第2天我们只需要盯住四个int整数、float浮点数、str字符串、bool布尔值。后面你会遇到list、tuple、dict、set这些容器类型也会遇到bytes、complex这类特殊类型但今天先把地基压实。2.1 四大基本类型int、float、str、bool的光谱先看一段最简单的代码age 25 # int整数 price 19.99 # float浮点数即小数 name Alice # str字符串 is_student True # bool布尔值只有True和False用type()函数可以随时查看一个变量的类型print(type(age)) # class int print(type(price)) # class float print(type(name)) # class str print(type(is_student)) # class bool这四种类型有几个值得注意的边界情况float的精度问题0.1 0.2 的结果并不是精确的0.3而是0.30000000000000004。这是二进制浮点数表示的固有问题几乎所有编程语言都一样。初学者第一次看到会很惊讶但你不必在这一天就和它硬磕只需要知道不要用浮点数直接做精确比较比如判断0.1 0.2 0.3会得到False。以后涉及钱的场景要么用整数分要么用专门的库。bool其实是int的子类True本质上是1False是0。所以True 1等于2这个行为合法但写出来容易让人困惑日常代码别这么用。字符串可以是空字符串也可以是包含一个字符的a。字符串的长度用len()获取len(hello)返回5。2.2 类型转换str(3)和int(3)为什么不是同一回事这是第2天的重头戏因为类型转换是几乎所有后续练习的必经之路。你需要记住三组核心转换# 转字符串 str(3) # 3 str(3.14) # 3.14 str(True) # True # 转整数 int(3) # 3 int(3.99) # 3注意是截断不是四舍五入 int(3.99) # 报错ValueError字符串里的小数不能直接转整数 # 转浮点数 float(3.14) # 3.14 float(3) # 3.0理解这套转换有个很简单的思路转换括号里放进去的东西决定了什么能转、什么不能转。int()能接受整数、浮点数、以及长得像整数的字符串int(3.99)会失败因为字符串3.99从内容上看就不是整数。同理float(abc)也会失败因为abc没法解释成任何数字。真正的坑出现在用户输入那里我在后面第5节会专门讲input()这里先埋伏笔input()返回的一定是字符串所以input(输入年龄: )拿到的其实是25你要把它转成int才能做年龄的判断。这个字符串和数字之间的转来转去是新手前两周最容易翻车的地方原因就是对刚才这套转换规则不熟。很多人会问那3和3长得那么像为什么不直接混着用原因很简单字符串3本质是一串字符它没有数学意义3 * 3不是9而是333。Python选择用*处理字符串重复这在后面操作字符串时很好用但如果你一开始没意识到字符串和数字是两种物种就会出现各种匪夷所思的结果。2.3 判断类型type()与isinstance()的差异除了type()还有一个isinstance()也能判断类型但两者有实质区别print(type(True) int) # False因为type(True)是bool print(isinstance(True, int)) # True因为bool是int的子类这个区别在判断继承关系时很关键。第2天你可能用不到但提前知道无害。日常代码里判断类型更推荐isinstance()因为它能正确处理子类关系而且可以一次判断多个类型print(isinstance(3, (int, float))) # True3. 运算符把数据组装起来的积木有数据就得有操作。Python的运算符分好几类第2天必须吃透的是算术、比较、逻辑、赋值这四类因为它们会出现在你后面写的几乎每一行代码里。3.1 算术运算符的边界情况除法、整除与取模的坑算术运算符有七个、-、*、/、//、%、**。前三个不用多说后四个才是考点print(10 / 3) # 3.3333333333333335普通除法结果一定是float print(10 // 3) # 3整除向下取整 print(10 % 3) # 1取余数 print(2 ** 3) # 8幂运算三个容易翻车的地方/的结果永远是浮点数即使4 / 2也返回2.0。这意味着如果你拿到2.0去做某些需要整数的操作比如列表索引会出类型问题。2.//是向下取整不是向零取整。正数之间没事但负数要注意-10 // 3的结果是-4而不是-3因为-10 / 3 ≈ -3.33向下取整就是-4。%取模运算的符号跟随除数-10 % 3的结果是2不是-1。很多教材不讲这一点但你要知道否则在某些数学计算场景会得到意外但正确的结果。这些边界情况不背下来也可以遇到的时候多print()几次就能摸清规律。但除法返回浮点这个性质今天记住了能帮你少走弯路。3.2 比较运算符与布尔逻辑短路求值是怎么回事比较运算符有六个、、、、、!。它们的结果一定是布尔值True或Falseprint(3 2) # True print(3 3.0) # True数值相同即相等 print(abc abc) # True字符串比较内容 print(abc ! xyz) # True这里有个值得提的细节比较的是值相等而is比较的是是不是同一个对象。对整数而言小整数有缓存机制x 5; y 5时x is y通常为True但x 256和x 257行为就不同了。初学阶段记住一句话判断值用别用is。这是一个经典的看着能用但实际有隐患的坑。逻辑运算符是and、or、not。最核心的知识点是短路求值A and B如果A为假整个表达式结果一定为假Python就不会再计算B。A or B如果A为真整个表达式结果一定为真Python就不会再计算B。短路求值不只是性能优化它还有一种实用写法user_input input(输入名字可留空: ) name user_input or 匿名用户 print(name)如果input()返回的是空字符串为空在布尔判定里是False表达式就会取匿名用户。这行代码既简单又地道等你学到后面会发现这种写法非常常见。3.3 赋值运算符链式赋值与复合赋值赋值运算符的大家都认识但它的两个衍生物容易忽略。第一个是链式赋值a b c 10三个变量同时指向整数10。这种写法简洁但如果后面你改其中一个的值另外两个不会跟着变——因为赋的是指向而非复制。第二个是复合赋值运算符、-、*、/、//、%、**count 0 count 1 # 等价于 count count 1 count * 3 # 等价于 count count * 3一开始有人会问为什么不直接写count count 1两种写法都行但更简洁、也更符合更新一个变量的直觉。你会在后面的循环里频繁用到它。4. 运算符优先级与表达式看起来简单却最容易翻车的地方很多初学者看到优先级三个字就头大觉得是考试题才考的东西。其实不是优先级直接决定你写出的表达式到底算什么。而且你越往后学表达式越长优先级带来的隐性问题就越明显。4.1 优先级速查表与括号的暴力美学Python运算符优先级从高到低的常用顺序大致是** 正负号 *、/、//、%、-、、、、!notandor。这句话读起来很拗口所以我的建议是不要背直接用括号。括号在Python里是最高的优先级它的作用不是计算更快而是消除歧义。比如# 想表达先算32的和再乘以4 result (3 2) * 4你不需要纠结*和谁优先因为括号已经把意图写死了。等熟练之后你自然会发现哪些括号可以去掉但新手阶段宁可多写括号也不要让读代码的人猜。有一个经典坑是连续的比较运算符。Python支持链式比较age 25 print(18 age 60) # True等价于18 age and age 60这是Python的一个亮点很多其他语言不支持这样写。但要注意别把它和数学上的连续不等式搞混它的行为在边界上和你直觉一致从左到右依次比较中间任何一个不满足就短路返回False。4.2 可读性优先别把表达式写成脑筋急转弯优先级本身不难难的是有人喜欢炫技。比如result 2 * 3 ** 2 1这个结果是19因为**先算3 ** 2 9然后2 * 9 18再加1。但你看这行代码第一眼能立刻说出顺序吗大概率不能。所以实战中我会建议把它写成result 2 * (3 ** 2) 1甚至拆成两行base 3 ** 2 result 2 * base 1表达式的目的应该是让逻辑清晰可见而不是考验阅读者的运算符记忆。这个原则贯穿Python的整个设计哲学——可读性优先。你今天写下的每一行让人看不懂的代码都是给未来的自己埋的雷。还有一个新手特别容易搞混的点not、and、or和比较运算符混用时not的优先级高于and但低于比较。比如not 3 2 # not (3 2)结果是False这里的先计算再对结果取not。如果写成(not 3) 2语义完全不同。同样多用括号别用脑内存这是我反复强调的原因。5. 输入输出与格式化程序与用户之间的翻译官前两天你可能只会用print()往屏幕输出内容今天加上input()来接收用户输入程序就从单向广播变成了双向对话。这一节的内容实战价值极高。5.1 input()返回的永远是字符串新手第一大坑input()的基本用法name input(请输入你的名字: ) print(你好, name)执行时程序会打印提示语然后等待用户输入用户按回车后输入的内容以字符串形式赋值给name。关键就在这无论用户输入的是什么input()返回的一定是字符串。如果用户输入了25你拿到的也是25而不是数字25。很多第一周写小计算器的人都在这里翻车age input(请输入年龄: ) if age 18: print(成年)这段代码会直接报错TypeError: not supported between instances of str and int。解决方式很固定——先转换再比较age int(input(请输入年龄: ))为什么这个坑这么普遍因为人类的大脑看到25这个符号自动把它当作数字处理了但计算机看到的是字符序列2和5。这提醒我们在写基于输入的代码时先问自己一个问题——我手上拿到的是什么类型在关键的赋值语句后加一行print(type(...))是调试阶段最有效的办法。如果你不确定用户会不会老老实实输入数字可以做一层保护。最简单的做法是循环重试但循环要明天学了分支和循环才会写所以今天你只需要知道这个坑和基本的转换方法即可。真想立刻解决的话可以用str.isdigit()先判断字符串是否全由数字组成s input(请输入数字: ) if s.isdigit(): num int(s) print(转换成功:, num) else: print(输入不是纯数字)isdigit()是一个字符串方法它用来判断这个字符串是否全部由数字字符组成第2天记住它的存在就行后面你会在很多校验场景用到。5.2 print()的 sep、end 与 f-string 格式化print()是最早接触的函数但它有几个容易被忽略的参数。sep指定多个值之间的分隔符默认是空格end指定输出末尾的字符默认是换行print(a, b, c, sep-) # a-b-c print(hello, end ) print(world) # hello world这些参数在你后面做简单文本拼接时很好用。但说到格式化输出我更推荐你现在就养成用f-string的习惯。f-string是Python 3.6引入的语法在字符串前面加f然后用花括号嵌入变量或表达式name Alice age 25 print(f{name}今年{age}岁明年{age 1}岁。) # 输出Alice今年25岁明年26岁。这里有个细节花括号里可以直接写表达式比如age 1程序会先算出来再放入字符串。f-string还可以控制格式比如保留两位小数price 19.995 print(f价格是{price:.2f}元):.2f的意思是这个浮点数保留两位小数输出。类似的还有整数补零f{num:03d}会把7输出为007。这些格式化语法今天不背也没关系但知道f-string里可以做这么多事这个事实能让你少走弯路——否则你有可能会去用str(price)拼接字符串费劲又不优雅。5.3 转义字符与原始字符串字符串里有些特殊字符需要转义才能正确输出。最常用的是\n换行、\t制表符相当于按Tab键、\\反斜杠本身、\双引号print(第一行\n第二行) # 第一行 # 第二行 print(他说\你好\) # 他说你好不过如果你要处理的字符串里有很多反斜杠比如Windows文件路径C:\Users\new\test.txt这里的\U、\n会被Python当作转义字符解释导致路径出错。解决方式是在字符串前面加r表示原始字符串raw stringpath rC:\Users\new\test.txt print(path)加上r之后\n不再被解释为换行而是保留字面意思。这个知识点现在看着冷门但等你写文件操作、处理路径的时候会回来感谢这一节的。6. 第二天的实战练习与报错排查说了这么多概念最后必须落回到手。我只推荐三个练习它们难度递进正好覆盖今天全部知识点。做完这三个你今天就算毕业了。6.1 三个递进练习计算器、温度转换、成绩等级第一个练习两数四则运算计算器。要求是让用户输入两个数字和一个运算符号程序输出结果。核心代码a float(input(请输入第一个数: )) b float(input(请输入第二个数: )) op input(请输入运算符 - * /: ) if op : result a b elif op -: result a - b elif op *: result a * b elif op /: if b 0: print(除数不能为0) else: result a / b else: print(不支持的运算符) print(f结果: {result})为什么用float而不是int因为用户可能输入小数用float更通用。这个练习覆盖了input()、类型转换、运算符、f-string和前面埋下的if分支——今天你可能还没正式学if但看一遍能大概猜到它在干嘛。第二个练习摄氏温度转华氏温度。公式是F C * 9 / 5 32c float(input(请输入摄氏温度: )) f c * 9 / 5 32 print(f{c}摄氏温度 {f}华氏温度)这个练习看起来简单但它考验的是运算符优先级是否真的理解了。c * 9 / 5中*和/优先级相同从左到右计算结果没问题。如果你写成c * (9 / 5) 32数值上也一样但思路不同。我建议你两种都写一遍体会一下不同写法对可读性的影响。第三个练习根据百分制分数输出等级。90分及以上为A80到89为B70到79为C60到69为D60以下为Escore float(input(请输入分数: )) if score 90: grade A elif score 80: grade B elif score 70: grade C elif score 60: grade D else: grade E print(f等级: {grade})这个练习的精髓在于elif的顺序——从高到低依次判断一旦满足就跳出。如果你把顺序反过来score 60在前那所有及格分数都会变成D。很多初学者在这一点上栽跟头但栽过之后对顺序即逻辑的理解会深一层。6.2 这个阶段最常见的三个报错与解决思路我把第2天高频出现的报错整理成一张表方便你排查报错信息触发原因解决思路SyntaxError: invalid syntax语法错误比如少了冒号、括号不匹配、变量名用了关键字检查错误提示指向的那一行从行尾往前找八成是标点问题TypeError: can only concatenate str (not int) to str字符串和数字用拼接比如年龄是 25把数字转成字符串年龄是 str(25)或者直接用f-stringValueError: invalid literal for int()用int()转换了一个不像整数的字符串比如int(abc)或int(3.14)先检查用户输入内容或改用float()转换三条报错的共性是什么都是类型和值的问题不是逻辑问题。这印证了今天反复提的一句核心写每一行代码前先搞清楚这个变量是什么类型、里面装了什么值。当你心里有这条线大部分报错都能在几秒内定位。6.3 给明天分支结构提前埋的种子今天你其实已经在用if、elif、else了只是还没系统学。明天的分支结构课程会把这些代码片段正式化——包括条件怎么写、嵌套分支、match语句等等。我建议你今天做完练习后可以试着用if写一个判断闰年的小程序规则是能被4整除但不能被100整除或者能被400整除。这个程序用到取模运算%、逻辑运算符and、or恰好把今天所有知识点串起来year int(input(请输入年份: )) is_leap (year % 4 0 and year % 100 ! 0) or (year % 400 0) print(f{year}年是闰年 if is_leap else f{year}年不是闰年)注意这里用了三元表达式一天可能看不懂没关系明天学完分支你再回来看这行会豁然开朗。学习语言元素就像学做菜先认识食材今天你认识了盐、糖、酱油明天就能学什么时候放、放多少的技巧。别看这些概念基础它们决定你后续所有的程序大厦能盖多高。最后分享一个我自己的习惯每次学完一个章节不要急着往下翻拿当天的知识点写一个无意义但完整的小程序——比如计算BMI、模拟购物车结算。这个过程不是为了产出什么有用工具而是强迫自己把听懂了变成手会了。第2天也许感觉内容琐碎但请相信语言元素这个地基打得越扎实后面第30天、第60天你会越轻松。