安装

安装完成以后,打开 Windows 命令提示符(Win+R),在窗口中输入python命令(注意字母p是小写的),如果出现 Python 的版本信息,并看到命令提示符>>>,就说明安装成功了。

安装成功后可以输入,确定程序的版本

  1. python --version

按下Ctrl+Z快捷键,或者输入 exit() 命令即可退出交互式编程环境,回到 Windows 命令行程序。

安装完开发环境后可以得到以下几个程序:

Python基础 - 图1

IDLE:它是一个 Python Shell (可以在打开的 IDLE 窗口的标题栏上看到),程序开发人员可以利用 Python Shell 与 Python 交互。

Python 3.10(64-bit):Python自带命令行

Python 3.10 Manuals(64-bit):离线文档

Python 3.10 Manuals Docs(64-bit):

Python开发工具

IDLE - 自带的集成开发工具

IDLE是安装Python环境时自带的集成开发工具,如下图所示。但是由于IDLE的用户体验并不是那么好所以很少在实际开发中被采用。

IPython - 更好的交互式编程工具

IPython是一种基于Python的交互式解释器。相较于原生的Python交互式环境,IPython提供了更为强大的编辑和交互功能。可以通过Python的包管理工具pip安装IPython,具体的操作如下所示。

  1. pip install ipython

  1. pip3 install ipython

安装成功后,可以通过ipython命令启动IPython

Vs Code - 万能编辑器

PyCharm - Python开发神器

初识Python

编写Python源代码

可以用文本编辑工具(推荐使用SublimeVisual Studio Code等高级文本编辑工具)编写Python源代码并用py作为后缀名保存该文件。

Hello Word

编写Python代码

  1. print('hello, world!')

运行程序

切换到源代码所在的目录并执行下面的命令,看看屏幕上是否输出了”hello, world!”。

  1. python hello.py

  1. python3 hello.py

注释

注释是编程语言的一个重要组成部分,用于在源代码中解释代码的作用从而增强程序的可读性和可维护性,当然也可以将源代码中不需要参与运行的代码段通过注释来去掉,这一点在调试程序的时候经常用到。注释在随源代码进入预处理器或编译时会被移除,不会在目标代码中保留也不会影响程序的执行结果。

  1. 单行注释 :以#和空格开头的部分
  2. 多行注释 :三个引号开头,三个引号结尾
  1. print("hello Python");
  2. # print("你好,Python")
  3. """
  4. Author: Chen
  5. Version: 1.0
  6. 第一个Python程序 - hello, Python!
  7. """

小练习

  1. 在Python交互式环境中输入下面的代码并查看结果,请尝试将看到的内容翻译成中文。
  1. import this
  1. 学习使用turtle在屏幕上绘制图形。

说明:turtle是Python内置的一个非常有趣的模块,特别适合对计算机程序设计进行初体验的小伙伴,它最早是Logo语言的一部分,Logo语言是Wally Feurzig和Seymour Papert在1966发明的编程语言。


变量和数据类型

变量命名

对于每个变量我们需要给它取一个名字,就如同我们每个人都有属于自己的响亮的名字一样。在Python中,变量命名需要遵循以下这些必须遵守硬性规则和强烈建议遵守的非硬性规则。

  • 硬性规则:
    • 变量名由字母(广义的Unicode字符,不包括特殊字符)、数字和下划线构成,数字不能开头。
    • 大小写敏感(大写的a和小写的A是两个不同的变量)。
    • 不要跟关键字(有特殊含义的单词,后面会讲到)和系统保留字(如函数、模块等的名字)冲突。
  • PEP 8要求:
    • 用小写字母拼写,多个单词用下划线连接。
    • 受保护的实例属性用单个下划线开头(后面会讲到)。
    • 私有的实例属性用两个下划线开头(后面会讲到)。

当然,作为一个专业的程序员,给变量(事实上应该是所有的标识符)命名时做到见名知意也是非常重要的。

变量的使用

  1. message = "hello python word!!!";
  2. print(message)

运行这个程序,看看结果如何。你会发现,输出与以前相同:

  1. hello python word!!!

我们添加了一个名为message的变量。每个变量都存储了一个值——与变量相关联的信息。在这里,存储的值为文本hello python word!!!

我们扩展这个程序:修改使其再打印一条消息。再添加下面两行代码

  1. message = "hello python word!!!";
  2. print(message)
  3. message = "hello python word copy!!!";
  4. print(message)

现在如果运行这个程序,将看到两行输出:

  1. hello python word!!!
  2. hello python word copy!!!

