Python教程1 - 简介

这个系列的教程适用于Python新手,讲得相比其他教程可能会更深入一些,内容将依照Python官方的中文教程。我将在我的博客知乎专栏上同步发表这个系列的文章。

本文是这个系列的第1篇文章,其中第1节将介绍如何安装Python,第2节会介绍Python基本类型和函数的调用方法,最后一节我会复习下先前讲到的知识。

# 1 Python介绍

# 1.1 Python版本

Python是免费开源的编程语言。它有两个版本:2和3。其中Python 2已经计划在2020年放弃支持,不建议大家再使用。这篇及之后的教程主要教Python 3

# 1.2 安装Python

你可以从Python官网的下载页面下载并安装Python。目前(2019年11月)最新版的Python是3.8,但不够稳定。如果你需要使用之后提到的JupyterLab的话,建议安装3.7版本,目前最新的是3.7.5。对于Windows建议选择“Windows x86-64 executable installer”。当然这篇文章可能会过时,大家可以酌情选择版本安装。这里给出Python 3.7.5的Windows版下载链接

安装的时候建议把add to PATH的选项勾上。

# 1.3 如何运行Python代码

Python是脚本语言或者称之为解释型语言。所谓解释型语言是相对编译型语言的,解释型语言不需要生成可执行文件(Windows上是后缀.exe的文件)就可以运行起来,因此开发起来更快捷,但运行速度更慢。而编译型语言则相反,因而开发较为麻烦,但运行速度快。将解释型语言代码一行行运行起来的软件称为解释器。而将编译型语言代码生成可执行文件的软件称为编译器。你有以下2种方式运行你的代码:

  • 将代码直接输入在解释器中:如果你安装了官方的Python,会自带一个叫Idle的软件,打开Idle,就可以输入代码;也有可能你没有Idle,这时候你可以打开命令行(Windows上Win键+R,然后输入cmd打开命令提示符)输入python回车,然后输入代码,退出可以输入exit();之后我们还会介绍JupyterLab。
  • 将代码保存成.py后缀的代码,再运行(Windows上双击即可),如果程序没有输入,可能会一闪而过。

第1种方式适合编辑测试代码,而第2种方式适合完成代码后便于运行,此外第2种方式可以使用个人喜好的编辑器或者IDE(IDE是集成开发环境的缩写,它提供了编辑、运行、调试一体化的开发环境),并且可以使用版本控制系统(如git,版本控制系统可以维护代码的所有历史版本,并提供多人协同开发之类的功能),我们会在以后介绍PyCharm的使用。

你现在可以不必急着安装编辑器/IDE。当然,我会给出我推荐的编辑器/IDE,以下按顺序排列。这只是个人偏好,我不想引起圣战:

  1. PyCharm
  2. Visual Studio Code
  3. Visual Studio
  4. Sublime Text
  5. Idle(Python自带)
  6. Notepad++
  7. Notepad(Windows自带,如果你其他编辑器都没的话)

# 1.4 Python的包管理器

Python可以被用于写爬虫,做网站,进行数据处理或者机器学习。其强大的功能除了来自于其优雅的语法,关键还在于它有很多库,包括标准库和可以下载的扩展库。我们称这些扩展库为。而自动下载管理这些包的工具称之为包管理器。Python的包管理器叫pip,你可以打开命令行,输入pip help查看pip支持的命令。以下是最常用的下载和安装命令:

  • pip install xxx:安装xxx包;
  • pip uninstall xxx:卸载xxx包;
  • pip install -r requirements.txt:将requirements.txt列出的包全部安装,许多Python项目会将需要安装的包列出在requirements.txt中,方便大家下载。

由于pip需要从国外下载包,这样网速会非常慢,遇到这种情况的可以考虑改一个源,从国内下载。比如可以改为清华TUNA的源,执行下面的命令即可。

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# 1.5 尝试安装JupyterLab

JupyterLab可以更方便交互式地编辑运行代码。它是Jupyter Notebook的改进版。它还能显示表格和图片,如果你要做数据处理和可视化,那它会是很好的工具。它的界面是通过浏览器查看的。它将代码、输出及其他信息保存成后缀为.ipynb的格式。安装它需要费一番力气,接下来介绍如何在Windows上安装它,你大概需要7G的存储(主要是Visual Studio占的)。跳过这一节也无妨。

