Python基础手册4——文本结构

简单来讲,Python程序可以分解成模块(文件)、语句、表达式以及对象(数据):
1、程序由模块组成
2、模块包含语句
3、语句包含表达式
4、表达式建立并处理对象。

所以,Python 代码主要由两部分构成:语句和表达式(函数、算术表达式等)。
语句使用关键字来组成命令,类似告诉解释器一个命令。你告诉 Python 做什么,它就为你做什么,语句可以有输出,也可以没有输出。
例如: if a > b : print("Hello World!")

而表达式没有关键字。它们可以是使用数学运算符构成的算术表达式,也可以是使用括号调用的函数。它们可以接受用户输入,也可以不接受用户输入,有些会有输出,有些则没有(在 Python 中未指定返回值的函数会自动返回 None,等价于 NULL)。
例如:a*b (1, 23, 34, 45) abs(-4)

一、编码声明

Python3中是按照编码声明给出的编码来读取源文件的,默认值为UTF-8。在此编码下,全世界大多数语言的字符可以同时用在字符串、标识符和注释中。尽管 Python 标准库仅使用英文字母字符做为标识符,这只是任何可移植代码应该遵守的约定。如果要正确的显示所有的字符,你的编辑器必须能识别出文件是 UTF-8 编码,并且它使用的字体能支持文件中所有的字符。

你可以为源文件指定不同的字符编码。在 #! 行(首行)后插入一行特殊的注释行来显示的定义源文件的编码:
# -- coding: <encoding-name> --

通过此声明,源文件中所有的文本都会被当做用 encoding 指代的 UTF-8 编码对待。这个特殊的编码注释必须在文件中的 第一 或 第二 行定义。如果编码声明在第二行,那么第一行必须为注释行。

在Python的文件中,空白行会被解释器忽略(但在交互模式提示符下不会)。语句和表达式中的空格几乎都忽略(除了在字符串常量内,以及用在缩进时不会)。# 号注释总是忽略。文档字符串会忽略,但是会在运行时自动将其附加到对象上,而且能由文档工具显示。

二、逻辑行和物理行

Python程序由多个逻辑行构成,逻辑行的结束由 NEWLINE 词符表示。通过遵循显式或隐式行连接规则,一个逻辑行(Python语法定义的一行语句)可以由一个或多个物理行(我们写的代码实际所占的一行位置)构成。

物理行是由行尾序列(Linux中的换行符 \n)终止的字符序列。Python会忽略只包含空格,制表符,换行符和注释的逻辑行(即不生成NEWLINE词符号)。

显示行连接(\)

Python 语句,一般使用换行分隔,也就是说一个物理行为一个逻辑行。两个或更多 物理行可以使用反斜杠字符 (\)加入一个逻辑行,也就是让一条语句横跨多行。

注:这种方法已经过时了,目前从某种程度上来说,不再提倡使用这种方法,因为关注并维护反斜线比较困难,而且这种做法相当脆弱(反斜线之后可能有空格)。

隐式行连接

在使用闭合操作符时,单一语句可以跨多行书写(一个逻辑行上的语句存在于多个物理行)。例如:包含在小括号( )、中括号[ ]、花括号{ } 中时可以多行书写,将要换行的语句可在任何缩进层次开始,但是最好让他们和上一行垂直对其以便于阅读,Python会在到达你输入的闭合括号 ) 、] 和 } 时结束当前语句。另外就是三引号包含下的字符串也可以跨行书写。

如果要在使用反斜线换行和使用括号元素换行作一个选择,我们推荐使用括号,这样可读性会更好。



三、缩进

Python的代码的层次关系是通过同样深度的空格或制表符缩进(也就是程序代码左侧的空白空间)来体现的,缩进程度相同的一组语句构成一个代码块(组)。同一代码块的代码行必须严格左对齐(左边有同样多的空格或同样多的制表符),如果不严格遵守这个规则,同一组的代码就可能被当成另一个组,甚至会导致语法错误。

代码块会在文件末尾或者触碰到缩进量较少的行时结束,而更深的嵌套块就是比所在块的语句进一步向右缩进。通常来说,顶层(无嵌套)代码必须于第一层缩进(一般为没有缩进)开始。

随着缩进深度的增加,代码块的层次也在加深,没有缩进的代码块是最高层次的,别称为脚本的“main”部分。

使用缩进对齐这种方式组织代码,不但代码风格优雅,而且也大大提高了代码的可读性。由于 Python 只使用缩进方式表达代码块逻辑,因此“神圣的大括号战争”永远不会发生在 Python 身上。