在程序中可随时修改变量的值,而Python将始终记录变量的最新值。

使用变量计算两个值得加减乘除

  1. # 使用变量保存数据并进行加减乘除运算
  2. a = 24
  3. b = 12
  4. print(a + b) # 36
  5. print(a - b) # 12
  6. print(a * b) # 288
  7. print(a / b) # 2.0

常用数据类型

  • 整型:Python中可以处理任意大小的整数,当然包括负整数(Python 2.x中有intlong两种类型的整数,但这种区分对Python来说意义不大,因此在Python 3.x中整数只有int这一种了),而且支持二进制(如0b100,换算成十进制是4)、八进制(如0o100,换算成十进制是64)、十进制(100)和十六进制(0x100,换算成十进制是256)的表示法。
  • 浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如123.456)之外还支持科学计数法(如1.23456e2)。
  • 字符串型:字符串是以单引号或双引号括起来的任意文本,比如'hello'"hello",字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法,而且可以书写成多行的形式(用三个单引号或三个双引号开头,三个单引号或三个双引号结尾)。
    • 如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:
  • 布尔型:布尔值只有TrueFalse两种值,要么是True,要么是False,在Python中,可以直接用TrueFalse表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如3 < 5会产生布尔值True,而2 == 1会产生布尔值False)。

在Python中可以使用type函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念是一致的,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解这个概念也不要紧,我们会在后续的章节中专门讲解函数的定义和使用。

  1. # 使用type()检查变量的类型
  2. a = 100
  3. b = 15.005
  4. d = 'hello, Python'
  5. e = True
  6. print(type(a)) # <class 'int'>
  7. print(type(b)) # <class 'float'>
  8. print(type(d)) # <class 'str'>
  9. print(type(e)) # <class 'bool'>

可以使用Python中内置的函数对变量类型进行转换。

  • int():将一个数值或字符串转换成整数,可以指定进制。
  • float():将一个字符串转换成浮点数。
  • str():将指定的对象转换成字符串形式,可以指定编码。
  • chr():将整数转换成该编码对应的字符串(一个字符)。
  • ord():将字符串(一个字符)转换成对应的编码(整数)。

转义符

如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:

  1. 'I\'m \"OK\"!'

表示内容为

  1. I'm "OK"!

转义字符\可以转义很多字符,比如:

  • \n表示换行
  • \t表示制表符
  • 字符\本身也要转义,所以\\表示的字符就是\

可以在Python的交互式命令行用print()打印字符串看看

小练习

输入和输出

输出

print()在括号中加上字符串,就可以向屏幕上输出指定的文字。比如输出'hello, world',用代码实现如下:

  1. print('hello, world')

print()函数也可以接受多个字符串,用逗号 "," 隔开,就可以连成一串输出:

  1. print('I\'m', 'so', 'handsome')
  2. I'm so handsome

print()会依次打印每个字符串,遇到逗号 "," 输出一个空格,因此,输出的字符串是这样拼起来的:

输入

现在,你已经可以用print()输出你想要的结果了。但是,如果要让用户从电脑输入一些字符怎么办?Python提供了一个input(),可以让用户输入字符串,并存放到一个变量里。比如输入用户的名字:

  1. name = input()
  2. Michael

当你输入name = input()并按下回车后,Python交互式命令行就在等待你的输入了。这时,你可以输入任意字符,然后按回车后完成输入。

输入完成后,不会有任何提示,Python交互式命令行又回到>>>状态了。那我们刚才输入的内容到哪去了?答案是存放到name变量里了。可以直接输入name查看变量内容:

  1. name
  2. 'michael'

运算符

运算符 描述
[]
[:]
下标,切片
** 指数
~
+
-
按位取反, 正负号
*
/
%
//
乘,除,模,整除
+
-
加,减
>>
<<
右移,左移
& 按位与
^
`
` 按位异或,按位或
<=
<
>
>=
小于等于,小于,大于,大于等于
==
!=
等于,不等于
is
is not
身份运算符
in
not in
成员运算符
not
or
and
逻辑运算符
=
+=
-=
*=
/=
%=
//=
**=
&=
`
=<br/>^=<br/>>>=<br/><<=`

Tips:在实际开发中,如果搞不清楚运算符的优先级,可以使用括号来确保运算的执行顺序。

赋值运算符