首先安装Visual Studio,下载链接在此处,下载Community版本就行,安装完毕之后打开Visual Studio Install,选择“桌面C++开发”,再选择安装,才算安装完毕,这是最耗时的一步。

然后安装Node.js,下载链接在此处,下载Current版本即可,安装一路默认。

最后打开命令行,输入pip install jupyterlab即可安装。

安装完毕后,在命令行输入jupyter lab就会自动打开网页。点击新建Python3 Notebook即可。

接下来介绍JupyterLab的简单用法。首先它是有自上向下排列的单元格组成的,每个单元格有输入框和输出内容。然后,其编辑是有两种模式的,一种是编辑单元格内容,一种是编辑单元格本身。编辑单元格内容模式下,会有一个单元格的输入框为白色且光标闪动,编辑文本像平常一样,可以按Tab补全,按Esc切换到编辑单元格模式;编辑单元格模式下,会有一个单元格左侧有蓝条,你可以点击x删除单元格,点击a在前方添加单元格,点击b在下方添加单元格,按Enter切换到编辑单元格模式。此外,在编辑单元格内容模式下,按Ctrl + Enter会运行单元格,而按Shift + Enter会运行并切换到下一单元格。

之后的教程我会同步发布Jupyter Notebook版本。

# 2 Python的非正式介绍

# 2.1 注释

Python的注释是以#开头到行末结束的一段文字,它会被彻底忽略,注意字符串(引号扩住的东西)中的#不会被作为注释。

# 这是注释
spam = 1  # 这也是注释
          # 这还是注释
text = "# 这不是注释"

# 2.2 数学运算

Python可以被像计算器那样使用,像下面的示例,它支持+-*/和括号,注意所有的括号均应使用圆括号。这里>>>后面的输入的内容,其他的是输出的结果。

>>> 2 + 2
4
>>> 50 - 5*6
20
>>> (50 - 5*6) / 4
5.0
>>> 8 / 5  # 除法返回浮点数(之后会介绍)
1.6

每一个值都有类型,关于类型详见2.2.2节。诸如2420的整数属于int类型,中文为整数类型,简称整型;诸如5.01.6有小数部分的值属于float类型,中文为浮点数类型,简称浮点型/运算附始终得到浮点类型,而其他数学运算如果有一个操作数是浮点数则结果是浮点数,否则为整数,关于运算符和操作数详见2.2.3。