注意:由于不同的平台的制表符换算成的空格数并不完全相同,如果你的代码要跨平台应用,或者会被不同的编辑器读写,建议你不要使用制表符。由于使用1个空格过短,使用8个空格又过长,所以我们常用 4个空格 来表示一级缩进。

四、注释(#)和 文档字符串

我们使用字符井号 #,来注释一个物理行,注释可以在一行的任何地方开始,解释器会忽略掉该行 # 之后的所有内容(只要#不是位于字符串常量中)。

以反斜杠连接的新的物理行的行不能被注释,反斜杠不会继续注释。

有一种叫做文档字符串的特别注释。你可以在模块、类或者函数的起始添加一个字符串,起到说明文档的功能。与普通注释不同,文档字符串可以在运行时访问,也可以用来自动生成文档。

# 注释只能在源代码中看到,要编写能够更广泛使用的注释,请使用文档字符串。通常来讲,文档字符串最适于大型功能的文档(例如,“我的文件、类、函数做这些事”),而 # 注释最适合用于较小功能的文档(例如:“这个表达式做这些事”)

五、同一行书写多个语句(;)

分号;允许你将多个语句写在同一行上,语句之间用分号隔开。而这些语句不能是复合语句也就是说不能在这行开始一个新的代码块。

注意: 同一行上书写多个语句会大大降低代码的可读性, Python 虽然允许但不提倡你这么做。

六、模块

每一个 Python 文件都可以被当成是一个模块,模块以磁盘文件的形式存在。当一个模块变得过大,并且驱动了太多功能的话,就应该考虑拆一些代码出来另外建一个模块。模块里的代码可以是一段直接执行的脚本,也可以是一堆类似库函数的代码,从而可以被别的模块导入(import)调用。

七、文档

Python 还提供了一个机制,可以通过 doc 特别变量,动态获得文档字串。在模块、类声明或函数声明中,Python会自动的封装第一个没有赋值的字符串(通常使用三对引号表示)并赋值给对应对象的 obj.doc 属性,使得我们可以在程序运行时可以通过访问对象的doc属性查看对象的文档说明。其中 obj是一个模块,类,或函数的名字。

Python还在标准库中附带了PyDoc工具,它知道如何提取文档字符串并且自动提取其结构化的信息,并将其格式化成各种类型的排列友好的报表。我们可以是引用内置的help() 函数来调用PyDoc提供的接口,从而生成简单的文字报表。



八、模块(文件)结构和布局

用模块(文件)来合理组织你的 Python 代码是简单又自然的方法。 你应该建立一种统一且容易阅读的结构,并将它应用到每一个文件中去。下面就是一种非常合理的布局。



(1) 起始行

通常只有在类 Unix 环境下才使用起始行,有起始行就能够仅输入脚本名字来执行脚本,无需直接调用解释器。

(2)模块文档

简要介绍模块的功能及重要全局变量的含义,模块外可通过 module.__doc__ 访问这些内容。

(3)模块导入

导入当前模块的代码需要的所有模块。每个模块仅导入一次(当前模块被加载时),函数内部的模块导入代码不会被执行, 除非该函数正在执行。

(4)变量定义

这里定义的变量为全局变量, 本模块中的所有函数都可直接使用。从好的编程风格角度说,除非必须,否则就要尽量使用局部变量代替全局变量,如果坚持这样做,你的代码就不但容易维护,而且还可以提高性能并节省内存。

(5)类定义语句

所有的类都需要在这里定义。当模块被导入时 class 语句会被执行, 类也就会被定义。类的文档变量是 class.__doc__

(6)函数定义语句

此处定义的函数可以通过 module.function() 在外部被访问到,当模块被导入时 def 语句会被执行, 函数也就都会定义好,函数的文档变量是 function.__doc__

(7) 主程序

无论这个模块是被别的模块导入还是作为脚本直接执行,都会执行这部分代码。通常这里不会有太多功能性代码,而是根据执行的模式调用不同的函数。

注意: 主程序调用 main()函数
主程序代码通常都和你前面看到的代码相似,检查 name 变量的值然后再执行相应的调用。主程序中的代码通常包括变量赋值, 类定义和函数定义,随后检查 name 来决定是否调用另一个函数(通常调用 main()函数)来完成该模块的功能。主程序通常都是做这些事。 不管用什么名字, 我们想强调一点那就是:这儿是放置测试代码的好地方。大部分的 Python 模块都是用于导入调用的,直接运行模块应该调用该模块的回归测试代码。

很多项目都是只有一个主程序,由它导入所有需要的模块。所以请记住,绝大部分的模块创建的目的是为了被别人调用而不是作为独立执行的脚本。 我们也很可能创建一个 Python 库风格的模块,这种模块的创建目的就是为了被其他模块调用。总之,只有一个模块,也就是包含主程序的模块会被直接执行,或由用户通过命令行执行,或作为批处理执行, 或由 Unix cron 任务定时执行,或通过 Web 服务器调用,或通过 GUI 执行。
时刻记住一个事实,那就是所有的模块都有能力来执行代码。最高级别的 Python 语句,也就是说, 那些没有缩进的代码行在模块被导入时就会执行, 不管是不是真的需要执行。由于有这样一个“特性”,比较安全的写代码的方式就是除了那些真正需要执行的代码以外, 几乎所有的功能代码都在函数当中。再说一遍, 通常只有主程序模块中有大量的顶级可执行代码,所有其它被导入的模块只应该有很少的顶级执行代码,所有的功能代码都应该封装在函数或类当中。

注意: __name__ 指示模块应如何被加载
由于主程序代码无论模块是被导入还是被直接执行都会运行, 我们必须知道模块如何决定运行方向。一个应用程序可能需要导入另一个应用程序的一个模块,以便重用一些有用的代码(否则就只能用拷贝粘贴那种非面向对象的愚蠢手段)。 这种情况下,你只想访问那些位于其它应用程序中的代码,而不是想运行那个应用程序。因此一个问题出现了,“Python 是否有一种方法能在运行时检测该模块是被导入还是被直接执行呢?” 判断 name 系统变量就是正确答案。

如果模块是被导入, name 的值为模块名字
如果模块是被直接执行, name 的值为 ' main '

九、在主程序中书写测试代码

优秀的程序员和软件工程师,总是会为我们的应用程序提供一组测试代码或者简单教程。对那些仅仅为了让别的程序导入而创建的模块来说, Python 有效的简化了这个任务。这些模块理论上永远不会被直接执行, 那么,可以让这个模块在被直接执行时进行系统测试,而且这样设置起来一点儿也不难。
测试代码仅当该文件被直接执行时运行, 也就是说不是被别的模块导入时。上文及核心笔记中提到如何判断一个模块是被直接运行还是被导入。我们应该利用 name 变量这个有利条件。将测试代码放在一个或者叫 main(), 或者叫 test()(或者你随便取个啥名字)的函数中,如果该模块是被当成脚本运行, 就调用这个函数。
这些测试代码应该随着测试条件及测试结果的变更及时修改, 每次代码更新都应该运行这些测试代码,以确认修改没有引发新问题。只要坚持这样做,你的代码就会足够健壮,更不用提验证和测试新特性和更新了。
在主程序中放置测试代码是测试模块的简单快捷的手段。Python 标准库中还提供了unittest 模块, 有时候它被称为 PyUnit, 是一个测试框架。当需要对一个大系统的组件进行正规系统的回规测试时,它就会派上场。


《Python基础手册》系列:

Python基础手册 1 —— Python语言介绍
Python基础手册 2 —— Python 环境搭建(Linux)
Python基础手册 3 —— Python解释器
Python基础手册 4 —— 文本结构
Python基础手册 5 —— 标识符和关键字
Python基础手册 6 —— 操作符
Python基础手册 7 —— 内建函数
Python基础手册 8 —— Python对象
Python基础手册 9 —— 数字类型
Python基础手册10 —— 序列(字符串)
Python基础手册11 —— 序列(元组&列表)
Python基础手册12 —— 序列(类型操作)
Python基础手册13 —— 映射(字典)
Python基础手册14 —— 集合
Python基础手册15 —— 解析
Python基础手册16 —— 文件
Python基础手册17 —— 简单语句
Python基础手册18 —— 复合语句(流程控制语句)
Python基础手册19 —— 迭代器
Python基础手册20 —— 生成器
Python基础手册21 —— 函数的定义
Python基础手册22 —— 函数的参数
Python基础手册23 —— 函数的调用
Python基础手册24 —— 函数中变量的作用域
Python基础手册25 —— 装饰器
Python基础手册26 —— 错误 & 异常
Python基础手册27 —— 模块
Python基础手册28 —— 模块的高级概念
Python基础手册29 —— 包

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容