赋值运算符应该是最为常见的运算符,它的作用是将右边的值赋给左边的变量。下面的例子演示了赋值运算符和复合赋值运算符的使用。

  1. """
  2. 赋值运算符和复合赋值运算符
  3. """
  4. a = 9
  5. b = 3
  6. a += b # 相当于:a = a + b
  7. a *= a + 2 # 相当于:a = a * (a + 2)
  8. print(a) # 168

分支结构

我们写的Python代码都是一条一条语句顺序执行,这种代码结构通常称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们成绩大于等于80算优秀并奖励一颗棒棒糖。相反小于80则罚抄作业,这里就会产生两个分支,而且这两个分支只有一个会被执行。类似的场景还有很多,我们将这种结构称之为“分支结构”或“选择结构”。

Python - if…elif…else

在Python中,要构造分支结构可以使用ifelifelse关键字。所谓关键字就是有特殊含义的单词,像ifelse就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名(事实上,用作其他的标识符也是不可以)。下面的例子中演示了如何构造一个分支结构。

  1. """
  2. 用户身份验证
  3. """
  4. username = input('请输入用户名: ')
  5. password = input('请输入口令: ')
  6. # 用户名是admin且密码是123456则身份验证成功否则身份验证失败
  7. if username == 'admin' and password == '111111':
  8. print('身份验证成功!')
  9. else:
  10. print('身份验证失败!')
  1. 请输入用户名: admin
  2. 请输入口令: 111111
  3. 身份验证成功!
  4. 请输入用户名: admin
  5. 请输入口令: 222222
  6. 身份验证失败!

注意:Python和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来表示代码的层次结构,如果if条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了。

换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。缩进可以使用任意数量的空格,但通常使用4个空格,建议大家不要使用制表键或者设置你的代码编辑工具自动将制表键变成4个空格

当然如果要构造出更多的分支,可以使用if...elif...else...结构或者嵌套的if...else...结构,下面的代码演示了如何利用多分支结构实现分段函数求值。

  1. score = int(input('请输入分数:: '))
  2. if score > 60 and score < 80:
  3. print("恭喜您及格了")
  4. elif score >= 80:
  5. print("恭喜您获得一朵小红花")
  6. else:
  7. print("抱歉要罚你抄课文了")

小练习:输入三条边长,如果能构成三角形就计算周长和面积。

  1. """
  2. 判断输入的边长能否构成三角形,如果能则计算出三角形的周长和面积
  3. """
  4. a = float(input('a = '))
  5. b = float(input('b = '))
  6. c = float(input('c = '))
  7. if a + b > c and a + c > b and b + c > a:
  8. print('周长: %f' % (a + b + c))
  9. p = (a + b + c) / 2
  10. area = (p * (p - a) * (p - b) * (p - c)) ** 0.5
  11. print('面积: %f' % (area))
  12. else:
  13. print('不能构成三角形')

循环结构

循环结构就是程序中控制某条或某些指令重复执行的结构。在Python中构造循环结构有两种做法,一种是for-in循环,一种是while循环。

我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中,让机器人向球门方向移动就是一个需要重复的动作,当然这里还会用到上一课讲的分支结构来判断机器人是否持球以及是否进入射门范围。

再举一个简单的例子,如果要实现每隔1秒中在屏幕上打印一次“hello, world”并持续打印一个小时,我们肯定不能够直接把print('hello, world')这句代码写3600遍,这里同样需要循环结构。

for-in循环

如果明确循环执行的次数或要对一个容器进行迭代(后面会讲到),那么我们推荐使用for-in循环,例如下面代码中计算1~100求和的结果。

  1. """
  2. 用for循环实现1~100求和
  3. """
  4. sum = 0
  5. for x in range(101):
  6. sum += x
  7. print(sum)

需要说明的是上面代码中的range(1, 101)可以用来构造一个从1到100的范围,当我们把这样一个范围放到for-in循环中,就可以通过前面的循环变量x依次取出从1到100的整数。当然,range的用法非常灵活,下面给出了一个例子:

  • range(101):可以用来产生0到100范围的整数,需要注意的是取不到101。
  • range(1, 101):可以用来产生1到100范围的整数,相当于前面是闭区间后面是开区间。
  • range(1, 101, 2):可以用来产生1到100的奇数,其中2是步长,即每次数值递增的值。
  • range(100, 0, -2):可以用来产生100到1的偶数,其中-2是步长,即每次数字递减的值。

知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。

  1. """
  2. 用for循环实现1~100之间的偶数求和
  3. """
  4. sum = 0
  5. for x in range(1,101):
  6. if x % 2 == 0:
  7. sum += x
  8. print(sum)
  9. # 相较于上面直接跳过奇数的做法,下面这种做法很明显并不是很好的选择。
  10. sum = 0
  11. for x in range(0,101,2):
  12. sum += x
  13. print(sum)