还有求商运算(floor division向下除法,又称为整除//求余运算%

>>> 17 / 3  # 除法运算返回浮点型
5.666666666666667
>>>
>>> 17 // 3  # 求商运算会抹去小数部分
5
>>> 17 % 3  # 求余运算
2
>>> 5 * 3 + 2  # 商 * 除数 + 余
17

此外Python中还有**乘方运算符:

>>> 5 ** 2  # 5 的平方
25
>>> 2 ** 7  # 2 的 7 次方
128

接下来我们会介绍一些较为抽象,但很有帮助的概念。如果你发现接下来的部分很困难,可以跳过(2.2.1-2.2.4)。

# 2.2.1 表达式(expression)

所谓表达式,就是产生一个值的一串式子,上个例子中的2 + 28 / 5都是表达式。表达式是构建复杂程序的一个重要组成部分。表达式的一大特点是可以嵌套,如上面例子中的(50 - 5*6) / 4,其中50 - 5*6是个子表达式。

# 2.2.2 值(value)和类型(type)

每一个都有类型,类型规定了可能的取值范围和可能的操作。如我们称2的类型为int整数类型,它可以表示所有的整数,而2.0float浮点数类型,可以存储约16位有效数字。此外我们马上还会学到字符串类型,它可以用于存储一串文本,但它不能参与乘除和减法的运算。

# 2.2.3 运算符(operator)和操作数(operand)

运算符代表了要执行的运算,而操作数是运算的输入,如8 / 5中,/被称为运算符,而85就被称为操作数。

运算符可以根据其携带的操作数个数分类,有一元、二元和三元、或者称之为单目、双目和三目。之前的示例中的+-*/都是二元运算符。一元运算符的一个例子是负号和正号,它也可以嵌套成形如--+1(负的负的正的1,即+1,也就是1)。

运算符也可以根据出现的位置分类。一元运算符有前缀后缀之分,前缀出现在操作数的前面,类似的后缀出现在操作数的后面。Python中,所有的一元运算符都是前缀的(所以并没有阶乘运算符!)。二元运算符也有前缀和后缀,除此还有中缀,即出现在两个操作数的中间,Python的所有的二元表达式都是中缀的。中缀表达式主要是方便人们的理解而出现的(实际上如果都采用前缀表达式就没有优先级和结合性什么事了,当然你可以不知道我在说什么)。

当然还有运算符可能有多个组成部分,如两个配对的中括号组成的下标运算符obj[k](我们会在以后介绍,objk是变量)和函数调用运算符。运算符也可能有两个以上的字符组成,如等会儿会讲到的整除//,注意不能在这种运算符的中间加上空格。运算符也可能由一个单词(我们称之为关键字)组成,如由not组成的一元逻辑非not a(我们会在以后介绍,a是变量)。

# 2.2.4 优先级(precedence)和结合性(associativity)

当我们计算50 - 5*6,我们会自然地先计算5*6,因为我们知道先乘除后加减。先算什么后算什么,这就是优先级决定的。每个运算符都有一个优先级,优先级约高的运算符就越先算,如乘除的优先级就比加减高。而当优先级一样的运算符连接在一起的时候,如3 - 2 + 1,我们知道先算3 - 2,这就是结合性决定的。每个运算符都有结合性,结合性有两种,自左至右和自右至左。自左至右就是先算左边的,而自右至左就是先算右边。你所见的+-*/都是自左至右结合的运算符。实际上Python的二元自右向左结合的运算符只有1个,就是次方运算符**,因而2**2**32**(2**3)就是256。注意:Python的次方运算符优先级高于负号,因而-2**2的值是-4

括号能够忽略优先级,使子表达式先算,比如5 * (3 - 2),没人会再去先算5 * 3,这就是括号的作用。

当你背熟运算符优先级和结合性列表的时候,你可能会跃跃欲试,能不用括号就不用括号,然而这可能并不是个好习惯。对于一些晦涩难懂的代码而言,加上括号有时能比用注释更好地告诉别人你在做什么。

# 2.3 变量(variable)与赋值(assignment)

变量赋值这两个概念是非常重要的,可以说如果你无法理解变量和赋值,那么你就不可能学会命令式编程。

先看看如何赋值。使用单个等号=给一个变量赋值。赋值左侧是变量名,右侧是表达式。赋值之后,可以用变量名指代原先表达式的值,进而出现在新的表达式中:

>>> width = 20
>>> height = 5 * 9
>>> width * height
900

变量可以存储值,当然这个值也有类型,我们会称之为变量的类型,同一个变量可以存储不同类型的值。给变量赋予一个值的过程称为赋值。其实私底下,我个人更愿称这个行为为变量被绑定上了一个值,我认为绑定比赋值这个词描述Python处理变量更准确。因为Python的变量本质上只是指向一个值的名字,如果一个值没有变量指向,它就会被销毁(这个过程称为垃圾回收)。当发生赋值时,并不是旧的值被改成了新的值,而是变量指向了一个新的值,这时候如果旧的值没有变量指向,就会被销毁。实际上,在Python中,所有的整数、浮点数和字符串都不可被修改,修改的只是变量所指的东西。之后我们会进一步介绍类型的可变性与不可变性的相关概念,我们即将学习的列表list类型就是可变的。

赋值是一个语句,不是一个表达式,因而不能出现1 + (a = 2)之类的式子,关于语句的更多概念我们将在下一个教程中介绍。

变量名是一种标识符(identifier),所谓标识符就是各种名字的统称。合法的标识符由数字、大小写英文字母和下线符_组成,但不能以数字开头(虽然可以但不推荐用中文命名)。

最后,如果一个变量未定义(未赋值),试图使用它时会向你提示错误:

>>> n  # 试图引用一个未定义的变量
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined

在交互模式下,上一次打印出来的表达式被赋值给变量_

>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06

# 2.4 字符串(String)

字符串这种类型用于存储有限长度的文本。使用一对单引号或一对双引号括住,即可创建字符串值。单引号和双引号除了转义稍有不同,完全等价。当遇到一些不可打印或是有特殊含义的字符就需要转义,我们先看例子,会在2.4.1我来详细解释转义:

>>> 'spam eggs'  # 单引号
'spam eggs'
>>> 'doesn\'t'  # 使用 \' 表示单引号...
"doesn't"
>>> "doesn't"  # ...或者使用双引号括住字符串
"doesn't"
>>> '"Yes," they said.'
'"Yes," they said.'
>>> "\"Yes,\" they said."
'"Yes," they said.'
>>> '"Isn\'t," they said.'
'"Isn\'t," they said.'

接下来我们也会介绍一些概念,2.4.1需要一定的理解,而2.4.2和2.4.3如果有阅读困难可以跳过。

# 2.4.1 转义(Escape)

转义字符是用一串文本来代表一个字符的方式,所有的转义序列都以反斜杠符\开头,可以大致分为以下几类:

  • 不可打印或不方便书写的字符:诸如换行\n和Tab\t之类的字符不方便在代码中书写出来,因而我们就用一串文本来表示这一个字符,关于换行、回车、Tab的介绍在后面。
  • 特殊含义的字符:有三个这种情况的:
    • 反斜杠符\\,由于它本身被作为转义字符的开始,打印反斜杠符就不得不转义了;
    • 如果字符串使用单引号括住,这时候字符串中的单引号作为字符串的起始和终止符也需要转义成\'
    • 类似地,如果字符串使用双引号括住,这时候字符串中的双引号也需要转义成\"
  • 字符编码表示:计算机只能存储二进制,因而每个字符都被编码成了一个整数,Python每个字符对应4字节的整数。我们可以直接在字符串中输入包含编码的转义序列得到对应的字符,有4种这样的转义序列,我们会在2.4.3进一步讨论:
    • 形如\ooo\后面跟1到3位8进制数,得到对应的1字节字符,其他3字节为0;
    • 形如\xhh\x后面根2位16进制数,得到对应的1字节字符,其他3字节为0;
    • 形如\uxxxx\u后面跟4位16进制数,得到对应的2字节字符,其他2字节为0;
    • 形如\Uxxxxxxxx\U后面跟8位16进制数,得到对应的4字节字符。

对于最后一种用整数表示字符编码的方式,我们给一些例子:

>>> '\101' # A 对应的编码是 65,而 1 * 64 + 1 = 101
'A'
>>> '\x41' # 4 * 16 + 1 = 101
'A'
>>> '\u6211' # “我”对应的编码是 25105,而 6 * 4096 + 2 * 256 + 1 * 16 + 1 = 25105
'我'
>>> '\U0001f60b' # 和上面一样的原理,“😋”的编码是 128523,这是一个emoji
'😋'

注意,转义序列对应的字符长度还为1,上面4个例子中的所有字符串长度都为1,即只包含一个字符。2.5会介绍3个函数,len获取字符串的长度、ord获取字符对应的整数编码,chr获取整数编码对应的字符。

最后我列出所有的转义字符列表,你不需要记住每一项:

转义序列 含义
\紧跟换行 \和换行被忽略
\\ 反斜杠符 (\)
\' 单引号 (')
\" 双引号 (")
\a ASCII 响铃 (Bell,BEL)
\b ASCII 退格 (Backspace,BS)
\f ASCII 换页 (Formfeed,FF)
\n ASCII 换行 (Linefeed,LF)
\r ASCII 回车 (Carriage Return,CR)
\t ASCII 水平制表符 (Horizontal Tab,TAB)
\v ASCII 垂直制表符 (Vertical Tab,VT)
\ooo 8进制值为 ooo 的字符
\xhh 16进制值为 hh 的字符
\N{name} 名字为name的 Unicode 字符
\uxxxx 16进制值为 xxxx 的16位字符
\Uxxxxxxxx 16进制值为 xxxxxxxx 的32位字符

