资讯动态

Python编程必修课:搞懂缩进、转义符与常量

发布时间:2026/10/3 14:41:30 来源:尧图企业网站定制
1. 空格Python里缩进不是格式而是语法1.1 缩进代码块的天然边界如果你之前学过C、Java或者JavaScript第一次看到Python代码的时候大概率会有点不适应——怎么没有花括号怎么判断一个函数到哪里结束答案就是缩进。在Python这门语言里缩进不只是为了好看它直接决定了代码的逻辑结构。我见过很多刚从其他语言转过来的朋友第一个星期几乎都在跟缩进搏斗。他们经常问我这样一个问题为什么Python要把缩进当语法这不是故意折腾人吗其实你想一下你在写其他语言的时候是不是也会习惯性地给代码分层缩进既然大家都会这么做那干脆把缩进变成强制规则这样所有人都必须写出一致的、可读的代码反而避免了花括号风格之争。像if...else嵌套多层的代码如果缩进乱了一眼就能看出结构问题这对团队协作的帮助是巨大的。标准约定是用4个空格表示一级缩进。这不是Python解释器要求的而是PEP 8代码规范建议的。我用过Tab也用过空格实话说混用才是最大的灾难源头。# 正确写法4个空格缩进 def greet(name): if name: message Hello, name print(message) else: print(Hello, stranger) # 逻辑错误写法else和if没有对齐 def greet(name): if name: message Hello, name print(message) else: # 这里的else会和if对齐但前面有个多余的缩进吗 print(Hello, stranger)注意上面这个“错误写法”其实有个陷阱——else如果比if多缩进了一格Python会直接报IndentationError因为else找不到对应的if。这种错误在复杂嵌套里非常难看所以我的习惯是每次写完一段代码先整体看一下缩进层次是否对称。如果你使用的是VS Code或PyCharm强烈建议开启“显示缩进”功能VS Code里叫Render WhitespacePyCharm里叫Show Whitespace。我踩过太多这种坑肉眼看着对齐了实际一个用了Tab一个用了空格运行直接报错。开启这个功能之后所有隐藏的空格和Tab都会变成小圆点或小箭头一眼就能看出问题。1.2 空格的位置里藏着大量坑除了行首的缩进Python里一些容易被忽略的“空格位置”也特别容易出问题。最常见的就是运算符两边要不要加空格、参数要不要加空格。PEP 8的建议很明确二元运算符两边各加一个空格逗号后面加一个空格但逗号前面不加。# 推荐写法可读性更好 x 10 20 result func(a, b, c) # 不推荐但不报错 x1020 result func(a,b,c)第一种写法属于“能跑但丑”第二种写法更极端容易让人看错运算优先级。有一次我写a b1 if b 5 else b-1这种句子时因为运算符和条件表达式混在一起少了空格调试了好久才发现是自己漏看了一个比较符号。从那以后我给自己定了个规矩凡是混合了赋值、比较、条件表达式的行必须空格分明。还有一个容易被坑的地方是函数调用和函数定义之间的空格。很多初学者会写def greet (name): # 函数名和括号之间有空格 print (name) # print后面有空格这在Python里其实不会报错但它是PEP 8明确不推荐的做法。为什么会有人这么写我猜是从某些参考资料或者视频教程里模仿来的但最好的做法还是保持def greet(name)和print(name)这种紧凑写法。再有一个跟空格相关的经典问题空格的编码形式。你可能会觉得“空格就是空格还能有什么”。实际上一个空格有两种来源一种是英文半角空格另一种是全角空格中文输入法下按空格键产生的。全角空格在Python里会被当作普通字符处理如果混进缩进里就会触发IndentationError: unexpected indent。这个错误报出来的时候肉眼很难找到问题在哪里因为全角空格和半角空格看起来几乎一样。我的排查方法是遇到莫名其妙的缩进错误就全选代码把显示缩进的开关打开一点点看。1.3 字符串内部的空格处理空格在字符串内部又是另一番天地。我们经常做的Hello World就是在字符串里显式加入空格。还有一种常见操作是把多个字符串拼接时自动加入空格比如print(Hello, World)。print(Hello, World) # 输出Hello World逗号自动加一个空格 print(Hello World) # 输出HelloWorld字符串直接拼接 print(Hello World) # 输出Hello World手动加空格这里我特别想提醒初学者print函数的多参数输出会自动加空格但如果用一个手动拼接字符串空格得自己处理。有一次我写一个日志输出本意是print([INFO], status_code, message)想把状态码和消息之间留个空格结果因为中间多了一个参数输出变成了[INFO] 200 OK后来才发现print已经帮你加过空格了根本不需要自己拼。字符串内部的空白还有\t制表符和\n换行符这两个“特殊空格”的问题。尤其在做表格对齐输出、日志排版的时候\t和空格混用在控制台上显示得非常乱。最典型的例子是print(姓名\t年龄) print(张三\t18)这个在大多数终端里是可以对齐的但如果姓名长短不一Tab的宽度就会导致错位。我自己在写命令行工具的时候更倾向于用str.format()或者f-string的格式化属性比如f{name:10}把名字补成10个字符宽度这样对齐效果比Tab稳定得多。这个知识点看似简单但真到了爬虫解析、日志导出、数据清洗的时候空格问题能让你排查半小时。2. 转义符字符串里那些“不老实”的字符2.1 常用转义符盘点转义符可能是初学者一学就会、一用就忘的知识点。它的核心作用很简单在字符串里表达“不能用键盘直接输入或直接显示”的字符。比如换行你在字符串里不能真的按回车键否则Python会报语法错误制表符也一样你不能真的在字符串里按Tab键。这时候就需要转义符出马。Python里最常见的几个转义符我列个表转义符含义典型场景\n换行输出多行文本、日志记录\t水平制表符对齐文本、生成表格数据\\反斜杠本身Windows路径、正则表达式\单引号单引号字符串中包含单引号\双引号双引号字符串中包含双引号\r回车配合\n处理Windows换行或制作进度条\b退格不太常用但可以“删除”字符\0空字符特殊数据处理场合这里重点说一下\n的长度问题。很多初学者以为\n是两个字符实际上是一个字符。字符串a\nb的长度是3不是4。理解这一点很重要不然以后做字符串切片、正则匹配的时候会出错。s a\nb print(len(s)) # 输出3而不是4 print(s) # 输出时显示为两行另外一个容易出问题的是Windows路径。比如你写C:\Users\name\DesktopPython会把\U当成一个Unicode转义符的起点直接报语法错误。最经典的解决办法是写C:\\Users\\name\\Desktop也就是把每个反斜杠变成两个。这也是转义符最频繁的应用场景之一。2.2 原始字符串 r路径处理的一把好手我平时写爬虫和数据处理脚本经常要处理Windows路径和正则表达式这两个地方如果不用原始字符串那转义符嵌套能把人绕晕。# 不用原始字符串的写法 path C:\\Users\\Desktop\\my_file.txt print(path) # 用原始字符串的写法 path rC:\Users\Desktop\my_file.txt print(path)原始字符串的原理很简单在字符串前面加一个r或RPython就不再对反斜杠做转义处理。这个特性在正则表达式里的价值更大。正则表达式本身就有很多反斜杠比如匹配数字的\d、匹配单词边界的\b如果你不用原始字符串正则里每个反斜杠都要额外转义写出来的东西几乎没法看。import re # 不推荐可读性差容易出错 pattern \\d\\s\\w # 推荐一眼看出来含义 pattern r\d\s\w亲身经历告诉我正则表达式如果你写非原始字符串版本大概率会被反斜杠搞到怀疑人生。比如有次我写一个匹配文件名的正则本来该用pattern r\.txt$结果不小心写成\.txt$你会发现Python把\.直接转义成了一个普通点然后正则再匹配时所有以任意字符加上txt结尾的都被匹配上了完全不是想要的效果。所以我的底线是任何包含反斜杠的字符串第一反应先考虑加r。不过要注意原始字符串有一个坑它不能以反斜杠结尾。比如你想表示一个Windows文件夹路径C:\folder\写成rC:\folder\会报SyntaxError因为末尾的反斜杠会“吞噬”结束引号。解决办法是写成C:\\folder\\或者用os.path模块拼接路径我个人更推荐后者——写代码时尽量不要硬编码路径。2.3 三引号与转义符的配合多行文本是转义符的一个天然对手。你当然可以用\n把多行文本拼在一个普通字符串里但这样阅读体验很差。Python提供了三引号字符串来优雅处理多行文本。text 第一行 第二行 第三行 print(text)三引号最大的好处是可以直接在字符串里按回车不用写\n。这在写长文档、邮件模板、SQL查询语句时非常实用。但这里有一个容易被忽视的问题三引号里的意外空格和缩进会成为字符串内容的一部分。# 小心这里的缩进会出现在输出里 data { name: python } 如果你希望输出时左边没有多余空格就得用textwrap.dedent()来去掉公共缩进或者干脆把内容对齐到最左边。我自己写多行SQL时经常踩这个坑后来干脆养成了一个习惯三引号字符串内部从第一列开始写不要为了代码好看而加缩进。另外一个很常见的场景是把转义符和三引号结合起来在三引号里你仍然可以使用\n、\t它们照样会被解析。于是你可以在一个长文本里既直接按回车分段又用\t做缩进对齐。比如生成一封格式化邮件正文时我就经常混用。3. 常量用约定和习惯守住“不应改变的值”3.1 Python没有真正的常量但有一整套约定先打击一下初学者Python语言本身没有真正意义上的常量。所谓常量就是一旦赋值就不能再修改的变量。C语言里有const关键字Java里有final但Python没有对应的语法级限制。这意味着你写了一个看起来是常量的东西理论上还是可以在代码里把它改了。但凡是上过几年项目的人都知道即使语言不提供常量机制我们还是需要常量的概念。比如一个项目的版本号、接口地址、超时时间、各种配置阈值这些值散落在代码各个角落如果没有一个集中的、明确标注“请勿修改”的容器项目维护到后面就是一场灾难。所以在Python社区里我们靠一套约定来模拟常量的行为用全大写加下划线命名比如MAX_RETRY_TIMES、API_BASE_URL。将常量集中放在一个模块里比如config.py其他地方通过import来引用。约定俗成看到全大写的变量就不要去修改它。这个约定不是Python官方强制规定的但PEP 8明确推荐了这种命名方式绝大多数Python开源项目也在遵守。你在GitHub上随便翻一个成熟的Python项目比如requests、flask都能看到大量全大写的常量定义。所以你可以说“Python没有真常量”但你不能说“Python没有常量的文化”。3.2 常量命名规范与配置模块化“把常量放在哪里”这个问题比“常量怎么命名”更值得花时间思考。我见过很多初学者在一个脚本里用PI 3.14159定义常量这当然没问题。但当项目变复杂脚本变长常量越来越多的时候问题就来了你在第800行定义了一个TIME_OUT 30在第900行又用了一个timeout 60这两个变量之间的关系是什么如果你把常量分散在各个文件里想找某个配置值在哪里定义得靠全局搜索效率极低。推荐的做法是建立一个config.py或constants.py模块专门存放所有常量。举个例子# config.py DATABASE_HOST localhost DATABASE_PORT 3306 MAX_RETRY_TIMES 3 REQUEST_TIMEOUT 30 DEFAULT_ENCODING utf-8然后在主程序里统一引用# main.py import config print(f数据库地址{config.DATABASE_HOST}:{config.DATABASE_PORT}) print(f请求超时{config.REQUEST_TIMEOUT}秒)这种做法的好处显而易见配置集中改一处全局生效看代码的人一进项目就能知道有哪些关键阈值而且可以避免魔术数字散落各处。我管这招叫“魔法数字消灭术”。实际开发中我还喜欢更进一步——把不同领域的常量拆分到不同的模块里比如config/database.py、config/network.py、config/business.py。这样每个模块的常量数量不会太多语义也更清晰。但对一个练习项目来说一个config.py已经足够。3.3 用不可变类型模拟“真常量”如果你确实想要更接近“真正的常量”的效果Python里有一个不是万能的、但值得一试的方案——使用不可变的数据结构以及定义一个拦截赋值的类。最简单的方式是用元组。比如坐标点、RGB颜色值、星期列表这些内容不该被修改可以用元组来表示RGB_RED (255, 0, 0) WEEK_DAYS (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)元组本身不可变你试图往里面加元素或修改元素都会抛TypeError。但问题在于如果你把整个RGB_RED变量重新赋值为别的东西Python也不会拦你。也就是说元组保护的是“容器内部内容”保护不了“变量引用本身”。如果你连“变量被重新赋值”都想阻止那需要写一个自定义类重写__setattr__方法class Constants: def __setattr__(self, name, value): if hasattr(self, name): raise TypeError(f常量 {name} 不能被修改) super().__setattr__(name, value) const Constants() const.PI 3.14159 print(const.PI) try: const.PI 3.14 # 这里会抛错 except TypeError as e: print(e)这个方案看起来“很香”但实际项目里我很少用。原因有几个第一这种写法增加了代码的复杂度初学者容易绕晕第二Python是动态语言如果你非要防着一个铁了心要修改你常量的人那办法多的是第三维护成本高别人看你代码时会觉得“为什么要用这么迂回的方式”。所以我个人的建议是练习阶段老老实实用全大写约定就够了。等你做团队项目再去考虑更复杂的保护方案。另外要提醒一点如果你在写其他语言比如C、C那“指针常量和常量指针”的区别会让你头疼但Python里完全没有这个概念不需要混淆进来。Python的变量更接近于“贴标签”——同一个对象可以有多个标签重新赋值只是把标签换到另一个人身上。4. 综合实操三个主题的组合练习与报错排查4.1 组合练习模拟一个简单配置读取模块把空格、转义符、常量三个知识点揉在一起做一个20行左右的练习项目最合适。这里我设计了一个“小工具”场景读取一个配置文件路径拼接出完整的日志文件路径并用固定常量控制最大重试次数和日志格式。# config.py import os # 常量区——全大写命名 CONFIG_DIR rC:\workspace\python_basic LOG_DIR CONFIG_DIR r\logs LOG_FILENAME app.log LOG_PATH os.path.join(LOG_DIR, LOG_FILENAME) MAX_RETRY_TIMES 3 REQUEST_TIMEOUT 30 LOG_FORMAT 时间: %s | 状态: %s | 消息: %s # 输出信息 print(日志文件路径, LOG_PATH)# main.py import config def write_log(message, statusINFO): # 使用常量拼接日志内容 log_text config.LOG_FORMAT % (2025-01-01 12:00:00, status, message) print(log_text) # 模拟重试逻辑中使用常量 for i in range(config.MAX_RETRY_TIMES): print(f尝试第 {i 1} 次超时时间为 {config.REQUEST_TIMEOUT} 秒) if __name__ __main__: write_log(服务启动成功)这个练习小项目里空格缩进、转义符原始字符串路径、常量全大写配置全都有了。你可以试着跑一遍然后修改一个常量值观察所有使用这个常量的地方是否同步变化。这就是常量模块化的好处——改一处全局生效。4.2 常见报错与排查思路这三个知识点引发的报错非常典型我把项目里经常遇到的整理成了一张速查表报错信息问题根源解决方案IndentationError: unexpected indent多余的空格缩进或者Tab和空格混用打开显示空白字符的功能删除多余空格TabError: inconsistent use of tabs and spacesTab和空格混用统一使用4个空格编辑器设置自动转Tab为空格SyntaxError: EOL while scanning string literal字符串引号没有闭合检查字符串是否是英文状态下的引号尤其检查全角引号SyntaxError: (unicode error) unicodeescape codec...Windows路径里反斜杠被解析为转义符使用原始字符串r...或把反斜杠写成双反斜杠TypeError: tuple object does not support item assignment试图修改元组中的元素意识到元组不可变改用列表或重新赋值AttributeError: Constants object has no attribute...访问不存在的常量检查常量名是否拼写正确注意全大写命名排查这些报错时我有一个固定的流程分享给你第一步先看报错给出的行号和列号这能省掉大量时间。Python的报错信息已经非常友好了它甚至会用一个^符号指出出错位置只是很多初学者看了一眼就慌了神没有仔细读。第二步看是不是“看起来对但实际不对”。比如缩进错误很多情况下是看不出来的需要手动在出错行前后“多发一个空格再删掉”有时候这种操作能刷新解析器状态其实是心理作用但确实能让你重新审视代码。第三步检查引号和括号是否匹配。转义符相关的报错绝大多数是引号闭合问题而不是转义符本身的问题。4.3 实操心得与练习建议写到这里说点我个人在带新人、带自己过程中的体会。第一这三个知识点分开看都很简单但合在一起就会形成连锁反应。我见过太多初学者在定义常量时用了转义符又在转义符字符串里写了中文全角空格然后在函数缩进上出错报错信息一串整个人就懵了。我建议你练习的时候刻意模拟这种混合错误写一段连着出错的小程序然后一条一条排查这对培养调试能力很有帮助。第二关于“要不要自己手写代码”的问题。现在AI辅助工具很多你完全可以先让AI帮你写一段示例代码但一定要自己手敲一遍而且故意敲几个错误版本看看报错信息长什么样。这个“主动踩坑”的过程比看任何教程都来得快。我有一次教一个朋友他连续写了三个不同的错误缩进版本看完报错信息后豁然开朗比之前给他讲十分钟理论都管用。第三关于编辑器的配置我再啰嗦一遍。VS Code里搜索renderWhitespace并设置为allPyCharm里在Settings → Editor → Appearance里勾选Show whitespace。这个小设置能让空格和Tab可视化直接消除了这一类视觉盲区的报错。另外强烈建议生效editor.insertSpaces: true和editor.detectIndentation: true这两项确保编译器自动把Tab转成4个空格。这能从根本上杀死Tab和空格混用的问题。最后分享一个小技巧如果你经常写Windows路径可以从一开始就统一用os.path.join()来处理路径拼接。os.path.join(C:\\workspace, logs, app.log)会自动处理斜杠不同操作系统下都能工作。避免手写路径字符串转义符带来的头疼能减少一大半。等到你能熟练用好这些看似琐碎的小工具你会发现自己写出来的Python代码终于开始有了一点“工程味”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