while循环

如果要构造不知道具体循环次数的循环结构,我们推荐使用while循环。while循环通过一个能够产生或转换出bool值的表达式来控制循环,表达式的值为True则继续循环;表达式的值为False则结束循环。

下面我们通过一个“猜数字”的小游戏来看看如何使用while循环。猜数字游戏的规则是:计算机出一个1到100之间的随机数,玩家输入自己猜的数字,计算机给出对应的提示信息(大一点、小一点或猜对了),如果玩家猜中了数字,计算机提示用户一共猜了多少次,游戏结束,否则游戏继续。

  1. """
  2. 猜数字游戏
  3. """
  4. import random
  5. answer = random.randint(1, 100)
  6. counter = 0
  7. while True:
  8. counter += 1
  9. number = int(input('请输入: '))
  10. if number < answer:
  11. print('大一点')
  12. elif number > answer:
  13. print('小一点')
  14. else:
  15. print('恭喜你猜对了!')
  16. break
  17. print('你总共猜了%d次' % counter)
  18. if counter > 7:
  19. print('你的智商余额明显不足')

break

上面的代码中使用了break关键字来提前终止循环,需要注意的是break只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了break之外。

continue

还有另一个关键字是continue,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。

总结

和分支结构一样,循环结构也是可以嵌套的,也就是说在循环中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个九九乘法表。

  1. """
  2. 输出乘法口诀表(九九表)
  3. """
  4. for i in range(1, 10):
  5. for j in range(1, i + 1):
  6. print('%d*%d=%d' % (i, j, i * j), end='\t')
  7. print()

练习

打印如下所示的三角形图案

  1. *
  2. **
  3. ***
  4. ****
  5. *****
  1. *
  2. **
  3. ***
  4. ****
  5. *****
  1. *
  2. ***
  3. *****
  4. *******
  5. *********

参考答案:

  1. """
  2. 打印三角形图案
  3. """
  4. row = int(input('请输入行数: '))
  5. for i in range(row):
  6. for _ in range(i + 1):
  7. print('*', end='')
  8. print()
  9. for i in range(row):
  10. for j in range(row):
  11. if j < row - i - 1:
  12. print(' ', end='')
  13. else:
  14. print('*', end='')
  15. print()
  16. for i in range(row):
  17. for _ in range(row - i - 1):
  18. print(' ', end='')
  19. for _ in range(2 * i + 1):
  20. print('*', end='')
  21. print()

更多联系请参考:https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/05.构造程序逻辑.md

函数

函数也可称为方法是带名字的代码块,用于完成具体某一块的工作。

函数的作用

要执行函数定义的特定任务,可调用该函数。需要在程序中多次执行同一项任务时,你无需反复编写完成该任务的代码,而只需调用执行该任务的函数,让Python运行其中的代码。你将发现,通过使用函数,程序的编写、阅读、测试和修复都将更容易。

编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。说到这里我们应该也可以理解了函数的作用。主要为了把重复的特定功能抽离出来封装为一个函数,我们只需要用到的时候,调用这个 函数 就可以了。

函数使用定义

Python中可以使用 def 关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,而函数执行完成后我们可以通过return关键字来返回一个值。

  1. def print_name(name):
  2. print("你好:",name)
  3. print_name("杨宝宝") #你好: 杨宝宝

第一行处的代码行使用关键字def来告诉Python你要定义一个函数。这是函数定义,向Python指出了函数名,还可能在括号内指出函数为完成其任务需要什么样的信息。在这里,函数名为 print_name(),它不需要任何信息就能完成其工作,因此括号是空的(即便如此,括号也必不可少)。最后,定义以冒号结尾。

紧跟在def print_name():后面的所有缩进行构成了函数体。代码行 print("你好:",name)函数体内的唯一一行代码,print()只做一项工作:输入姓名,向你问好。

要使用这个函数,那就要调用它。要调用函数,可依次指定函数名以及用括号括起的必要信息,如第三行,传入我们需要问候的人名。就可想您问好。

实参和形参

前面定义函数 print_name() 时,要求给变量 name 指定一个值。调用这个函数并提供人名时,它将打印相应的问候语。在函数print_name() 的定义中,变量 name 是一个 形参 ——函数完成其工作所需的一项信息。