# 2.4.2 空白符

换行\r、回车\n、水平制表\t等一些字符被称为空白符,实际生活中常用的空白符就这3个。

在Windows上打开记事本,按下Tab键后,光标会向右跳过约8个字符的位置,你就输入了水平制表符。它被用于将不同行的字对齐到同一列上,制作表格。然后你可以在Python中尝试下一个例子:

>>> print('1\t2\n34\t5')
1       2
34      5

print函会将字符串打印出来。这里我们输出了两行,第一行是数字1跟水平制表符再跟2,第二行是数字34跟水平制表符再跟5。可以看出即使前面字符的长度不同,水平制表符还会把它之后的字符对齐到下一个8个字符的位置。

当你在记事本敲入回车,你的光标会到下一行,这个时候你同时输入了两个字符:回车\r之后换行\n。在Windows上,文本文件的换行其实包含了这两个字符,即CRLF。但在别的系统不是这样的,Linux的文本文件以\n换行,即LF。而Mac的文本文件又以\r换行,即CR。各个系统之间不同的换行方式其实带来了一些问题,我们会在以后关于文件IO的部分更细致的谈到这些问题。说了这么多,可能很多人还不知道回车换行这两个字符的含义,这要追溯到计算机出现之前,那时候有电传打字机。在那个时代,回车是让打字机回到行首,而换行则是让打字机移到下一行。虽然电传打字机消失了,但回车和换行还是遗留到了现在。

