本章主要内容
- 缩进和代码块构建
- 识别注释
- 给变量赋值
- 对表达式求值
- 使用常见数据类型
- 获取用户输入
- 选用正确的Python式编码风格
本章介绍最基础的Python知识,包括如何使用赋值和表达式、如何输入数字或字符串、如何在代码中标明注释等。首先将介绍Python如何组织代码块,这与其他的所有主流语言都不一样。
4.1 缩进和代码块构建
与其他大部分编程语言不一样,Python使用空白符(whitespace)和缩进来标识代码块。也就是说,循环体、else
条件从句之类的构成,都是由空白符来确定的。大部分编程语言都是使用某种大括号来标识代码块的。下面的C语言代码将会计算9的阶乘,结果保存在变量r
中:
/* C语言代码 */
int n, r;
n = 9;
r = 1;
while (n > 0) {
r *= n;
n--;
}
这里的while
循环体是用大括号包围起来的,也就是每次循环将要执行的代码。如上面的代码所示,为了能清晰地表达用途,代码一般都会多少带点缩进。但是写成以下格式也是允许的:
/* 随意缩进的C语言代码 */
int n, r;
n = 9;
r = 1;
while (n > 0) {
r *= n;
n--;
}
虽然以上代码非常难以阅读,但仍然可以正确运行。
下面是Python的等价实现:
# Python代码(赞!)
n = 9
r = 1
while n > 0:
r = r * n ⇽--- Python还支持C风格的写法r * = n
n = n – 1 ⇽--- Python还支持C风格的写法n - = 1
Python不用大括号标识代码结构,而是用缩进本身来标识。上述最后两行代码就是while
循环体,就是因为它们紧随while
语句,并且比while
语句缩进一级。如果这两行代码没做缩进,就不会构成while
循环体。
采用缩进而非大括号来标识代码结构,可能需要一些时间来习惯,但却有明显的好处。
- 不再可能有缺失或多余的大括号。再也不用一遍遍地翻看代码,只为在底部找到与前面的左括号匹配的右括号。
- 代码结构的外观直观反映了其实际结构,看一眼就可以轻松了解代码的架构。
- Python的编码风格能大致统一。换句话说,不太可能因为要看懂别人的古怪代码而抓狂。所有人的代码都很像是自己写的。
可能大家的代码已经坚持采用了缩进,所以这算不上是一大进步。如果用了IDLE,每行都会自动缩进。如果要回退缩进级别,只需要按下Backspace键即可。大多数编程用的编辑器和IDE(如Emacs、VIM和Eclipse)都提供了自动缩进功能。如果在提示符后输入命令时,前面有一个或多个空格,那么Python解释器会返回错误消息。这件事可能需要犯一两次错误才会适应。
4.2 识别注释
在大多数情况下,Python文件中符号#之后的任何内容都是注释,将会被编译器忽略。有一种情况明显例外,即字符串中的#
只是一个普通字符:
# 将5赋给x
x = 5
x = 3 # 现在x成了3
x = "# This is not a comment"
Python代码中经常会加入注释。
4.3 变量和赋值
赋值是最常用的Python命令,用法也与其他编程语言很类似。下面用Python代码新建变量x
,并赋值为5
:
x = 5
与很多其他计算机语言不同的是,Python既不需要声明变量类型,也不需要在每行代码后面添加结束符。代码换行即表示结束,变量在首次被赋值时会自动创建。
{Python中的变量:是容器(bucket)还是标签(label)?!}
在Python中“变量”这个名称或许有点儿误导性,应该叫“名称”或“标签”会更准确一些。但是,似乎所有人都习惯称为“变量”了。无论叫什么名称,都应该知道Python中的变量是如何工作的。
对变量的常见解释就是存储值的容器,有点儿像是个桶(bucket),当然这不算精确。对许多编程语言(如C语言)来说,这种解释是合理的。
但是,Python中的变量不是容器,而是指向Python对象的标签,对象位于解释器的命名空间中。任意数量的标签(或变量)可以指向同一个对象。当对象发生变化时,所有指向它的变量的值都会改变。
看过以下这段简单的代码,就能理解上述含义了:
>>> a = [1, 2, 3]
>>> b = a
>>> c = b
>>> b[1] = 5
>>> print(a, b, c)
[1, 5, 3] [1, 5, 3] [1, 5, 3]
如果将变量视为容器,以上结果就说不通了。改变了一个容器的内容,另外两个容器不应该同时发生变化。但是,如果变量只是指向对象的标签,就说得通了。3个标签都指向同一个对象,若对象发生变化,则3个标签都会反映出来。
如果变量指向的是常量或不可变值,上述区别就不是十分明显了:
>>> a = 1
>>> b = a
>>> c = b
>>> b = 5
>>> print(a, b, c)
1 5 1
因为变量指向的对象无法改变,所以变量的表现与两种解释均符合。实际上在第3行代码执行完毕后,
a
、b
和c
就全都指向了同一个不可更改的整数对象,其值为1
。下一行代码b =5
则让b
指向整数对象5
,但a
和c
的指向没有变化。
Python变量可以被设为任何对象,而在C和许多其他语言中,变量只能存储声明过的类型的值。下面的Python代码是完全合法的:
>>> x = "Hello"
>>> print(x)
Hello
>>> x = 5
>>> print(x)
5
一开始x
是指向字符串对象“Hello”
的,然后又指向了整数对象5
。当然,这种特性可能会遭到滥用,因为随意让同一个变量名先后指向不同的数据类型,可能会让代码变得难以理解。
新的赋值操作会覆盖之前所有的赋值,del
语句则会删除变量。如果在删除变量之后设法输出该变量的内容,将会引发错误,效果就像从未创建过该变量一样:
>>> x = 5
>>> print(x)
5
>>> del x
>>> print(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>
这里首先出现了跟踪信息(traceback),当检测到错误(称为异常)时就会被打印。最后一行代码显示出检测到了异常,在这里是x
的NameError
。在被删除之后,x
不再是有效的变量名了。因为在交互模式下只输出了一行代码,所以上述示例中只返回了“line 1, in <module>
”的跟踪信息。通常在错误发生时,会返回已有函数的完整动态调用层次信息。如果用了IDLE,返回的信息也是差不多的,可能会得到如下所示的代码:
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
print(x)
NameError: name 'x' is not defined
第14章将会更加详细地介绍这种错误处理机制。在Python标准库文档中,列出了所有可能出现的异常及其引发原因。请使用索引来查找收到的某个异常的信息(如NameError
)。
Python变量的名称是区分大小写的,可以包含字母、数字和下划线,但必须以字母或下划线开头。关于创建Python式风格的变量名称的更多内容,参见4.10节。
4.4 表达式
对于Python支持的算术表达式,多数读者都很熟悉。以下代码将会计算3和5的平均值,结果保存在变量z
中:
x = 3
y = 5
z = (x + y) / 2
注意,只涉及整数的算术操作符并不一定返回整数。即便所有数值全是整数,除法运算(从Python 3开始)也会返回浮点数,所以小数部分不会被截断。如果需要传统的返回截断整数的整除,可以换用操作符//
。
Python使用的是标准的算术优先规则。如果上述最后一行代码省略了圆括号,就会被计算为x +(y / 2)
。
表达式不一定只是包含数值,字符串、布尔值和许多其他类型的对象都能以各种方式在表达式中使用。后面在用到时会更详细地介绍。
动手题:变量和表达式 请在Python shell中创建一些变量。在变量名中放置空格、短线或其他非字母或数字的字符,看看会发生什么?再尝试一些复杂的表达式,例如
x = 2 + 4 * 5 - 6/3
。用圆括号对数字进行各种不同形式的分组,看看运算结果与原来未分组时的表达式有何不同。
4.5 字符串
与其他大多数编程语言一样,Python用双引号标识字符串。以下代码将字符串“Hello, World”
赋给变量x
:
x ="Hello, World"
反斜杠可用于将字符转义,赋予字符特殊的含义。\n
表示换行符,\t
表示制表符,\\\
表示反斜杠符本身。而\”
则是双引号本身,而不是字符串的结束符:
x = "\tThis string starts with a \"tab\"."
x = "This string contains a single backslash(\\\)."
可以用单引号代替双引号。以下两行代码的效果是一样的:
x = "Hello, World"
x = 'Hello, World'
它们的唯一区别是:在单引号标识的字符串中,不需要对双引号字符加反斜杠;在双引号标识的字符串中,也不需要对单引号字符加反斜杠:
x = "Don't need a backslash"
x = 'Can\'t get by without a backslash'
x = "Backslash your \" character!"
x = 'You can leave the " alone'
普通字符串不允许跨行。以下代码将是无效的:
# 以下Python代码将引发错误——不能让1个字符串跨越2行
x = "This is a misguided attempt to
put a newline into a string without using backslash-n"
但是Python支持用三重双引号标识字符串,这样字符串中不用反斜杠就能包含单引号和双引号:
x = """Starting and ending a string with triple " characters
permits embedded newlines、and the use of " and ' without
backslashes"""
现在x
包含了两个“””
之间的所有字符。可以用三重单引号‘’’
来代替双引号以达到同样的效果。
Python为字符串处理提供了足够的功能,第6章将会做专题讨论。
4.6 数值
也许大家已经对其他编程语言的标准数值操作比较熟悉了,因此本书没有用单独的章节来介绍Python的数值处理能力。本节将会介绍Python数值的独有特性,Python文档中给出了全部可用的函数。
Python提供了4种数值:整数、浮点数、复数和布尔值。整数常量就是0、-11、+33、123456之类的整数值,并且范围是无限的,大小仅受限于机器资源。浮点数可用小数点或科学计数法表示:3.14、-2E-8、2.718281828。浮点数的精度由底层硬件决定,但通常相当于C语言中的双精度(64位)类型。复数受关注的程度可能不高,本节后面将会单独讨论。布尔值是True
或False
,除是字符串形式之外,效果与1和0相同。
Python的算术操作与C语言很类似。两个整数的计算操作会生成一个整数,当然除法(/
)除外,因为除法的结果会是浮点数。如果用了除号//
,则结果会是经过截断的整数。浮点数操作则总是会返回浮点数。下面是一些例子:
>>> 5 + 2 - 3 * 2
1
>>> 5 / 2 # 普通除法将返回浮点数
2.5
>>> 5 / 2.0 # 结果还是浮点数
2.5
>>> 5 // 2 # 用'//'整除将返回截断后的整数值
2
>>> 30000000000 # 在很多编程语言中,整型是放不下的
30000000000
>>> 30000000000 * 3
90000000000
>>> 30000000000 * 3.0
90000000000.0
>>> 2.0e-8 # 科学计数法将返回浮点数
2e-08
>>> 3000000 * 3000000
9000000000000
>>> int(200.2) ⇽--- ❶
200
>>> int(2e2) ⇽--- ❶
200
>>> float(200) ⇽--- ❶
200.0
{:—}这几句代码显式在多个类型间转换❶,int
函数会将浮点数截断。
与C或Java相比,Python的数值有两个优点:整数可为任意大小,两个整数的除法结果是浮点数。
4.6.1 内置数值处理函数
Python提供了以下数值操作函数,作为其内核的一部分:
abs、divmod、float、hex、int、max、min、oct、pow、round
{:—}详情参见官方文档。
4.6.2 高级数值处理函数
Python没有内置更高级的数值处理函数,例如三角函数、双曲线三角函数,以及一些有用的常量,但它们都在标准模块math
中提供,稍后将会详细介绍模块。现在只要知道,必须在Python程序或交互式会话中执行以下语句,才能使用本节的数学函数,这就足够了。
from math import *
math
模块提供了以下函数和常量:
acos、asin、atan、atan2、ceil、cos、cosh、e、exp、fabs、floor、fmod、frexp、hypot、ldexp、
log、log10、mod、pi、pow、sin、sinh、sqrt、tan、tanh
{:—}详情参见官方文档。
4.6.3 数值计算
由于受限于运算速度,基本安装的Python不太适合执行密集型数值计算。但强大的Python扩展NumPy
高效实现了很多高级的数值处理操作。NumPy
重点实现的是数组操作,包括多维矩阵,以及快速傅里叶变换等更高级的函数。在SciPy官网中应该能够找到NumPy
或其链接。
4.6.4 复数
只要表达式带有nj
的形式,就会自动创建复数:n
与Python的整数和浮点数形式相同,j
当然就是标准的虚数表示法,等于-
1的平方根。例如:
>>> (3+2j)
(3+2j)
注意,当计算结果为复数时,Python会加上圆括号,表示显示的是个对象值:
>>> 3 + 2j - (4+4j)
(-1-2j)
>>> (1+2j) * (3+4j)
(-5+10j)
>>> 1j * 1j
(-1+0j)
计算j * j
则会如愿返回-1
,但结果仍然是Python复数型对象。复数永远不会被自动转换为等价的实数或整数对象。但可以用real
和imag
属性轻松访问到复数的实部和虚部。
>>> z = (3+5j)
>>> z.real
3.0
>>> z.imag
5.0
注意,复数的实部和虚部总是以浮点数返回。
4.6.5 高级复数函数
大多数用户都会认为,计算-
1的平方根不该有结果,而是应该报错,因此math
模块中的函数并不适用于复数,与其类似的复数函数是由cmath
模块提供的:
acos、acosh、asin、asinh、atan、atanh、cos、cosh、e、exp、log、log10、pi、sin、sinh、sqrt、
tan、tanh
为了能在代码中清晰地标识出这些特殊用途的复数函数,避免与普通的同名函数产生冲突,最好的做法是先导入cmath
模块:
import cmath
{:—}然后在用到复数函数时,显式地引用cmath
包:
>>> import cmath
>>> cmath.sqrt(-1)
1j
{尽量少用<module> import *!}
上述例子很好地说明了为什么应尽量减少
import
语句的from <module> import *
的用法。如果用from
形式先导入math
模块,再导入cmath
模块,那么cmath
模块中的函数将会覆盖math
模块的同名函数。对于阅读代码的人来说,也要花费更多的精力来找出某个函数的来源。有一些模块经过了明确设计,必须使用上例中的导入形式。有关如何使用模块和模块名称的更多详细信息,参见第10章。
重点是要记住,在导入cmath
模块之后,几乎就能对其他数值类型进行任何操作了。
动手题:字符串和数值操作 在Python shell中,创建一些字符串和数值型变量(整数、浮点数和复数)。体验一下操作的结果,包括跨类型的操作。例如,能否让字符串乘以整数,或者乘以浮点数或复数呢?接下来载入
math
模块并测试一些函数,然后载入cmath
模块并执行同名函数。当载入cmath
模块后,对整数或浮点数调用其中的函数,会产生什么结果?怎样才能让math
模块的函数重新可用呢?
4.7 None值
除字符串、数值等标准类型之外,Python还有一种特殊的基本数据类型,它定义了名为None
的特殊数据对象。顾名思义,None
用于表示空值。在Python中,None
会以各种方式存在。例如,Python中的“过程”,只是一个没有显式返回值的函数,这表示默认返回的是None
。
在日常的Python编程中,None
经常被用作占位符,用于指示数据结构中某个位置的数据将会是有意义的,即便该数据尚未被计算出来。检测None
是否存在十分简单,因为在整个Python系统中只有1个None
的实例,所有对None
的引用都指向同一个对象,None
只等价于它自身。
4.8 获取用户输入
利用input()
函数可以获取用户的输入。input()
函数可以带一个字符串参数,作为显示给用户的提示信息:
>>> name = input("Name? ")
Name? Jane
>>> print(name)
Jane
>>> age = int(input("Age? ")) ⇽--- 将输入的字符串转换为整数
Age? 28
>>> print(age)
28
>>>
这种获取用户输入的方法相当简单。用户输入是以字符串的形式获得的,所以要想用作数字,必须用int()
或float()
函数进行转换。这算得上是个小陷阱吧。
动手题:获取用户输入 体验一下用
input()
函数读取用户输入的字符串和整数。代码与上述例子类似,如果读取整数时没有在input()
外面调用int()
,那会出现什么效果?能否修改一下代码,读取一个浮点数(如28.5)?如果故意输入“错误”的数据类型会怎么样?例如,本该是整数的地方输入了浮点数,本该是数字的地方输入了字符串,反之又会如何?
4.9 内置操作符
Python提供了多种内置操作符,标准的操作符有+
、*
等,更高级的有移位、按位逻辑运算函数等。大多数操作符都不是Python独有的,其他的编程语言也提供,因此本书不再做解释。Python内置操作符的完整列表,可在官方文档中找到。
4.10 基本的Python编码风格
除明确要求用缩进来标识代码块之外,Python对编码风格的限制相对较少。即便如此,缩进量和缩进类型(制表符与空格)也没做强制性规定。不过在“Python 增强提案 8”(Python Enhancement Proposal 8,PEP 8)中,包含了推荐的编码风格规范。本书附录A有对PEP 8的概括性介绍,全文可在Python官方网站在线获取。表4-1中列出了部分Python式风格的规范,但为了能完全理解Python式风格,还请反复阅读PEP 8。
表4-1 Python式编码风格规范
场景 | 建议 | 示例 |
---|---|---|
模块/包名 | 简短、全小写、非必要时不带下划线 | imp 、sys |
函数名 | 全小写、用下划线增加可读性 | foo() 、my_func() |
变量名 | 全小写、用下划线增加可读性 | my_var |
类名 | 单词首字母大写 | MyClass |
常量名 | 全大写、下划线分隔 | PI 、TAX_RATE |
缩进 | 每级相差4个空格、不用Tab键 | |
比较操作 | 不要与True 或False 值做比较 | if my_var: if not my_var: |
强烈建议遵循PEP 8规范。因为每条规范都是精心挑选过的,并经过了时间考验,能让代码更容易被Python程序员理解。
速测题:Python风格 请在以下变量名和函数名中,选出不大符合Python风格的名称,并说明理由:
bar(
、varName
、VERYLONGVARNAME
、foobar
、longvarname
、foo_bar()
、really_very_long_var_name
。
4.11 小结
- 上面介绍的基础语法已足够开始写Python代码了。
- Python语法一目了然、始终如一。
- 由于语法没有很多新奇之处,很多程序员的上手速度快得出奇。
本文摘自《Python 快速入门》第3版
- Python编程基础教程从入门到实践书籍
- 零基础快速上手学Python核心编程
- Python软件基金会作品,提供习题答案及源代码
这是一本Python快速入门书,基于Python 3.6编写。本书分为4部分,第一部分讲解Python的基础知识,对Python进行概要的介绍;第二部分介绍Python编程的重点,涉及列表、元组、集合、字符串、字典、流程控制、函数、模块和作用域、文件系统、异常等内容;第三部分阐释Python的特性,涉及类和面向对象、正则表达式、数据类型即对象、包、Python库等内容;第四部分关注数据处理,涉及数据文件的处理、网络数据、数据的保存和数据探索,最后给出了相关的案例。
本书框架结构清晰,内容编排合理,讲解循序渐进,并结合大量示例和习题,让读者可以快速学习和掌握Python,既适合Python初学者学习,也适合作为专业程序员的简明Python参考书。