在代码 print_name("杨宝宝") 中值 “杨宝宝” 是一个实参。实参是调用函数时传递给函数的信息。我们调用函数时,将要让函数使用的信息放在括号内。在print_name("杨宝宝") 中,将实参 “杨宝宝” 传递给了函数 print_name() ,这个值被存储在形参 name中。

函数的参数

函数是绝大多数编程语言中都支持的一个代码的”构建块”,但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持函数的重载,因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。

  1. from random import randint
  2. def roll_dice(n=2):
  3. """摇色子"""
  4. total = 0
  5. for _ in range(n):
  6. total += randint(1, 6)
  7. return total
  8. def add(a=0, b=0, c=0):
  9. """三个数相加"""
  10. return a + b + c
  11. # 如果没有指定参数那么使用默认值摇两颗色子
  12. print(roll_dice())
  13. # 摇三颗色子
  14. print(roll_dice(3))
  15. print(add())
  16. print(add(1))
  17. print(add(1, 2))
  18. print(add(1, 2, 3))
  19. # 传递参数时可以不按照设定的顺序进行传递
  20. print(add(c=50, a=100, b=200))

我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用add函数,这跟其他很多语言中函数重载的效果是一致的。

其实上面的add函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。

  1. """
  2. 可变参数
  3. """
  4. # 在参数名前面的*表示args是一个可变参数
  5. def add(*args):
  6. total = 0
  7. for val in args:
  8. total += val
  9. return total
  10. # 在调用add函数时可以传入0个或多个参数
  11. print(add())
  12. print(add(1))
  13. print(add(1, 2))
  14. print(add(1, 2, 3))
  15. print(add(1, 3, 5, 7, 9))

模块管理函数

任何一们编程语言给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。

最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。

  1. def foo():
  2. print('hello, world!')
  3. def foo():
  4. print('goodbye, world!')
  5. # 下面的代码会输出 goodbye, world
  6. foo()

当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为foo的函数,那么怎么解决这种命名冲突呢?

Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过import关键字导入指定的模块就可以区分到底要使用的是哪个模块中的foo函数,代码如下所示。

  1. # module1.py
  2. def foo():
  3. print('hello, 小张!')
  4. # module2.py
  5. def foo():
  6. print('hello, 小杨!')
  7. # test.py
  8. from module1 import foo
  9. # 输出hello, world!
  10. foo()
  11. from module2 import foo
  12. # 输出goodbye, world!
  13. foo()

也可以按照如下所示的方式来区分到底要使用哪一个foo函数。

  1. # test.py
  2. import module1 as m1
  3. import module2 as m2
  4. m1.foo()
  5. m2.foo()

但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个foo,因为后导入的foo覆盖了之前导入的foo

  1. # test.py
  2. from module1 import foo
  3. from module2 import foo
  4. # 输出goodbye, world!
  5. foo()
  6. test.py
  7. from module2 import foo
  8. from module1 import foo
  9. # 输出hello, world!
  10. foo()

函数的返回值return

函数并非总是直接显示输出,相反,它可以处理一些数据或返回一个或一组值。函数返回的值被称为返回值。在函数中,可使用return语句将值返回到调用函数的代码行。返回值让你能够将程序的大部分繁重工作移到函数中去完成,从而简化主程序。

  1. def love(cname,yname):
  2. message = cname+' love '+yname
  3. return message
  4. print(love("陈宝宝","杨宝宝"))

小练习

实现判断一个数是不是素数的函数。

  1. def is_prime(num):
  2. # 判断一个数是不是素数
  3. for factor in range(2, int(num ** 0.5) + 1):
  4. if num % factor == 0:
  5. return False
  6. return True if num != 1 else False

变量的作用域

字符串

所谓字符串,就是由零个或多个字符组成的有限序列。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。

  1. s1 = 'hello, world!'
  2. s2 = "hello, world!"
  3. # 以三个双引号或单引号开头的字符串可以折行
  4. s3 = """
  5. hello,
  6. world!
  7. """
  8. print(s1, s2, s3, end='')

可以在字符串中使用\(反斜杠)来表示转义,也就是说\后面的字符不再是它原来的意义,例如:\n不是代表反斜杠和字符n,而是表示换行;而\t也不是代表反斜杠和字符t,而是表示制表符。所以如果想在字符串中表示'要写成\',同理想表示\要写成\\。可以运行下面的代码看看会输出什么。

  1. s1 = '\'hello, world!\''
  2. s2 = '\n\\hello, world!\\\n'
  3. print(s1, s2, end='')