# 2.4.3 进制、字节、码位与编码

关于2进制、8进制、16进制的数学含义,我就留给数学老师来讲解了,如果这一段阅读有困难可以忽略。这里指出一下16进制使用额外的A到F(小写亦可)分别表示10到15。1个字节是8个比特,而16进制1位能表示2进制的4位,因而1个字节可以对应2位16进制。因此,大家更喜欢16进制,8进制并不常用。

接下来我们谈谈字符编码,ASCII是最早出现的编码规范,它使用了7个比特存储字符,里面的字符只有很少的标点符号、英文、数字等。

Unicode是之后出现的编码规范,它包含了中日韩表意文字、甚至还有emoji。每个Unicode字符都与一个32位的整数对应(因而它可以表达2的32次方个字符,目前还留有很大的空间供未来使用),这个整数称为码位(codepoint)。Python的字符串内部就是存储了这些码位。然而如果直接每个字符32位存储在文本文件里,会浪费很多空间(因为实际上除了emoji和少数汉子,几乎所有的字符都最多只用了16位),因而我们采用变长字符编码来将一个码位映射成多个字节。其中最著名的编码方案就是UTF-8,它会把32位的码位映射成1到6个字节,而且它与ASCII完全兼容。

在Python中,字符串通过encode()函数编码成实际存在在文件中的bytes类型,而bytes类型通过decode()函数编码成字符串,我们在2.5节会给出例子。

然而在Unicode出现之前就有了中文Windows系统,那时候中文都采用国标码GBK编码。这里就不再赘述。当你的代码保存成文件后,如果你使用了中文做字符串,那么声明你代码所用的编码可能是必须的。你需要在代码的最开始加上一行如下的声明:

# -*- coding: encoding -*-

其中encoding改成你的编码方式,比如gbk或者utf-8

# 2.4.4 字面量(Literal)

字面量是指源代码中用于表示一个值的一串文本,它也有类型。比如1就是int类型字面量,而1.0就是float类型字面量,还有'abc'就是字符串类型字面量。

# 2.5 函数(Function)

函数是许多编程语言的又一个重要组成部分。它是一种特殊的类型,支持被调用。它将一系列的操作封装、复用。这里我们不会涉及如何编写函数,只会谈谈如何使用它。

首先我们来谈谈普通函数的调用,它的形式类似于func(arg1, arg2, ...),先写函数名fun,后面再用括号括住逗号分割的列表,我们称之为参数。函数调用的本质也是个运算符,他拥有很高的优先级,其中的funcarg1都可以为子表达式。不同的函数会有不同的参数个数和参数类型的要求。类似数学的函数,调用一些函数会得到值,我们称为返回值。我们来看例子,print函数接受多个参数,参数没有类型限制,不返回值(也可以认为返回空),它会将所有的参数输出出来,并在最后加上换行:

>>> print(3, 'is odd')
3 is odd
>>> print('123\n456')
123
456

最后一个例子我们调用print输出了123、换行和456。接下来我们再介绍3个函数:

  • len:接受1个字符串类型的参数,返回字符串的长度;
  • ord:接受1个长度为1的字符串,返回字符串中字符的码位;
  • chr:接受1个整数,返回整数对应的字符组成的长度为1的字符串。

我们可以说ordchr函数互为反函数。看例子:

>>> len('') # 空字符串
0
>>> len('123') # 长度为3
3
>>> ord('😋')
128523
>>> chr(128523)
'😋'

然后,还有一类特殊的函数叫方法,它是某一类型拥有的函数,它的调用形如obj.method(arg1, arg2, ...),与普通函数的差别是多了obj.obj是某一类型的值(或者产生该类型的值的表达式,包括变量)。其实obj.method是个子表达式,它获得一个函数,然后和后面的函数调用运算符构成了方法的调用。这里介绍两个方法:

  • 字符串类型的encode:可以接受一个代表编码的参数,将字符串转化为该编码的bytes类型返回。
  • bytes类型的decode:可以接受一个代表编码的参数,将bytes解码为该编码的字符串类型返回。
>>> '😋'.encode('utf-8')   # 字符串用 utf-8 编码成 bytes
b'\xf0\x9f\x98\x8b'
>>> _.decode('utf-8')      # bytes 再用 utf-8 解码成字符串
'😋'

这里我们可以看出这个这个emoji被编码成了4个字节,然而我们可以注意到这个编码f09f988b(16进制)和它的Unicode码位0001f60b(16进制)是不同的。

# 2.6 字符串(续)

我们继续讲解字符串。先前讲到字符串的转义,然而有时候我们不需要转义,希望让\直接代表它本身(这种情况多发生在Windows路径和正则表达式中),我们可以使用原始字符串(raw string),只需要在字符串的前面加上r即可:

>>> print('C:\some\name')  # 这里 \n 表示换行!
C:\some
ame
>>> print(r'C:\some\name')  # 引号前有 r
C:\some\name

普通的字符串是不能跨行的(除了\紧跟换行这种转义),而Python也提供了三重引号用于跨行输入的多行字符串,如"""..."""'''...'''...表示内容)。字符串的中的回车会被包含到字符串中,如果你不想包含换行可以在行尾添加一个\,这其实就是上面转义表中的第一行转义。如下:

print("""\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")

上面的代码会产生如下输出:

Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to

注意代码中的第一个换行和反斜杠符一起被消去了。

接下来我们来介绍字符串支持的一些操作:字符串和字符串之间可以用+连接;字符串和整数做乘法可以使字符串重复整数次。如下:

>>> # 'un' 重复 3 次,而后拼接上 'ium'
>>> 3 * 'un' + 'ium'
'unununium'

相邻的字符串字面值可以不用+就串联起来,这拥有最高的优先级。注意只有字面量有这种行为,变量和字面量不能这样连接:

>>> 'Py' 'thon'
'Python'
>>> prefix = 'Py'
>>> prefix 'thon'  # 无法将变量和字符串字面常量串接在一起
  File "<stdin>", line 1
    prefix 'thon'
                ^
SyntaxError: invalid syntax

接下来我们来讲字符串的的索引和切片操作。有时,我们需要将字符串的某个位置或某一部分取出来,这就需要索引(index)和切片(slice)操作。

在字符串后面用中括号括住一个整数,就可以取出整数对应位置的字符,类似text[n]。索引是从0开始到字符串长度减1,索引也可以是负数,表示倒数第几个字符,是从-1到负的字符串长度。超出这个范围的索引是个错误。注意Python没有单独的字符类型,字符就是长度为1的字符串:

>>> word = 'Python' # 长度为 6
>>> word[0]  # 位置为 0 的字符
'P'
>>> word[5]  # 位置为 5 的字符(最后一个字符)
'n'
>>> word[-1]  # 倒数第 1 个字符
'n'
>>> word[-2]  # 倒数第 2 个字符
'o'
>>> word[-6]  # 倒数第 6 个字符(第一个字符)
'P'

可以用下面的图来表示Python这个字符串中各个字符的位置,这里位置6不能用于索引,但可以用于表示切片的结尾:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5   6
  -6  -5  -4  -3  -2  -1

接下来是切片,切片是中括号括住冒号分隔的两个整数,它返回一段字符串,类似text[a:b]。它会取出索引值为[a, b)区间(左闭右开)内的所有字符(b的值可以为字符串的长度)。此外ab都可以省略,省略a就相当于a为0(切片从头开始),省略b就相当于b为字符串长度(切片一直到尾结束)。与索引不同,切片的起始和终止都可以超过范围,如下:

>>> word[42]  # the word only has 6 characters
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
>>> word[4:42]
'on'
>>> word[42:]
''

如果你第一次学习编程,或者一直使用类似MATLAB的软件,你可能很好奇为什么要从0开始数数,又为什么使用左闭右开的区间。实际上从0会更加方便,以后会体现出来。

Python的字符串是不可变的,因而以下的操作是个错误:

>>> word[0] = 'J'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> word[2:] = 'py'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

但你可以创建字符串,并将它赋值给新的变量:

>>> word = 'J' + word[1:]
>>> word
'Jython'
>>> word = word[:2] + 'py'
>>> word
'Jypy'

# 2.7 列表(List)

通过方括号括起,逗号分隔的一组值,就可以得到列表字面量,形如[a, b, ...., n]。一个列表可以包含不同类型的元素,但通常各个元素类型相同。

>>> squares = [1, 4, 9, 16, 25]
>>> squares
[1, 4, 9, 16, 25]

和字符串一样,列表支持索引切片,不过索引返回的是元素:

>>> squares[0]  # 索引返回元素
1
>>> squares[-1]
25
>>> squares[-3:]  # 切片返回新的列表
[9, 16, 25]

完整的切片会返回列表的浅拷贝,形如squares[:]。所谓浅拷贝就是返回的是个新列表,但两个列表中所指的元素是一样的,我们会在之后给出一个例子。

列表支持+的拼接操作和*的重复操作,如下:

>>> [1] * 3 + [2]
[1, 1, 1, 2]

列表是可变的,因而可以对列表的索引赋值,其结果是元素被替换:

>>> cubes = [1, 8, 27, 65, 125]  # 位置 3 的值是错的
>>> cubes[3] = 64  # 替换掉错误的值
>>> cubes
[1, 8, 27, 64, 125]

通过append()方法可以为列表添加元素,这会改列表本身:

>>> cubes.append(216)  # 添加 6 的 3 次方
>>> cubes.append(7 ** 3)  # 添加 7 的 3 次方
>>> cubes
[1, 8, 27, 64, 125, 216, 343]

切片赋值也是可以的,这可能会改变列表的大小,或者把列表清空:

>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> letters
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> # 替换掉一些值
>>> letters[2:5] = ['C', 'D', 'E']
>>> letters
['a', 'b', 'C', 'D', 'E', 'f', 'g']
>>> # 移除这些值
>>> letters[2:5] = []
>>> letters
['a', 'b', 'f', 'g']
>>> # 清空列表
>>> letters[:] = []
>>> letters
[]

函数len()也可以用于获取列表的长度

>>> letters = ['a', 'b', 'c', 'd']
>>> len(letters)
4

列表也可以嵌套列表,甚至嵌套它自己,如下:

>>> a = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> x = [a, n]
>>> x
[['a', 'b', 'c'], [1, 2, 3]]
>>> x[0]
['a', 'b', 'c']
>>> x[0][1]
'b'

最后我们为浅拷贝给出一些例子:

>>> b = a = ['a', 'b', 'c'] # 让 a 和 b 指向同一列表
>>> a[:] = []               # 清空 a 指向的列表
>>> b                       # b 也为空
[]
>>> # 采用浅拷贝,这里给出个为啥叫“拷贝”的例子
>>> a = ['a', 'b', 'c']
>>> b = a[:]                # b 是 a 的浅拷贝
>>> a[:] = []               # 清空 a 指向的列表
>>> b                       # b 不为空
['a', 'b', 'c']
>>> # 再给出一个为啥叫“浅”的例子
>>> a = [1, [2]]
>>> b = a[:]
>>> a[0] = 3                # a 修改了但 b 没有修改
>>> b
[1, [2]]
>>> a[1][0] = 4
>>> b
[1, [3]]

最后一个例子可能对初学者有点困惑,我们把最后的变量布局画出来,浅拷贝只是创建了新列表,但新列表所指向的元素和旧列表是公用的,如果元素都是不可变的可能感受不到这个差异,但如果元素是可变的,那么修改旧列表中的该元素,新列表也会改变:

     +---+---+
a -> | 3 |   |
     +---+-+-+
           |     +---+
           +---> | 4 |
           |     +---+
     +---+-+-+
b -> | 1 |   |
     +---+---+

# 3 复习

# 3.1 概念重提

有两种方法运行Python代码

  • 交互运行:好处是方便测试,有以下几种:
    • 用Idle:只有Windows可用,有自动补全;
    • 在命令行里用Python:跨平台,无自动补全;
    • 在命令行里启动JupyteLab,并在浏览器中使用:有最好的交互体验,文件后缀为.ipynb
  • 保存执行:好处是方便使用,可以选择编辑器/IDE,并搭配版本控制系统,文件后缀为.py

运行Python代码的软件称为Python解释器。Python的扩展库称为,管理这些包的软件叫包管理器,Python自带的包管理器叫pip。你可以在命令行中输入以下命令安装/卸载包:

  • pip install xxx:安装xxx包;
  • pip uninstall xxx:卸载xxx包;
  • pip install -r requirements.txt:将requirements.txt列出的包全部安装。

关于JupyterLab的安装和使用这里不再重复。

Python的注释#开头,到行尾结束。

Python对于数学类型支持+-*///(整除)、%(求余)和**(乘方)。数学类型有整型浮点型

使用赋值语句可以将值绑定到变量。交互模式下,_变量存储上一个表达式的值。

字符串字面量可以由双引号和单引号括住,特殊的字符需要转义。除了普通的字符串字面量,还有原始字符串多行字符串。字符串支持数乘重复和加法拼接、索引切片操作。

函数是一种特殊的类型,它可以被调用。函数可以分为普通函数方法。有以下函数或方法:

  • print()打印一系列的值;
  • len():获得字符串或列表的长度;
  • ord():返回字符的码位;
  • chr():根据码位返回字符;
  • 字符串的encode():将字符串转换成bytes;
  • bytes的decode():将bytes转换为字符串;
  • 列表的append():向列表追加元素。

列表是可变的类型,同样支持数乘重复和加法拼接、索引切片操作。由于可变,它的索引和切片都可以被赋值。它存储的不必是同一类型,可以构成嵌套列表。

# 3.2 术语表

术语 英文 含义
表达式 expression 产生一个值的一串式子
类型 type 每个值有类型,类型决定了操作和取值范围
整型 int 存储整数的类型
浮点型 float 存储实数的类型,会有精度损失
运算符 operator 代表所要进行的运算
操作数 operand 代表运算的输入,是一种值
优先级 precedence 决定不同运算符运算先后顺序的主要因素
结合性 associativity 决定同优先级运算符运算的顺序
变量 variable 能代表值的一个名字
赋值 assignment 将值绑定到名字的操作
标识符 identifier 名字的统称
字符串 string 存储文本的类型
转义 escape 用一串文本代表一个字符
字面量 literal 代码中代表一个值的文本
函数 function 一种类型,可被调用,接受多个值,返回一个值
参数 parameter 函数接受的值
返回值 return value 函数返回的值
方法 method 一种特殊的函数,属于某一类型
浅拷贝 shallow copy 一种只拷贝最外层成员的拷贝方法
2016-2020 Ziping Sun
京ICP备 17062397号