\后面还可以跟一个八进制或者十六进制数来表示字符,例如\141\x61都代表小写字母a,前者是八进制的表示法,后者是十六进制的表示法。也可以在\后面跟Unicode字符编码来表示字符,例如\u9a86\u660a代表的是中文“骆昊”。运行下面的代码,看看输出了什么。

  1. s1 = '\141\142\143\x61\x62\x63'
  2. s2 = '\u9a86\u660a'
  3. print(s1, s2)

如果不希望字符串中的\表示转义,我们可以通过在字符串的最前面加上字母r来加以说明,再看看下面的代码又会输出什么。

  1. s1 = r'\'hello, world!\''
  2. s2 = r'\n\\hello, world!\\\n'
  3. print(s1, s2, end='')

Python为字符串类型提供了非常丰富的运算符,我们可以使用+运算符来实现字符串的拼接,可以使用*运算符来重复一个字符串的内容,可以使用innot in来判断一个字符串是否包含另外一个字符串(成员运算),我们也可以用[][:]运算符从字符串取出某个字符或某些字符(切片运算),代码如下所示。

  1. s1 = 'hello ' * 3
  2. print(s1) # hello hello hello
  3. s2 = 'world'
  4. s1 += s2
  5. print(s1) # hello hello hello world
  6. print('ll' in s1) # True
  7. print('good' in s1) # False
  8. str2 = 'abc123456'
  9. # 从字符串中取出指定位置的字符(下标运算)
  10. print(str2[2]) # c
  11. # 字符串切片(从指定的开始索引到指定的结束索引)
  12. print(str2[2:5]) # c12
  13. print(str2[2:]) # c123456
  14. print(str2[2::2]) # c246
  15. print(str2[::2]) # ac246
  16. print(str2[::-1]) # 654321cba
  17. print(str2[-3:-1]) # 45

字符串常用方法

  1. str1 = 'hello, world!'
  2. # 通过内置函数len计算字符串的长度
  3. print(len(str1)) # 13
  4. # 获得字符串首字母大写的拷贝
  5. print(str1.capitalize()) # Hello, world!
  6. # 获得字符串每个单词首字母大写的拷贝
  7. print(str1.title()) # Hello, World!
  8. # 获得字符串变大写后的拷贝
  9. print(str1.upper()) # HELLO, WORLD!
  10. # 从字符串中查找子串所在位置
  11. print(str1.find('or')) # 8
  12. print(str1.find('shit')) # -1
  13. # 与find类似但找不到子串时会引发异常
  14. # print(str1.index('or'))
  15. # print(str1.index('shit'))
  16. # 检查字符串是否以指定的字符串开头
  17. print(str1.startswith('He')) # False
  18. print(str1.startswith('hel')) # True
  19. # 检查字符串是否以指定的字符串结尾
  20. print(str1.endswith('!')) # True
  21. # 将字符串以指定的宽度居中并在两侧填充指定的字符
  22. print(str1.center(50, '*'))
  23. # 将字符串以指定的宽度靠右放置左侧填充指定的字符
  24. print(str1.rjust(50, ' '))
  25. str2 = 'abc123456'
  26. # 检查字符串是否由数字构成
  27. print(str2.isdigit()) # False
  28. # 检查字符串是否以字母构成
  29. print(str2.isalpha()) # False
  30. # 检查字符串是否以数字和字母构成
  31. print(str2.isalnum()) # True
  32. str3 = ' jackfrued@126.com '
  33. print(str3)
  34. # 获得字符串修剪左右两侧空格之后的拷贝
  35. print(str3.strip())

我们之前讲过,可以用下面的方式来格式化输出字符串。

  1. a, b = 5, 10
  2. print('%d * %d = %d' % (a, b, a * b))

当然,我们也可以用字符串提供的方法来完成字符串的格式,代码如下所示。

  1. a, b = 5, 10
  2. print('{0} * {1} = {2}'.format(a, b, a * b))

Python 3.6以后,格式化字符串还有更为简洁的书写方式,就是在字符串前加上字母f,我们可以使用下面的语法糖来简化上面的代码。

  1. a, b = 5, 10
  2. print(f'{a} * {b} = {a * b}')

列表 List

Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素。

List列表的基本使用

  1. list1 = [1, 3, 5, 7, 100]
  2. print(list1) # [1, 3, 5, 7, 100]
  3. # 乘号表示列表元素的重复
  4. list2 = ['hello'] * 3
  5. print(list2) # ['hello', 'hello', 'hello']
  6. # 计算列表长度(元素个数)
  7. print(len(list1)) # 5
  8. # 下标(索引)运算
  9. print(list1[0]) # 1
  10. print(list1[4]) # 100
  11. # print(list1[5]) # IndexError: list index out of range
  12. print(list1[-1]) # 100
  13. print(list1[-3]) # 5
  14. list1[2] = 300
  15. print(list1) # [1, 3, 300, 7, 100]
  16. # 通过循环用下标遍历列表元素
  17. for index in range(len(list1)):
  18. print(list1[index])
  19. # 通过for循环遍历列表元素
  20. for elem in list1:
  21. print(elem)
  22. # 通过enumerate函数处理列表之后再遍历可以同时获得元素索引和值
  23. for index, elem in enumerate(list1):
  24. print(index, elem)

List列表元素的增删

  1. list1 = [1, 3, 5, 7, 100]
  2. # 添加元素
  3. list1.append(200)
  4. list1.insert(1, 400)
  5. # 合并两个列表
  6. # list1.extend([1000, 2000])
  7. list1 += [1000, 2000]
  8. print(list1) # [1, 400, 3, 5, 7, 100, 200, 1000, 2000]
  9. print(len(list1)) # 9
  10. # 先通过成员运算判断元素是否在列表中,如果存在就删除该元素
  11. if 3 in list1:
  12. list1.remove(3)
  13. if 1234 in list1:
  14. list1.remove(1234)
  15. print(list1) # [1, 400, 5, 7, 100, 200, 1000, 2000]
  16. # 从指定的位置删除元素
  17. list1.pop(0)
  18. list1.pop(len(list1) - 1)
  19. print(list1) # [400, 5, 7, 100, 200, 1000]
  20. # 清空列表元素
  21. list1.clear()
  22. print(list1) # []

List列表切片

和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。

  1. fruits = ['grape', 'apple', 'strawberry', 'waxberry']
  2. fruits += ['pitaya', 'pear', 'mango']
  3. # 列表切片
  4. fruits2 = fruits[1:4]
  5. print(fruits2) # apple strawberry waxberry
  6. # 可以通过完整切片操作来复制列表
  7. fruits3 = fruits[:]
  8. print(fruits3) # ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya', 'pear', 'mango']
  9. fruits4 = fruits[-3:-1]
  10. print(fruits4) # ['pitaya', 'pear']
  11. # 可以通过反向切片操作来获得倒转后的列表的拷贝
  12. fruits5 = fruits[::-1]
  13. print(fruits5) # ['mango', 'pear', 'pitaya', 'waxberry', 'strawberry', 'apple', 'grape']

List列表排序

  1. list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
  2. list2 = sorted(list1)
  3. # sorted函数返回列表排序后的拷贝不会修改传入的列表
  4. # 函数的设计就应该像sorted函数一样尽可能不产生副作用
  5. list3 = sorted(list1, reverse=True)
  6. # 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
  7. list4 = sorted(list1, key=len)
  8. print(list1)
  9. print(list2)
  10. print(list3)
  11. print(list4)
  12. # 给列表对象发出排序消息直接在列表对象上进行排序
  13. list1.sort(reverse=True)
  14. print(list1)

高级特性

生成式和生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

  1. a = [x for x in range(10)]
  2. # print(a)输出:
  3. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  4. b = [x + y for x in 'ABCDE' for y in '1234567']
  5. # print(b)输出:
  6. ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7']
  7. # 用列表的生成表达式语法创建列表容器
  8. # 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
  9. c = [x ** 2 for x in range(1, 1000)]
  10. print(sys.getsizeof(c)) # 查看对象占用内存的字节数
  11. print(c)
  12. # 请注意下面的代码创建的不是一个列表而是一个生成器对象
  13. # 通过生成器可以获取到数据但它不占用额外的空间存储数据
  14. # 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
  15. d = (x ** 2 for x in range(1, 1000))
  16. print(sys.getsizeof(d)) # 相比生成式生成器不占用存储数据的空间
  17. print(d)
  18. for val in d:
  19. print(val)

创建a、b、cd的区别仅在于最外层的[]()a、b、c是一个list,而d是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

  1. g = (x * x for x in range(10))
  2. print(g) # 跑错 <generator object <genexpr> at 0x1022ef630>
  3. #正确方式如下
  4. next(g)
  5. 0
  6. next(g)
  7. 1
  8. next(g)
  9. 4
  10. next(g)
  11. 9
  12. next(g)
  13. 16
  14. next(g)
  15. 25
  16. next(g)
  17. 36
  18. next(g)
  19. 49
  20. next(g)
  21. 64
  22. next(g)
  23. 81
  24. next(g)
  25. Traceback (most recent call last):
  26. File "<stdin>", line 1, in <module>
  27. StopIteration

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

  1. g = (x * x for x in range(10))
  2. for n in g:
  3. print(n)

元组

Python中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面演示如何定义和使用元组。

  1. # 定义元组
  2. t = ('陈宝宝', 19, True, '河南郑州市')
  3. print(t)
  4. # 获取元组中的元素
  5. print(t[0])
  6. print(t[3])
  7. # 遍历元组中的值
  8. for member in t:
  9. print(member)
  10. # 重新给元组赋值
  11. # t[0] = '王大锤' # TypeError
  12. # 变量t重新引用了新的元组原来的元组将被垃圾回收
  13. t = ('张宝宝', 20, True, '云南昆明')
  14. print(t)
  15. # 将元组转换成列表
  16. person = list(t)
  17. print(person)
  18. # 列表是可以修改它的元素的
  19. person[0] = '李小龙'
  20. person[1] = 25
  21. print(person)
  22. # 将列表转换成元组
  23. fruits_list = ['apple', 'banana', 'orange']
  24. fruits_tuple = tuple(fruits_list)
  25. print(fruits_tuple)

这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?

  1. 元组中的元素是无法修改的,事实上我们在项目中尤其是多线程环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。

集合

Python基础 - 图2

可以按照下面代码所示的方式来创建和使用集合。

  1. # 创建集合的字面量语法
  2. set1 = {1, 2, 3, 3, 3, 2}
  3. print(set1)
  4. print('Length =', len(set1))
  5. # 创建集合的构造器语法(面向对象部分会进行详细讲解)
  6. set2 = set(range(1, 10))
  7. set3 = set((1, 2, 3, 3, 2, 1))
  8. print(set2, set3)
  9. # 创建集合的推导式语法(推导式也可以用于推导集合)
  10. set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0}
  11. print(set4)

向集合添加元素和从集合删除元素。

  1. set1.add(4)
  2. set1.add(5)
  3. set2.update([11, 12])
  4. set2.discard(5)
  5. if 4 in set2:
  6. set2.remove(4)
  7. print(set1, set2)
  8. print(set3.pop())
  9. print(set3)

集合的成员、交集、并集、差集等运算。

  1. # 集合的交集、并集、差集、对称差运算
  2. print(set1 & set2)
  3. # print(set1.intersection(set2))
  4. print(set1 | set2)
  5. # print(set1.union(set2))
  6. print(set1 - set2)
  7. # print(set1.difference(set2))
  8. print(set1 ^ set2)
  9. # print(set1.symmetric_difference(set2))
  10. # 判断子集和超集
  11. print(set2 <= set1)
  12. # print(set2.issubset(set1))
  13. print(set3 <= set1)
  14. # print(set3.issubset(set1))
  15. print(set1 >= set2)
  16. # print(set1.issuperset(set2))
  17. print(set1 >= set3)
  18. # print(set1.issuperset(set3))

Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符,上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如&运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。

字典

字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。

  1. # 创建字典的字面量语法
  2. scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
  3. print(scores)
  4. # 创建字典的构造器语法
  5. items1 = dict(one=1, two=2, three=3, four=4)
  6. # 通过zip函数将两个序列压成字典
  7. items2 = dict(zip(['a', 'b', 'c'], '123'))
  8. # 创建字典的推导式语法
  9. items3 = {num: num ** 2 for num in range(1, 10)}
  10. print(items1, items2, items3)
  11. # 通过键可以获取字典中对应的值
  12. print(scores['骆昊'])
  13. print(scores['狄仁杰'])
  14. # 对字典中所有键值对进行遍历
  15. for key in scores:
  16. print(f'{key}: {scores[key]}')
  17. # 更新字典中的元素
  18. scores['白元芳'] = 65
  19. scores['诸葛王朗'] = 71
  20. scores.update(冷面=67, 方启鹤=85)
  21. print(scores)
  22. if '武则天' in scores:
  23. print(scores['武则天'])
  24. print(scores.get('武则天'))
  25. # get方法也是通过键获取对应的值但是可以设置默认值
  26. print(scores.get('武则天', 60))
  27. # 删除字典中的元素
  28. print(scores.popitem())
  29. print(scores.popitem())
  30. print(scores.pop('骆昊', 100))
  31. # 清空字典
  32. scores.clear()
  33. print(scores)