Python3极简教程(一小时学完)上

开始 Python 之旅

本教程基于 Python for you and me 教程翻译制作,其中参考了 Python tutorial 和 _The Python Standard Library_,并对原教程的内容进行了改进与补充。

相关链接地址如下:

  • _Python tutorial_:Python 入门指南 — Python tutorial 3.6.3 documentation
  • _Python for you and me_:Welcome to Python for you and me — Python for you and me 0.5.beta1 documentation
  • _The Python Standard Library_:The Python Standard Library — Python 3.12.4 documentation

Python for you and me 的法律声明有如下内容:

Permission is granted to copy, distribute, and/or modify this document under the terms of the Open Publication Licence, Version 1.0, or any later version.

知识点
  • 使用交互模式的 Python3 解释器
  • 简单使用 vim 编写 Python3 脚本
  • 执行 Python3 脚本
  • Python3 代码风格建议
  • Python3 中使用注释
  • 认识 Python3 模块
实验环境
  • Python 3.5
  • Xfce 终端
  • Vim
适合人群

本教程属于初级级别课程,不仅适用于那些有其它语言基础的同学,对没有编程经验的同学也非常友好。

解释器

Python 是一个脚本语言,你可以在 Python 解释器中直接写代码或者将代码写到一个文件里,然后执行这个文件(即脚本文件)。

首先,我们从使用解释器开始,打开 Xfce 终端,键入python3 并回车。这个时候解释器便工作于交互模式

1-2-1

1-2-2

作为我们第一个 Python 代码,我们将打印经典的 "Hello World!",输入python3,如下所示:

>>> print("Hello World!")
Hello World!

1-2-3

欣赏完自己的杰作后,按快捷键 Ctrl + D 即可退出解释器,你也可以键入 exit() 然后敲回车来退出解释器。

脚本文件

作为正式的程序员,你可能想要将上述的代码写到一个源文件。那么我们来创建一个 helloworld.py 文件,用你喜欢的任何编辑器来创建这个文件,我使用 Vim ,你可以使用基于 GUI 的工具,如实验楼环境桌面上的 gedit。

我们建议使用 Vim,在这里我们简单的使用它,你可以在实验楼的课程 Vim 编辑器 里深入学习或者通过这张 vi(vim)键盘图帮助你记忆:

1-3-1

(原图来自:www.viemu.com)

首先,创建的文件会放在 /home/shiyanlou 目录下,打开 Xfce 终端,键入 vim helloworld.py 来启动 Vim 并编辑 helloworld.py,启动后不要乱按键。

1-3-2

然后按 i 键进入插入模式,此时你可以看到左下角有 “插入” 两个字,现在你可以键入下面的代码了。

#!/usr/bin/env python3
print("Hello World!")

其中第一行的前两个字符 #! 称为 Shebang ,目的是告诉 shell 使用 Python 解释器执行其下面的代码。

1-3-3

然后按 Esc 键退出插入模式,再键入 :wq 回车,Vim 就会保存文件并退出。

要运行脚本文件 helloworld.py,还要为文件添加可执行权限(否则会出现权限不足的错误):

chmod +x helloworld.py

在这里使用到了 linux 下的 chmod 命令来变更文件或目录的权限,+x代表赋予了其可执行的权限。

点击此处快速掌握常用 Unix/Linux 常用命令查看。

然后执行脚本文件:

./helloworld.py

1-3-4

这里需要注意如果程序中没有 #!/usr/bin/env python3 的话,应该使用 python3 helloworld.py 来执行,否则使用 ./helloworld.py 程序会被当作 bash 脚本来执行,最终报错。

代码风格建议

在 Python 中,空格很重要,我们使用空格将标识符区分开,行开始处的空格我们称之为缩进,如果你的缩进是错误的,Python 解释器会抛出错误。例如下面情形:

>>> a = 12
>>>   a = 12
  File "<stdin>", line 1
    a = 12
    ^
IndentationError: unexpected indent

这是因为第二行开始处出现了多余的空格,所以需要经常检查代码的缩进。

如果你在代码中混用制表符和空格,这种缩进错误会很常见。

所以如果是用空格,就一直用空格缩进,不要使用制表符。

建议遵守以下约定:

  • 使用 4 个空格来缩进
  • 永远不要混用空格和制表符
  • 在函数之间空一行
  • 在类之间空两行
  • 字典,列表,元组以及参数列表中,在 , 后添加一个空格。对于字典,: 后面也添加一个空格
  • 在赋值运算符和比较运算符周围要有空格(参数列表中除外),但是括号里则不加空格:a = f(1, 2) + g(3, 4)

注释

注释是一些文本用来解释此处代码是做什么的,写注释是为了使他人更容易理解代码。

Python 的注释以 # 字符开始的,在 # 字符到行尾之间的所有东西都被程序忽略为注释,也就说对程序没有任何影响。

>>> # 这是一个注释
>>> # 下面这一行是求两数之和
>>> a = 12 + 34
>>> print(a) # 这里也是注释 :)

注释主要面向的人群是开发人员和维护代码库的人员,所以如果你写了一些复杂代码,你应该写足够的注释在里面,以便让其他任何人能够通过你的注释读懂代码。你应该总是在 # 后跟一个空格,然后再写注释。你也可以做一些标准的注释,像下面这样。

# FIXME -- fix these code later
# TODO -- in future you have to do this

模块

模块是包含了我们能复用的代码的文件,包含了不同的函数定义,变量。模块文件通常以 .py 为扩展名。

Python 本身在默认安装时就带有大量的模块。我们之后将会用到其中的一部分。在使用模块前先导入它。

>>> import math    # 导入math模块
>>> print(math.e)
2.71828182846

我们会在模块这个实验里学习更多有关模块的知识。

总结

完成本实验后,应当明白如何使用交互模式的 Python3 解释器,如何简单使用 vim 编写脚本文件并执行这个脚本文件。

要特别注意的是,Python 使用缩进来定义语句块,缩进是 Python 语法的一部分,这与其它大多数语言不同。

代码风格在本课程的实验 PEP8 代码风格指南 有更为详细的讲述。注释的话,能使用英文建议使用英文。模块同样会在之后的实验里深入学习。

变量和数据类型

本节实验内容主要包括 Python 基本的数据类型和变量。

知识点
  • python 关键字
  • 变量的定义与赋值
  • input() 函数
  • 字符串的格式化

关键字和标识符

每一种编程语言都有它们自己的语法规则,就像我们所说的外语。

下列的标识符是 Python3 的关键字,并且不能用于通常的标识符。关键字必须完全按照下面拼写:

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not
class               from                or
continue            global              pass

这些内容可以在 Python3 解释器中得到:

2-1-1关键字

在 Python 中 我们不需要为变量指定数据类型。所以你可以直接写出 abc = 1 ,这样变量 abc 就是整数类型。如果你写出 abc = 1.0 ,那么变量 abc 就是浮点类型。

>>> a = 13
>>> b = 23
>>> a + b
36

通过上面的例子你应该理解了如何在 Python 中定义变量,也就是只需要输入变量名和值就行了。Python 也能操作字符串,它们用单引号或双引号括起来,就像下面这样。

>>> 'ShiYanLou'
'ShiYanLou'
>>> 'ShiYanLou\'s best'
"ShiYanLou's best"
>>> "Hello World!"
'Hello World!'

从键盘读取输入

通常情况下,Python 的代码中是不需要从键盘读取输入的。不过我们还是可以在 Python 中使用函数 input() 来做到这一点,input() 有一个用于打印在屏幕上的可选字符串参数,返回用户输入的字符串。

我们来写一个程序,它将会从键盘读取一个数字并且检查这个数字是否小于 100。这个程序名称是 /home/shiyanlou/testhundred.py。还记得如何使用 Vim 吗?忘了的话可以看看下面的动图,打开 Xfce 终端,使用 Vim 开始编写代码:

2-3-1

在终端中输入:

vim testhundred.py

输入i后,编写以下代码,注意缩进:

#!/usr/bin/env python3
number = int(input("Enter an integer: "))
if number <= 100:
    print("Your number is less than or equal to 100")
else:
    print("Your number is greater than 100")

接着按 ESC 键并输入 :wq 退出 Vim。

该段代码的含义如下:

如果 number 小于等于 100,输出 “Your number is less than or equal to 100”,如果大于 100,输出 “Your number is greater than 100”。

在执行程序前,别忘了为文件添加可执行权限:

chmod +x testhundred.py

程序运行起来就像这样:

./testhundred.py
Enter an integer: 13
Your number is less than or equal to 100
./testhundred.py
Enter an integer: 123
Your number is greater than 100

后续程序中将会用到之后实验将学到的 while 循环语句,这里先简单介绍下。

while 是使用一个表达式作为判断的条件,如果条件不能够达成则停止循环:

w = 20
while w > 1:
    print(w)
    w -= 1

这个循环中判断条件为 w > 1,当条件不满足的时候就停止循环。当 w 的值小于等于 1 的时候,循环退出。这里要注意 w -= 1,等同于 w = w - 1

下一个程序我们写入 /home/shiyanlou/investment.py,来计算投资,使用 Vim 输入以下代码:

#!/usr/bin/env python3
amount = float(input("Enter amount: "))  # 输入数额
inrate = float(input("Enter Interest rate: "))  # 输入利率
period = int(input("Enter period: "))  # 输入期限
value = 0
year = 1
while year <= period:
    value = amount + (inrate * amount)
    print("Year {} Rs. {:.2f}".format(year, value))
    amount = value
    year = year + 1

运行程序:

cd /home/shiyanlou
chmod +x investment.py
./investment.py
Enter amount: 10000
Enter Interest rate: 0.14
Enter period: 5
Year 1 Rs. 11400.00
Year 2 Rs. 12996.00
Year 3 Rs. 14815.44
Year 4 Rs. 16889.60
Year 5 Rs. 19254.15

while year <= period: 的意思是,当 year 的值小于等于 period 的值时,下面的语句将会一直循环执行下去,直到 year 大于 period 时停止循环。

Year {} Rs. {:.2f}".format(year, value) 称为字符串格式化,大括号和其中的字符会被替换成传入 str.format() 的参数,也即 year 和 value。其中 {:.2f} 的意思是替换为 2 位精度的浮点数。

代码示例

一些关于变量和数据类型的代码实例。下面的例子程序请都写在 /home/shiyanlou 目录下。

本部分包括下面的几个实例:

  1. 求 N 个数字的平均值
  2. 华氏温度到摄氏温度转换程序

请按照指定目录完成每个程序,并理解每个程序中每行代码的含义。

求 N 个数字的平均值

下面的程序用来求 N 个数字的平均值。请将程序代码写入到文件 /home/shiyanlou/averagen.py 中,程序中将需要输入 10 个数字,最后计算 10 个 数字的平均值。

代码内容,请理解每一行代码含义:

#!/usr/bin/env python3
N = 10
sum = 0
count = 0
print("please input 10 numbers:")
while count < N:
    number = float(input())
    sum = sum + number
    count = count + 1
average = sum / N
print("N = {}, Sum = {}".format(N, sum))
print("Average = {:.2f}".format(average))

运行程序过程,需要输入 10 个数字:

cd /home/shiyanlou
chmod +x averagen.py
./averagen.py
1.2
3.4
3.5
33.2
2
4
6
2.4
4
5.5
N = 10, Sum = 65.2
Average = 6.52
温度转换

在下面的程序里,我们使用公式 C = (F - 32) / 1.8 将华氏温度转为摄氏温度。

#!/usr/bin/env python3
fahrenheit = 0
print("Fahrenheit Celsius")
while fahrenheit <= 250:
    celsius = (fahrenheit - 32) / 1.8 # 转换为摄氏度
    print("{:5d} {:7.2f}".format(fahrenheit , celsius))
    fahrenheit = fahrenheit + 25

{:5d} 的意思是替换为 5 个字符宽度的整数,宽度不足则使用空格填充。 {:7.2f}的意思是替换为为 7 个字符宽度的保留两位的小数,小数点也算一个宽度,宽度不足则使用空格填充。其中7指宽度为 7,.2f指保留两位小数。

运行程序:

cd /home/shiyanlou
chmod +x temperature.py
./temperature.py
Fahrenheit Celsius
    0  -17.78
   25   -3.89
   50   10.00
   75   23.89
  100   37.78
  125   51.67
  150   65.56
  175   79.44
  200   93.33
  225  107.22
  250  121.11

单行定义多个变量或赋值

你甚至可以在一行内将多个值赋值给多个变量。

在终端中输入:

python3

进入到 python3 交互式界面:

>>> a , b = 45, 54
>>> a
45
>>> b
54

这个技巧用来交换两个数的值非常方便。

>>> a, b = b , a
>>> a
54
>>> b
45

要明白这是怎么工作的,你需要学习元组(_tuple_)这个数据类型。我们是用逗号创建元组。在赋值语句的右边我们创建了一个元组,我们称这为元组封装(_tuple packing_),赋值语句的左边我们则做的是元组拆封 (_tuple unpacking_)。

下面是另一个元组拆封的例子:

>>> data = ("shiyanlou", "China", "Python")
>>> name, country, language = data
>>> name
'shiyanlou'
>>> country
'China'
>>> language
'Python'

总结

完成这个实验我们应该了解到以下的内容:

  1. Python3 关键字有哪些(在这里不要求全部记住)
  2. 变量如何赋值变量
  3. Python3 怎样从键盘读取输入
  4. Python3 字符串的格式化
  5. 元组封装和拆封

在这里可以了解更多有关字符串格式化的信息:string — Common string operations — Python 3.12.4 documentation。

运算符和表达式

在 Python 中你会写大量的表达式。

表达式由运算符和操作数组成,像 2+3 就是一个表达式,其中 2 和 3 是操作数,加号是运算符。

本节实验中我们将学习运算符和表达式的基本用法。

知识点
  • 关系/逻辑运算
  • 表达式
  • 类型转换

运算符

运算符是一些符号,它告诉 Python 解释器去做一些数学或逻辑操作。一些基本的数学操作符如下所示:

>>> 2 + 3
5
>>> 23.0 - 3
20.0
>>> 22 / 12
1.8333333333333333

只要有任意一个操作数是浮点数,结果就会是浮点数。

进行除法运算时若是除不尽,结果将会是小数,这很自然,如果要进行整除,使用 // 运算符,它将返回商的整数部分。

% 是求余运算符:

>>> 14 % 3
2

整数运算示例

整数运算符比较容易理解,代码如下:

#!/usr/bin/env python3
days = int(input("Enter days: "))
months = days // 30
days = days % 30
print("Months = {} Days = {}".format(months, days))

在终端里使用 Vim 输入上述代码后,用chmod命令赋予可执行权限,最后运行程序:

3-2.1-1

Enter days:后获得用户输入的天数,然后获得月份数和天数,最后把这些数打印出来。你可以使用更容易的办法:

#!/usr/bin/env python3
days = int(input("Enter days: "))
print("Months = {} Days = {}".format(*divmod(days, 30)))

divmod(num1, num2) 返回一个元组,这个元组包含两个值,第一个是 num1 和 num2 相整除得到的值,第二个是 num1 和 num2 求余得到的值,然后我们用 * 运算符拆封这个元组,得到这两个值。

关系运算符

你可以使用下面的运算符实现关系运算。

关系运算符

OperatorMeaning
<Is less than
<=Is less than or equal to
>Is greater than
>=Is greater than or equal to
==Is equal to
!=Is not equal to

在终端里输入python3进入交互页面,试一试以下一些例子:

>>> 1 < 2
True
>>> 3 > 34
False
>>> 23 == 45
False
>>> 34 != 323
True

逻辑运算符

对于逻辑 与,或,非,我们使用 andornot 这几个关键字。

逻辑运算符 and 和 or 也称作短路运算符:它们的参数从左向右解析,一旦结果可以确定就停止。例如,如果 A 和 C 为真而 B 为假,A and B and C 不会解析 C 。作用于一个普通的非逻辑值时,短路运算符的返回值通常是能够最先确定结果的那个操作数。

关系运算可以通过逻辑运算符 and 和 or 组合,比较的结果可以用 not 来取反意。逻辑运算符的优先级又低于关系运算符,在它们之中,not 具有最高的优先级,or 优先级最低,所以 A and not B or C 等于 (A and (notB)) or C。当然,括号也可以用于比较表达式。

下面是一些例子:

>>> 5 and 4   # 首先判断5,肯定为true,那么最终的结果就取决于 and 后面那个的布尔值,4 的布尔值为 true,这样就可以确定整个表达式的值为 true 了,所以返回 4
4
>>> 0 and 4   # 首先判断0,因为 0 的布尔值为 false,那么不管 and 后面那个的布尔值是什么,整个表达式的布尔值都应该为 false 了,这个时候就不需要判断 4 了,直接返回最先确定结果的那个数也就是0
0
>>> False or 3 or 0
3
>>> 2 > 1 and not 3 > 5 or 4
True

简写运算符

x op= expression 为简写运算的语法形式。其等价于 x = x op expression ,举例如下:

>>> a = 12
>>> a += 13
>>> a
25
>>> a /= 3
>>> a
8.333333333333334
>>> a += (26 * 32)
>>> a
840.3333333333334

/home/shiyanlou/shorthand.py 示例,输入quit()退出 python 交互页面,用 Vim 写入以下代码:

#!/usr/bin/env python3
N = 100
a = 2
while a < N:
    print(str(a))
    a *= a

运行程序:

cd /home/shiyanlou
chmod +x shorthand.py
./shorthand.py
2
4
16

表达式

通常我们书写表达式的时候,会在每一个运算符左右都放一个空格,这样使代码更可读,如:

a = 234 * (45 - 56 / 34)

一个用于展示表达式的例子,注意其中运算符的优先级。

#!/usr/bin/env python3
a = 9
b = 12
c = 3
x = a - b / 3 + c * 2 - 1
y = a - b / (3 + c) * (2 - 1)
z = a - (b / (3 + c) * 2) - 1
print("X = ", x)
print("Y = ", y)
print("Z = ", z)

运行程序:

cd /home/shiyanlou
chmod +x evaluationexp.py
./evaluationexp.py
X = 10.0
Y = 7.0
Z = 4.0

第一个计算的是 _x_,步骤如下:

9 - 12 / 3 + 3 * 2 -1
9 - 4 + 3 * 2 - 1
9 - 4 + 6 - 1
5 + 6 - 1
11 - 1
10

由于括号的存在,y 和 z 的计算方式不同,你可以自己去验证它们。

类型转换

我们可以手动的执行类型转换。

类型转换函数转换路径
float(string)字符串 -> 浮点值
int(string)字符串 -> 整数值
str(integer)整数值 -> 字符串
str(float)浮点值 -> 字符串
>>> a = 8.126768
>>> str(a)
'8.126768'

可以分别尝试下前面的四个类型转换函数。

程序示例

evaluateequ.py

这个程序计算数列 1/x+1/(x+1)+1/(x+2)+ ... +1/n,我们设 x = 1,n = 10。

使用 Vim 写入以下代码:

#!/usr/bin/env python3
sum = 0
for i in range(1, 11):
    sum += 1.0 / i
    print("{:2d} {:6.4f}".format(i , sum))

编写完成后运行 chmod +x evaluateequ.py 给文件增加运行权限。

运行程序:

3-2.7-1

quadraticequation.py

这个程序的名称为 quadratic equation 组合,是二次方程的英文词组。

这个程序用来求解二次方程式:

#!/usr/bin/env python3
import math
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
d = b * b - 4 * a * c
if d < 0:
    print("ROOTS are imaginary")
else:
    root1 = (-b + math.sqrt(d)) / (2 * a)
    root2 = (-b - math.sqrt(d)) / (2 * a)
    print("Root 1 = ", root1)
    print("Root 2 = ", root2)

编写完成后运行 chmod +x quadraticequation.py 给文件增加运行权限。

运行程序:

3-2.7-2

salesmansalary.py

这个程序计算一位数码相机销售人员的工资。他的基本工资是 1500,每售出一台相机他可以得到 200 并且获得 2% 的抽成。程序要求输入相机数量及单价。

#!/usr/bin/env python3
basic_salary = 1500
bonus_rate = 200
commission_rate = 0.02
numberofcamera = int(input("Enter the number of inputs sold: "))
price = float(input("Enter the price of camera: "))
bonus = (bonus_rate * numberofcamera)
commission = (commission_rate * price * numberofcamera)
print("Bonus        = {:6.2f}".format(bonus))
print("Commission   = {:6.2f}".format(commission))
print("Gross salary = {:6.2f}".format(basic_salary + bonus + commission))

编写完成后运行 chmod +x salesmansalary.py 给文件增加运行权限,然后运行程序。

运行效果如下:

图片描述

总结

本节实验知识点回顾:

  • 关系/逻辑运算
  • 表达式
  • 类型转换

除了数值运算,关系和逻辑运算也是程序的重要组成部分。另外 Python 是强类型语言,所以必要的时候需要手动进行类型转换。

挑战:圆的面积

介绍

我们通过实验已经学习了基本的 Python 3 语法。现在我们就来用一个挑战来巩固一下我们的实验效果。

本挑战中,我们将实现一个程序用来计算半径为 2 的圆的面积并打印输出。

目标

在 /home/shiyanlou/Code 创建一个 Python 脚本 CircleArea.py

cd /home/shiyanlou/Code
touch CircleArea.py

CircleArea.py 能够计算出一个半径为 2 的圆的面积,并且把面积打印出来,保留小数点后 10 位。

不要使用 input 等方法获得输入,程序不需要输入任何参数,程序执行如下,直接输出半径为 2 的圆的面积:

python3 /home/shiyanlou/Code/CircleArea.py

提示语

  • import math
  • 使用 format 可以控制输出的小数位数,可以使用 '{:.10f}'.format() 来进行格式化输出保留 10 位小数
  • 注意代码文件的路径必须是 /home/shiyanlou/Code/CircleArea.py
  • 不要使用 input 获取输入,程序执行不需要任何输入的参数

知识点

  • 导入模块
  • 打印输出
  • 运算符和表达式

参考代码

注意:请务必先独立思考获得 PASS 之后再查看参考代码,直接拷贝代码收获不大。

/home/shiyanlou/Code/CircleArea.py 参考代码:

参考答案:

import math
# 计算圆的面积
area = 2 * 2 * math.pi
# 格式化输出圆的面积,保留10位小数
print("{:.10f}".format(area))

控制流 If-else

我们处理现实生活中的问题时会做出决定,就像决定买哪种相机或者怎样更好的打篮球。

同样我们写计算机程序的时候也要做相同的事情。

我们通过 if-else 语句来做决定,我们使用它来改变程序运行的流程。

知识点
  • if 语句
  • else 语句
  • 真值检测

If 语句

语法如下:

if expression:
    do this

如果表达式 expression 的值为真(不为零的任何值都为真),程序将执行缩进后的内容。务必要使用正确的缩进,在表达式为真的情况将会执行缩进的所有行。

一个简单的例子,使用vim写入文件 /home/shiyanlou/number100.py,程序接受用户输入的一个数并且检查这个数是否小于 100。

#!/usr/bin/env python3
number = int(input("Enter a number: "))
if number < 100:
    print("The number is less than 100")

然后我们运行它:

cd /home/shiyanlou
chmod +x number100.py
./number100.py

操作截图:

5-2-1

Else 语句

在上面的例子中,我们想要这样做:如果输入数大于 100 则打印 "Greater than"。我们使用 else 语句来做到这一点,它将在 if 语句未满足的情况时工作。

#!/usr/bin/env python3
number = int(input("Enter a number: "))
if number < 100:
    print("The number is less than 100")
else:
    print("The number is greater than 100")

运行它:

此处输入图片的描述

另一个非常基础的例子:

>>> x = int(input("Please enter an integer: "))
>>> if x < 0:
...      x = 0
...      print('Negative changed to zero')
... elif x == 0:
...      print('Zero')
... elif x == 1:
...      print('Single')
... else:
...      print('More')

在上面的例子中,elif 是 else if 的缩写。

真值检测

检测真值的优雅方式是这样的:

if x:
    pass

不要像下面这样做:

if x == True:
    pass

总结

这个实验非常简单,这里只是提一句,Python 中的很多值是具有布尔意义的,所以有时候我们可以写的更优雅一点。

同时也要注意下 if elif else 的基本语法结构。

循环

在以前的例子里,有些时候我们需要多次执行相同的任务,我们使用一个计数器来检查代码需要执行的次数。这个技术被称为循环。

实验知识点
  • while 循环
  • print() 函数的 end 参数
  • 列表
    • 索引
    • 切片
  • for 循环
  • range() 函数
  • continue 关键字
  • for 循环中的 else 关键字

while 循环

while 语句的语法如下:

while condition:
    statement1
    statement2

        想要多次执行的代码必须以正确的缩进放在 while 语句下面。在表达式 condition 为真的时候它们才会执行。同 if-else 一样,非零值为真。启动 python3 交互页面,让我们写一个简单的代码,它按顺序打印 0 到 10 的数字:

>>> n = 0
>>> while n < 11:
...     print(n)
...     n += 1
...
0
1
2
3
4
5
6
7
8
9
10

        在第一行我们使 n = 0,然后在 while 语句中把条件设置为 n < 11,这意味着在 while 语句下面缩进的所有行将会被执行,直到 n 的值大于等于 11。在循环里我们只是打印 n 的值然后令它增一。

        想想如果没有循环语句,你想要打印 0 到 10 的所有数字,那你得手动打印 11 次!

斐波那契(Fibonacci)数列

我们来试试打印斐波那契数列。这个数列前两项为 1,之后的每一个项都是前两项之和。所以这个数列看起来就像这样: 1,1,2,3,5,8,13,...

#!/usr/bin/env python3
a, b = 0, 1
while b < 100:
    print(b)
    a, b = b, a + b

运行程序:

此处输入图片的描述

第一行代码中我们初始化 a 和 b。当 b 的值小于 100 的时候,循环执行代码。循环里我们首先打印 b 的值,然后在下一行将 a + b 的值赋值给 bb 的值赋值给 a

学习其他语言的同学在这里可能有些困惑,你可以这样理解,Python 中赋值语句执行时会先对赋值运算符右边的表达式求值,然后将这个值赋值给左边的变量。

默认情况下,print() 除了打印你提供的字符串之外,还会打印一个换行符,所以每调用一次 print() 就会换一次行,如同上面一样。

你可以通过 print() 的另一个参数 end 来替换这个换行符,就像下面这样,下面的程序写入 /home/shiyanlou/fibonacci2.py

#!/usr/bin/env python3
a, b = 0, 1
while b < 100:
    print(b, end=' ')
    a, b = b, a + b
print()

运行程序:

cd /home/shiyanlou
chmod +x fibonacci2.py
./fibonacci2.py

此处输入图片的描述

幂级数

我们来写一个程序计算幂级数:e^x = 1 + x + x^2 / 2! + x^3 / 3! + ... + x^n / n! (0 < x < 1)。

该程序写入代码文件 /home/shiyanlou/powerseries.py

#!/usr/bin/env python3
x = float(input("Enter the value of x: "))
n = term = 1
result = 1.0
while n <= 100:
    term *= x / n
    result += term
    n += 1
    if term < 0.0001:
        break
print("No of Times= {} and Sum= {}".format(n, result))

运行程序:

此处输入图片的描述

在这个程序里我们介绍一个新的关键字 break,它可以终止最里面的循环。这个例子里我们在 if 语句里使用 break

if term < 0.0001:
    break

这意味着如果 term 的值小于 0.0001,那么终止循环。

乘法表

这个例子里我们打印 10 以内的乘法表。写入代码文件 /home/shiyanlou/multiplication.py

#!/usr/bin/env python3
i = 1
print("-" * 50)
while i < 11:
    n = 1
    while n <= 10:
        print("{:4d}".format(i * n), end=' ')
        n += 1
    print()
    i += 1
print("-" * 50)

运行如下:

此处输入图片的描述

这里我们在 while 循环里使用了另一个 while 循环,这被称为嵌套循环。你应该已经看到一条有趣的语句:

print("-" * 50)

字符串若是乘上整数 n,将返回由 n 个此字符串拼接起来的新字符串。

下面是一些例子:

>>> 's' * 10
'ssssssssss'
>>> print("*" * 10)
**********
>>> print("#" * 20)
####################
>>> print("--" * 20)
----------------------------------------
>>> print("-" * 40)
----------------------------------------

一些打印星号的例子

这里是一些你可以在大学的实验报告里经常看到的例子。

设计 1
#!/usr/bin/env python3
row = int(input("Enter the number of rows: "))
n = row
while n >= 0:
    x =  "*" * n
    print(x)
    n -= 1

运行这个程序:

此处输入图片的描述

设计 2
#!/usr/bin/env python3
n = int(input("Enter the number of rows: "))
i = 1
while i <= n:
    print("*" * i)
    i += 1

运行这个程序:

此处输入图片的描述

设计 3
#!/usr/bin/env python3
row = int(input("Enter the number of rows: "))
n = row
while n >= 0:
    x = "*" * n
    y = " " * (row - n)
    print(y + x)
    n -= 1

运行这个程序:

此处输入图片的描述

 列表

在继续学习循环之前,我们先学习一个叫做列表的数据结构。它可以写作中括号之间的一列逗号分隔的值。列表的元素不必是同一类型:

>>> a = [ 1, 342, 223, 'India', 'Fedora']
>>> a
[1, 342, 223, 'India', 'Fedora']

你可以将上面的列表想象为一堆有序的盒子,盒子包含有上面提到的值,每个盒子都有自己的编号(红色的数字),编号从零开始,你可以通过编号访问每一个盒子里面的值。对于列表,这里的编号称为索引。

6-3-1

我们像下面这样通过索引来访问列表中的每一个值:

>>> a[0]
1
>>> a[4]
'Fedora'

如果我们使用负数的索引,那将会从列表的末尾开始计数,像下面这样:

>>> a[-1]
'Fedora'

你甚至可以把它切成不同的部分,这个操作称为切片,例子在下面给出:

>>> a[0:-1]
[1, 342, 223, 'India']
>>> a[2:-2]
[223]

切片并不会改变正在操作的列表,切片操作返回其子列表,这意味着下面的切片操作返回列表一个新的(栈)拷贝副本:

>>> a[:]
[1, 342, 223, 'India', 'Fedora']

切片的索引有非常有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为切片的索引的大小。如果是字符串,则为字符串大小。

>>> a[:-2]
[1, 342, 223]
>>> a[-2:]
['India', 'Fedora']

有个办法可以很容易地记住切片的工作方式:切片时的索引是在两个元素之间 。左边第一个元素的索引为 0,而长度为 n 的列表其最后一个元素的右界索引为 n。例如:

 +---+-----+-----+---------+----------+
 | 1 | 342 | 223 | 'India' | 'Fedora' |
 +---+-----+-----+---------+----------+
   0    1     2       3        4          5
  -5   -4    -3      -2       -1

上面的第一行数字给出列表中的索引点 0...5。第二行给出相应的负索引。切片是从 i 到 j 两个数值表示的边界之间的所有元素。

对于非负索引,如果上下都在边界内,切片长度就是两个索引之差。例如 a[2:4] 是 2。

Python 中有关下标的集合都满足左闭右开原则,切片中也是如此,也就是说集合左边界值能取到,右边界值不能取到。

对上面的列表, a[0:5] 用数学表达式可以写为 [0,5) ,其索引取值为 0,1,2,3,4,所以能将a中所有值获取到。 你也可以用a[:5], 效果是一样的。

a[-5:-1],因为左闭右开原则,其取值为 -5,-4,-3,-2 是不包含 -1 的。

为了取到最后一个值,你可以使用 a[-5:] ,它代表了取该列表最后 5 个值。

试图使用太大的索引会导致错误:

>>> a[32]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> a[-10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Python 能够优雅地处理那些没有意义的切片索引:一个过大的索引值(即大于列表实际长度)将被列表实际长度所代替,当上边界比下边界大时(即切片左值大于右值)就返回空列表:

>>> a[2:32]
[223, 'India', 'Fedora']
>>> a[32:]
[]

切片操作还可以设置步长,就像下面这样:

>>> a[1::2]
[342, 'India']

它的意思是,从切片索引 1 到列表末尾,每隔两个元素取值。

列表也支持连接这样的操作,它返回一个新的列表:

>>> a + [36, 49, 64, 81, 100]
[1, 342, 223, 'India', 'Fedora', 36, 49, 64, 81, 100]

列表允许修改元素:

>>> cubes = [1, 8, 27, 65, 125]
>>> cubes[3] = 64
>>> cubes
[1, 8, 27, 64, 125]

也可以对切片赋值,此操作可以改变列表的尺寸,或清空它:

>>> 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
[]

细心的同学可能发问了,前面不是说过切片操作不改变列表么?严格来说,这里并不算真正的切片操作,只是上面代码中赋值运算符左边的这种操作与切片操作形式一样而已。

要检查某个值是否存在于列表中,你可以这样做:

>>> a = ['ShiYanLou', 'is', 'cool']
>>> 'cool' in a
True
>>> 'Linux' in a
False

这意味着我们可以将上面的语句使用在 if 子句中的表达式。通过内建函数 len() 我们可以获得列表的长度:

>>> len(a)
3

如果你想要检查列表是否为空,请这样做:

if list_name: # 列表不为空
    pass
else: # 列表为空
    pass

列表是允许嵌套的(创建一个包含其它列表的列表),例如:

>>> 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'

for 循环

通过 for 语句我们可以使用 for 循环。Python 里的 for 循环与 C 语言中的不同。这里的 for 循环遍历任何序列(比如列表和字符串)中的每一个元素。下面给出示例:

>>> a = ['ShiYanLou', 'is', 'powerful']
>>> for x in a:
...     print(x)
...
ShiYanLou
is
powerful

我们也能这样做:

>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> for x in a[::2]:
...     print(x)
1
3
5
7
9
range() 函数

如果你需要一个数值序列,内置函数  range()  会很方便,它生成一个等差数列(并不是列表):

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4
>>> range(1, 5)
range(1, 5)
>>> list(range(1, 5))
[1, 2, 3, 4]
>>> list(range(1, 15, 3))
[1, 4, 7, 10, 13]
>>> list(range(4, 15, 2))
[4, 6, 8, 10, 12, 14]

continue 语句

如同 break ,我们可以在循环中使用另一个语句 continue。它会跳过其后的代码回到循环开始处执行。这意味着它可以帮助你跳过部分循环。在下面的例子中,我们要求用户输入一个整数,如果输入的是负数,那么我们会再次要求输入,如果输入的是整数,我们计算这个数的平方。用户输入 0 来跳出这个无限循环。

#!/usr/bin/env python3
while True:
    n = int(input("Please enter an Integer: "))
    if n < 0:
        continue # 这会返回到循环开始处执行
    elif n == 0:
        break
    print("Square is ", n ** 2)
print("Goodbye")

运行程序:

此处输入图片的描述

循环的 else 语句

我们可以在循环后面使用可选的 else 语句。它将会在循环完毕后执行,除非有 break 语句终止了循环。

>>> for i in range(0, 5):
...     print(i)
... else:
...     print("Bye bye")
...
0
1
2
3
4
Bye bye

在本课程的后续内容中,我们会看到更多有关 break 和 continue 的例子。

棍子游戏

这是一个非常简单的游戏。这里有 21 根棍子,首先用户选 1 到 4 根棍子,然后电脑选 1 到 4 根棍子。谁选到最后一根棍子谁就输。判断一下用户有赢的机会吗?如果没有的话,如何修改游戏规则可以使用户有赢的机会呢?

特别说明:用户和电脑一次选的棍子总数只能是 5。

#!/usr/bin/env python3
sticks = 21

print("There are 21 sticks, you can take 1-4 number of sticks at a time.")
print("Whoever will take the last stick will lose")

while True:
    print("Sticks left: " , sticks)
    if sticks == 1:
        print("You took the last stick, you lose")
        break
    sticks_taken = int(input("Take sticks(1-4):"))
    if sticks_taken >= 5 or sticks_taken <= 0:
        print("Wrong choice")
        continue
    print("Computer took: " , (5 - sticks_taken) , "\n")
    sticks -= 5

总结

这个实验中我们了解了两种循环:while 和 for 循环,其中的 for 循环我们通常与 range() 函数配合使用,要特别注意的是,range() 函数返回的并不是列表而是一种可迭代对象:

此处输入图片的描述

python 中 for 循环的 else 子句给我们提供了检测循环是否顺利执行完毕的一种优雅方法。

数据结构

Python 有许多内建的数据结构。如果你困惑于什么是数据结构,那么可以参考一下 Wikipedia。

简单的来说,数据结构(data structure)是计算机中存储、组织数据的方式。比如我们之前的课程中使用过的列表就是一种数据结构,在这里我们还会深入学习它。

知识点
  • 列表的方法与列表元素的删除
  • 将列表用作栈和队列
  • 列表推导式
  • 元组、集合、字典的创建与操作
  • enumerate() 和 zip() 函数

列表

>>> a = [23, 45, 1, -3434, 43624356, 234]
>>> a.append(45)
>>> a
[23, 45, 1, -3434, 43624356, 234, 45]

首先我们建立了一个列表 a。然后调用列表的方法 a.append(45) 添加元素 45 到列表末尾。你可以看到元素 45 已经添加到列表的末端了。有些时候我们需要将数据插入到列表的任何位置,这时我们可以使用列表的 insert() 方法。

>>> a.insert(0, 1) # 在列表索引 0 位置添加元素 1
>>> a
[1, 23, 45, 1, -3434, 43624356, 234, 45]
>>> a.insert(0, 111) # 在列表索引 0 位置添加元素 111
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 234, 45]

列表方法 count(s) 会返回列表元素中 s 的数量。我们来检查一下 45 这个元素在列表中出现了多少次。

>>> a.count(45)
2

如果你想要在列表中移除任意指定值,你需要使用 remove() 方法。

>>> a.remove(234)
>>> a
[111, 1, 23, 45, 1, -3434, 43624356, 45]

现在我们反转整个列表。

>>> a.reverse()
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111]

怎样将一个列表的所有元素添加到另一个列表的末尾呢,可以使用列表的 extend() 方法。

>>> b = [45, 56, 90]
>>> a.extend(b) # 添加 b 的元素而不是 b 本身
>>> a
[45, 43624356, -3434, 1, 45, 23, 1, 111, 45, 56, 90]

给列表排序,我们使用列表的 sort() 方法,排序的前提是列表的元素是可比较的。

>>> a.sort()
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111, 43624356]

你也能使用 del 关键字删除指定位置的列表元素。

>>> del a[-1]
>>> a
[-3434, 1, 1, 23, 45, 45, 45, 56, 90, 111]

将列表用作栈和队列

栈是我们通常所说的一种 LIFO (Last In First Out 后进先出)数据结构。它的意思是最后进入的数据第一个出来。一个最简单的例子往一端封闭的管道放入一些弹珠然后取出来,如果你想把弹珠取出来,你必须从你最后放入弹珠的位置挨个拿出来。用代码实现此原理:

>>> a = [1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
>>> a.pop()
6
>>> a.pop()
5
>>> a.pop()
4
>>> a.pop()
3
>>> a
[1, 2]
>>> a.append(34)
>>> a
[1, 2, 34]

上面的代码中我们使用了一个新方法 pop()。传入一个参数 i 即 pop(i) 会将第 i 个元素弹出。

在我们的日常生活中会经常遇到队列,比如售票窗口、图书馆、超市的结账出口。队列 是一种在末尾追加数据以及在开始弹出数据的数据结构。与栈不同,它是 FIFO (First In First Out 先进先出)的数据结构。

>>> a = [1, 2, 3, 4, 5]
>>> a.append(1)
>>> a
[1, 2, 3, 4, 5, 1]
>>> a.pop(0)
1
>>> a.pop(0)
2
>>> a
[3, 4, 5, 1]

我们使用 a.pop(0) 弹出列表中第一个元素。

列表推导式

列表推导式为从序列中创建列表提供了一个简单的方法。如果没有列表推导式,一般都是这样创建列表的:通过将一些操作应用于序列的每个成员并通过返回的元素创建列表,或者通过满足特定条件的元素创建子序列。

假设我们创建一个 squares 列表,可以像下面这样创建。

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

注意这个 for 循环中的被创建(或被重写)的名为  x  的变量在循环完毕后依然存在。使用如下方法,我们可以计算 squares 的值而不会产生任何的副作用:。

squares = list(map(lambda x: x**2, range(10)))

等价于下面的列表推导式。

squares = [x**2 for x in range(10)]

上面这个方法更加简明且易读。

列表推导式由包含一个表达式的中括号组成,表达式后面跟随一个  for 子句,之后可以有零或多个  for  或  if  子句。结果是一个列表,由表达式依据其后面的 for  和  if  子句上下文计算而来的结果构成。

例如,如下的列表推导式结合两个列表的元素,如果元素之间不相等的话:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

等同于:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

值得注意的是在上面两个方法中的 for  和 if 语句的顺序。

列表推导式也可以嵌套。

>>> a=[1,2,3]
>>> z = [x + 1 for x in [x ** 2 for x in a]]
>>> z
[2, 5, 10]

元组

元组是由数个逗号分割的值组成。

>>> a = 'Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus'
>>> a
('Fedora', 'ShiYanLou', 'Kubuntu', 'Pardus')
>>> a[1]
'ShiYanLou'
>>> for x in a:
...     print(x, end=' ')
...
Fedora ShiYanLou Kubuntu Pardus

你可以对任何一个元组执行拆封操作并赋值给多个变量,就像下面这样:

>>> divmod(15,2)
(7, 1)
>>> x, y = divmod(15,2)
>>> x
7
>>> y
1

元组是不可变类型,这意味着你不能在元组内删除或添加或编辑任何值。如果你尝试这些操作,将会出错:

>>> a = (1, 2, 3, 4)
>>> del a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion

要创建只含有一个元素的元组,在值后面跟一个逗号。

>>> a = (123)
>>> a
123
>>> type(a)
<class 'int'>
>>> a = (123, )
>>> b = 321,
>>> a
(123,)
>>> b
(321,)
>>> type(a)
<class 'tuple'>
>>> type(b)
<class 'tuple'>

通过内建函数 type() 你可以知道任意变量的数据类型。还记得我们使用 len() 函数来查询任意序列类型数据的长度吗?

>>> type(len)
<class 'builtin_function_or_method'>

集合

集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 symmetric difference(对称差集)等数学运算。

大括号或 set() 函数可以用来创建集合。注意:想要创建空集合,你必须使用 set() 而不是 {}。后者用于创建空字典,我们在下一节中介绍的一种数据结构。

下面是集合的常见操作:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # 你可以看到重复的元素被去除
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket
True
>>> 'crabgrass' in basket
False

>>> # 演示对两个单词中的字母进行集合操作
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # a 去重后的字母
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # a 有而 b 没有的字母
{'r', 'd', 'b'}
>>> a | b                              # 存在于 a 或 b 的字母
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # a 和 b 都有的字母
{'a', 'c'}
>>> a ^ b                              # 存在于 a 或 b 但不同时存在的字母
{'r', 'd', 'b', 'm', 'z', 'l'}

从集合中添加或弹出元素:

>>> a = {'a','e','h','g'}
>>> a.pop()  # pop 方法随机删除一个元素并打印
'h'
>>> a.add('c')
>>> a
{'c', 'e', 'g', 'a'}

字典

字典是无序的键值对(key:value)集合,同一个字典内的键必须是互不相同的。一对大括号 {} 创建一个空字典。初始化字典时,在大括号内放置一组逗号分隔的键:值对,这也是字典输出的方式。我们使用键来检索存储在字典中的数据。

>>> data = {'kushal':'Fedora', 'kart_':'Debian', 'Jace':'Mac'}
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian'}
>>> data['kart_']
'Debian'

创建新的键值对很简单:

>>> data['parthan'] = 'Ubuntu'
>>> data
{'kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}

使用 del 关键字删除任意指定的键值对:

>>> del data['kushal']
>>> data
{'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'

使用 in 关键字查询指定的键是否存在于字典中。

>>> 'ShiYanLou' in data
False

必须知道的是,字典中的键必须是不可变类型,比如你不能使用列表作为键。

dict() 可以从包含键值对的元组中创建字典。

>>> dict((('Indian','Delhi'),('Bangladesh','Dhaka')))
{'Indian': 'Delhi', 'Bangladesh': 'Dhaka'}

如果你想要遍历一个字典,使用字典的 items() 方法。

>>> data
{'Kushal': 'Fedora', 'Jace': 'Mac', 'kart_': 'Debian', 'parthan': 'Ubuntu'}
>>> for x, y in data.items():
...     print("{} uses {}".format(x, y))
...
Kushal uses Fedora
Jace uses Mac
kart_ uses Debian
parthan uses Ubuntu

许多时候我们需要往字典中的元素添加数据,我们首先要判断这个元素是否存在,不存在则创建一个默认值。如果在循环里执行这个操作,每次迭代都需要判断一次,降低程序性能。

我们可以使用 dict.setdefault(key, default) 更有效率的完成这个事情。

>>> data = {}
>>> data.setdefault('names', []).append('Ruby')
>>> data
{'names': ['Ruby']}
>>> data.setdefault('names', []).append('Python')
>>> data
{'names': ['Ruby', 'Python']}
>>> data.setdefault('names', []).append('C')
>>> data
{'names': ['Ruby', 'Python', 'C']}

试图索引一个不存在的键将会抛出一个 keyError 错误。我们可以使用 dict.get(key, default) 来索引键,如果键不存在,那么返回指定的 default 值。

>>> data['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'foo'
>>> data.get('foo', 0)
0

如果你想要在遍历列表(或任何序列类型)的同时获得元素索引值,你可以使用 enumerate()

>>> for i, j in enumerate(['a', 'b', 'c']):
...     print(i, j)
...
0 a
1 b
2 c

你也许需要同时遍历两个序列类型,你可以使用 zip() 函数。

>>> a = ['Pradeepto', 'Kushal']
>>> b = ['OpenSUSE', 'Fedora']
>>> for x, y in zip(a, b):
...     print("{} uses {}".format(x, y))
...
Pradeepto uses OpenSUSE
Kushal uses Fedora

程序示例

本节实验将会通过两个实例程序来熟悉 Python3 的基本数据结构:

  1. 判断学生成绩是否达标的程序
  2. 计算两个矩阵的 Hadamard 乘积

students.py

这是一个判断学生成绩是否达标的程序,要求输入学生数量,以及各个学生物理、数学、历史三科的成绩,如果总成绩小于 120,程序打印 “failed”,否则打印 “passed”。

代码写入 /home/shiyanlou/students.py 文件:

#!/usr/bin/env python3
n = int(input("Enter the number of students: "))
data = {} # 用来存储数据的字典变量
Subjects = ('Physics', 'Maths', 'History') # 所有科目
for i in range(0, n):
    name = input('Enter the name of the student {}: '.format(i + 1)) # 获得学生名称
    marks = []
    for x in Subjects:
        marks.append(int(input('Enter marks of {}: '.format(x)))) # 获得每一科的分数
    data[name] = marks
for x, y in data.items():
    total =  sum(y)
    print("{}'s total marks {}".format(x, total))
    if total < 120:
        print(x, "failed :(")
    else:
        print(x, "passed :)")

name 和 marks 是变量,name 用来存储学生的名称,marks 是个列表,用来存储输入的学生的成绩数据。

data 是个字典,字典的键值对中,键指的是 name 的值,值指的是 marks 的值。因此会使用 data[name] = marks 将学生数据存入到 data 字典。

最后通过 for 循环遍历字典,x 为学生的 name,y 为学生成绩列表 marks,sum() 函数会将传入的列表进行加和。

运行如下:

此处输入图片的描述

matrixmul.py

这个例子里我们计算两个矩阵的 Hadamard 乘积。要求输入矩阵的行/列数(在这里假设我们使用的是 n × n 的矩阵)。

代码写入 /home/shiyanlou/matrixmul.py 文件:

#!/usr/bin/env python3
n = int(input("Enter the value of n: "))
print("Enter values for the Matrix A")
a = []
for i in range(n):
    a.append([int(x) for x in input().split()])
print("Enter values for the Matrix B")
b = []
for i in range(n):
    b.append([int(x) for x in input().split()])
c = []
for i in range(n):
    c.append([a[i][j] * b[i][j] for j in range(n)])
print("After matrix multiplication")
print("-" * 7 * n)
for x in c:
    for y in x:
        print(str(y).rjust(5), end=' ')
    print()
print("-" * 7 * n)

运行如下:

此处输入图片的描述

这里我们使用了几次列表推导式。[int(x) for x in input().split()] 首先通过 input() 获得用户输入的字符串,再使用 split() 分割字符串得到一系列的数字字符串,然后用 int() 从每个数字字符串创建对应的整数值。我们也使用了 [a[i][j] * b[i][j] for j in range(n)] 来得到矩阵乘积的每一行数据。

总结

本实验了解了 Python 内置的几种常用数据结构,在写程序的过程中,不同的场景应当选取合适的数据结构。

一般来说,目前我们见到的数据结构已经够用了,不过 Python 中还有一些其它有用的数据结构,可以在这里了解:Data Types — Python 3.12.4 documentation。

字符串

字符串是 Python 中最常用的数据类型。本节实验将会学习如何对 Python3 的字符串进行处理操作。

知识点
  • 字符串的 3 种表示
  • 字符串的分割、连接、大小写转换、搜索等常用操作

字符串表示

可以通过几种不同的方式表示字符串。如单引号('...')或双引号("...")。下面的例子能帮助你更好的理解字符串。

>>> s = "I am Chinese"
>>> s
'I am Chinese'
>>> s = 'I am Chinese'
>>> s = "Here is a line \
... split in two lines"
>>> s
'Here is a line split in two lines'
>>> s = "Here is a line \n split in two lines"
>>> s
'Here is a line \n split in two lines'
>>> print(s)
Here is a line
 split in two lines

如果你想要分几行输入字符串,并且希望行尾的换行符自动包含到字符串当中,可以使用三对引号:"""...""" 或 '''...''' 。

>>> 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

字符串的方法

每一个字符串对象都有几个可用的内建方法,我们已经使用过一些了,比如 s.split()

>>> s = "shi yan lou"
>>> s.title()
'Shi Yan Lou'

方法 title() 返回字符串的标题版本,即单词首字母大写其余字母小写。

>>> z = s.upper()
>>> z
'SHI YAN LOU'
>>> z.lower()
'shi yan lou'

方法 upper() 返回字符串全部大写的版本,反之 lower() 返回字符串的全部小写版本。

>>> s = "I am A pRoGraMMer"
>> s.swapcase()
'i AM a PrOgRAmmER'

方法 swapcase() 返回字符串大小写交换后的版本 :)

>>> s = "jdwb 2323bjb"
>>> s.isalnum()
False
>>> s = "jdwb2323bjb"
>>> s.isalnum()
True

方法 isalnum() 检查所有字符是否只有字母和数字,上面的代码中第一行的字符串 s 中包含空格字符,所以返回 False

>>> s = "SankarshanSir"
>>> s.isalpha()
True
>>> s = "Sankarshan Sir"
>>> s.isalpha()
False

方法 isalpha() 检查字符串之中是否只有字母。

>>> s = "1234"
>>> s.isdigit() # 检查字符串是否所有字符为数字
True
>>> s = "ShiYanLou is coming"
>>> s.islower() # 检查字符串是否所有字符为小写
False
>>> s = "Shiyanlou Is Coming"
>>> s.istitle() # To 检查字符串是否为标题样式
True
>>> s = "CHINA"
>>> s.isupper() # 检查字符串是否所有字符为大写
True

我们可以使用 split() 分割任意字符串,split() 允许有一个参数,用来指定字符串以什么字符分隔(默认为 " "),它返回一个包含所有分割后的字符串的列表。

>>> s = "We all love Python"
>>> s.split()
['We', 'all', 'love', 'Python']
>>> x = "shiyanlou:is:waiting"
>>> x.split(':')
['shiyanlou', 'is', 'waiting']

相反的,方法 join() 使用指定字符连接多个字符串,它需要一个包含字符串元素的列表作为输入然后连接列表内的字符串元素。

>>> "-".join("GNU/Linux is great".split())
'GNU/Linux-is-great'

在上面的例子中,我们基于空格 " " 分割字符串 "GNU/Linux is great",然后用 "-" 连接它们。

字符串剥离

字符串有几个进行剥离操作的方法。最简单的一个是 strip(chars),用来剥离字符串首尾中指定的字符,它允许有一个字符串参数,这个参数为剥离哪些字符提供依据。不指定参数则默认剥离掉首尾的空格和换行符,代码如下:

>>> s = "  a bc\n "
>>> s.strip()
'a bc'

你可以使用 lstrip(chars) 或 rstrip(chars) 只对字符串左或右剥离。

>>> s = "www.foss.in"
>>> s.lstrip("cwsd.") #删除在字符串左边出现的'c','w','s','d','.'字符
'foss.in'
>>> s.rstrip("cnwdi.") #删除在字符串右边出现的'c','n','w','d','i','.'字符
'www.foss'

文本搜索

字符串有一些方法能够帮助你搜索字符串里的文本或子字符串。下面给出示例:

>>> s = "faulty for a reason"
>>> s.find("for")
7
>>> s.find("fora")
-1
>>> s.startswith("fa") # 检查字符串是否以 fa 开头
True
>>> s.endswith("reason") # 检查字符串是否以 reason 结尾
True

find() 能帮助你找到第一个匹配的子字符串,没有找到则返回 -1。

回文检查

回文是一种无论从左还是从右读都一样的字符序列。比如 “madam”。在这个例子中,我们检查用户输入的字符串是否是回文,并输出结果。

代码写入文件 /home/shiyanlou/palindrome.py

#!/usr/bin/env python3
s = input("Please enter a string: ")
z = s[::-1]  #把输入的字符串s 进行倒序处理形成新的字符串z
if s == z:
    print("The string is a palindrome")
else:
    print("The string is not a palindrome")

运行程序:

此处输入图片的描述

单词计数

在讲解单词计数之前我们先了解一个概念:格式化操作符(%)。

我们先看下面的这个例子:

print("my name is %s.I am %d years old" % ('Shixiaolou',4))

此处输入图片的描述

在这个例子中,%s 为第一个格式符,表示一个字符串;%d 为第二个格式符,表示一个整数。格式符为真实值预留位置,并控制显示的格式。常用的有:

  • %s 字符串(用 str() 函数进行字符串转换)
  • %r 字符串(用 repr() 函数进行字符串转换)
  • %d 十进制整数
  • %f 浮点数
  • %% 字符 %

那么接下来我们对用户输入的一行文本进行单词计数。

代码写入文件 /home/shiyanlou/countwords.py

#!/usr/bin/env python3
s = input("Enter a line: ")
print("The number of words in the line are %d" % (len(s.split(" "))))

运行程序:

此处输入图片的描述

总结

回顾本节实验的知识点:

  • 字符串的 3 种表示
  • 字符串的分割、连接、大小写转换、搜索等常用操作

本实验学习了字符串的几种表示方法(3 种引号)以及对字符串的各种常用操作(分割、连接、大小写转换、搜索...),应当熟练掌握它们。

单词计数的实例需要仔细理解,字符串的更多用法在实验楼的项目实验中也都会涉及到。

函数

我们经常需要在同一个程序里多次复用代码。函数可以很好的帮助我们完成这一点。我们在函数里写我们要重复做的事,然后我们在任何需要的时候调用它。我们已经看到一些内建的函数,比如 len()divmod()

知识点
  • 函数的定义
  • 局部/全局变量的概念
  • 默认参数,关键字参数及强制关键字参数
  • 文档字符串的使用
  • 高阶函数,map() 函数

定义一个函数

我们使用关键字 def 来定义一个函数,语法描述如下所示:

def 函数名(参数):
    语句1
    语句2

让我们编写一个函数,它将接受两个整数作为输入,然后返回总和。

>>> def sum(a, b):
...     return a + b

第二行有个 return 关键字,我们把 a + b 的值返回给调用者。

你必须像下面这样调用这个函数。

>>> res = sum(234234, 34453546464)
>>> res
34453780698L

还记得我们上一个实验讲过的回文检查程序么,让我们编写一个函数来检查给出的字符串是否为回文,然后返回 True 或者 False

#!/usr/bin/env python3
def palindrome(s):
    return s == s[::-1]
if __name__ == '__main__':
    s = input("Enter a string: ")
    if palindrome(s):
        print("Yay a palindrome")
    else:
        print("Oh no, not a palindrome")

将上述代码写入到 testpalindrome.py 文件,执行如下:

python3 testpalindrome.py
Enter a string: test111
Oh no, not a palindrome

python3 testpalindrome.py
Enter a string: 1234321
Yay a palindrome

局域或全局变量

我们通过几个例子来弄明白局域或全局变量,首先我们在函数内部和函数调用的代码中都使用同一个变量 a,将下方代码写入 /home/shiyanlou/local.py

#!/usr/bin/env python3
def change():
    a = 90
    print(a)
a = 9
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)

运行程序:

此处输入图片的描述

首先我们对 a 赋值 9,然后调用更改函数,这个函数里我们对 a 赋值 90,然后打印 a 的值。调用函数后我们再次打印 a 的值。

当我们在函数里写 a = 90 时,它实际上创建了一个新的名为 a 的局部变量,这个变量只在函数里可用,并且会在函数完成时销毁。所以即使这两个变量的名字都相同,但事实上他们并不是同一个变量。

那么如果我们先定义 a,在函数中是否可以直接使用呢?

例如下面这段代码:

#!/usr/bin/env python3
a = 9
def change():
    print(a)
change()

这段代码是没有问题的,可以直接打印输出 9。稍微改动一下:

#!/usr/bin/env python3
a = 9
def change():
    print(a)
    a = 100
change()

现在就会报错了:“UnboundLocalError: local variable 'a' referenced before assignment”,原因是当函数中只要用到了变量 a,并且 a 出现在表达式 = 的前面,就会被当作局部变量。当执行到 print(a) 的时候会报错,因为 a 作为函数局部变量是在 print(a) 之后才定义的。

现在我们使用 global 关键字,对函数中的 a 标志为全局变量,让函数内部使用全局变量的 a,那么整个程序中出现的 a 都将是这个:

#!/usr/bin/env python3
a = 9
def change():
    global a
    print(a)
    a = 100
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)

程序中的 end=' ' 参数表示,print 打印后的结尾不用换行,而用空格。默认情况下 print 打印后会在结尾换行。

程序执行的结果,不会报错了,因为函数体内可以访问全局的变量 a

Before the function call  9
inside change function 9
After the function call  100

在函数内使用 global 会有什么作用呢?尝试下面的代码:

#!/usr/bin/env python3
def change():
    global a
    a = 90
    print(a)
a = 9
print("Before the function call ", a)
print("inside change function", end=' ')
change()
print("After the function call ", a)

程序执行的结果:

Before the function call  9
inside change function 90
After the function call  90

这里通过关键字 global 来告诉 a 的定义是全局的,因此在函数内部更改了 a 的值,函数外 a 的值也实际上更改了。

运行程序:

此处输入图片的描述

默认参数值

函数的参数变量可以有默认值,也就是说如果我们在调用函数时对指定的参数变量没有给出任何值则会赋其默认值。

>>> def test(a , b=-99):
...     if a > b:
...         return True
...     else:
...         return False

在上面的例子里,我们在函数的参数列表写出 b = -99。这表示如果调用者未给出 b 的值,那么 b 的值默认为 -99。这是一个关于默认参数的非常简单的例子。

你可以通过调用函数测试代码。

>>> test(12, 23)
False
>>> test(12)
True

有两个非常重要的地方,第一个是具有默认值的参数后面不能再有普通参数,比如 f(a,b=90,c) 就是错误的。

第二个是默认值只被赋值一次,因此如果默认值是任何可变对象时会有所不同,比如列表、字典或大多数类的实例。例如,下面的函数在后续调用过程中会累积(前面)传给它的参数:

>>> def f(a, data=[]):
...     data.append(a)
...     return data
...
>>> print(f(1))
[1]
>>> print(f(2))
[1, 2]
>>> print(f(3))
[1, 2, 3]

要避免这个问题,你可以像下面这样:

>>> def f(a, data=None):
...     if data is None:
...         data = []
...     data.append(a)
...     return data
...
>>> print(f(1))
[1]
>>> print(f(2))
[2]

关键字参数

函数可以通过关键字参数的形式来调用,形如 keyword = value。如下:

>>> def func(a, b=5, c=10):
...     print('a is', a, 'and b is', b, 'and c is', c)
...
>>> func(12, 24)
a is 12 and b is 24 and c is 10
>>> func(12, c = 24)
a is 12 and b is 5 and c is 24
>>> func(b = 12, c = 24, a = -1)
a is -1 and b is 12 and c is 24

在上面的例子中你能看见调用函数时使用了变量名,比如 func(12,c = 24),这样我们将 24 赋给 c 且 b 具有默认值。

强制关键字参数

我们也能将函数的参数标记为只允许使用关键字参数。用户调用函数时将只能对每一个参数使用相应的关键字参数。

>>> def hello(*, name='User'):
...     print("Hello", name)
...
>>> hello('shiyanlou')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() takes 0 positional arguments but 1 was given
>>> hello(name='shiyanlou')
Hello shiyanlou

了解更多,请阅读PEP-3102。

文档字符串

在 Python 里我们使用文档字符串(_docstrings_)来说明如何使用代码,这在交互模式非常有用,也能用于自动创建文档。下面我们来看看使用文档字符串的例子。

#!/usr/bin/env python3
import math

def longest_side(a, b):
    """
    Function to find the length of the longest side of a right triangle.

    :arg a: Side a of the triangle
    :arg b: Side b of the triangle

    :return: Length of the longest side c as float
    """
    return math.sqrt(a*a + b*b)

if __name__ == '__main__':
    print(longest_side.__doc__)
    print(longest_side(4,5))

运行程序:

此处输入图片的描述

高阶函数

高阶函数(_Higher-order function_)或仿函数(_functor_)是可以接受函数作为参数的函数:

  • 使用一个或多个函数作为参数
  • 返回另一个函数作为输出

Python 里的任何函数都可以作为高阶函数,下面举一个简单的例子:

# 创建一个函数,将参数列表中每个元素都变成全大写
>>> def high(l):
...     return [i.upper() for i in l]
...
# 创建高阶函数,接受一个函数和一个列表作为参数
>>> def test(h, l):
...     return h(l)
...
>>> l = ['python', 'Linux', 'Git']
# 运行高阶函数,返回预期的结果
>>> test(high, l)
['PYTHON', 'LINUX', 'GIT']

阅读官方文档了解更多。

map 函数

map 是一个在 Python 里非常有用的高阶函数。它接受一个函数和一个序列(迭代器)作为输入,然后对序列(迭代器)的每一个值应用这个函数,返回一个序列(迭代器),其包含应用函数后的结果。

举例:

>>> lst = [1, 2, 3, 4, 5]
>>> def square(num):
...     "返回所给数字的平方."
...     return num * num
...
>>> print(list(map(square, lst)))
[1, 4, 9, 16, 25]

总结

经过本实验应当知道如何定义函数,局域变量和全局变量一定要弄清楚,参数默认值、关键字参数也需要掌握。

另外,其它高级语言常见的函数重载,Python 是没有的,这是因为 Python 有默认参数这个功能,函数重载 的功能大都可以使用默认参数达到。

在后面我们还介绍了高阶函数的概念并使用了 map() 函数。在 Python 中还有其它的高阶函数,如 sorted()、filter() 以及 functools 模块中的函数,大家可以了解一下。

文件处理

文件是保存在计算机存储设备上的一些信息或数据。你已经知道了一些不同的文件类型,比如你的音乐文件,视频文件,文本文件。Python 给了你一些简单的方式操纵文件。通常我们把文件分为两类,文本文件和二进制文件。文本文件是简单的文本,二进制文件包含了只有计算机可读的二进制数据。

知识点
  • 文件打开模式
  • 文件读取与写入
  • with 语句

文件操作

本部分会实践学习以下的知识:

  1. 文件打开与关闭
  2. 文件读取与写入

所有的实例程序都需要你在实验环境中完整的输入并执行。

文件打开

我们使用 open() 函数打开文件。它需要两个参数,第一个参数是文件路径或文件名,第二个是文件的打开模式。模式通常是下面这样的:

  • "r",以只读模式打开,你只能读取文件但不能编辑/删除文件的任何内容
  • "w",以写入模式打开,如果文件存在将会删除里面的所有内容,然后打开这个文件进行写入
  • "a",以追加模式打开,写入到文件中的任何数据将自动添加到末尾

默认的模式为只读模式,也就是说如果你不提供任何模式,open() 函数将会以只读模式打开文件。我们将实验打开一个文件,不过要准备实验材料:

wget https://labfile.oss-internal.aliyuncs.com/courses/596/sample.txt

此处输入图片的描述

然后进入 Python3 打开这个文件。

>>> fobj = open("sample.txt")
>>> fobj
<_io.TextIOWrapper name='sample.txt' mode='r' encoding='UTF-8'>

文件关闭

打开文件后我们应该总是关闭文件。我们使用方法 close() 完成这个操作。

>>> fobj.close()

始终确保你显式关闭每个打开的文件,一旦它的工作完成你没有任何理由保持打开文件。因为程序能打开的文件数量是有上限的。如果你超出了这个限制,没有任何可靠的方法恢复,因此程序可能会崩溃。每个打开的文件关联的数据结构(文件描述符/句柄/文件锁...)都要消耗一些主存资源。因此如果许多打开的文件没用了你可以结束大量的内存浪费,并且文件打开时始终存在数据损坏或丢失的可能性。

文件读取

使用 read() 方法一次性读取整个文件。

>>> fobj = open("sample.txt")
>>> fobj.read()
'I love Python\nI love shiyanlou\n'
>>> fobj.close()

如果你再一次调用 read(),它会返回空字符串因为它已经读取完整个文件。

read(size) 有一个可选的参数 size,用于指定字符串长度。如果没有指定 size  或者指定为负数,就会读取并返回整个文件。当文件大小为当前机器内存两倍时,就会产生问题。反之,会尽可能按比较大的  size  读取和返回数据。

readline() 能帮助你每次读取文件的一行。

>>> fobj = open("sample.txt")
>>> fobj.readline()
'I love Python\n'
>>> fobj.readline()
'I love shiyanlou\n'
>>> fobj.close()

使用 readlines() 方法读取所有行到一个列表中。

>>> fobj = open('sample.txt')
>>> fobj.readlines()
['I love Python\n', 'I love shiyanlou\n']
>>> fobj.close()

你可以循环遍历文件对象来读取文件中的每一行。

>>> fobj = open('sample.txt')
>>> for x in fobj:
...     print(x, end = '')
...
I love Python
I love shiyanlou
>>> fobj.close()

让我们写一个程序,这个程序接受用户输入的字符串作为将要读取的文件的文件名,并且在屏幕上打印文件内容。

#!/usr/bin/env python3
name = input("Enter the file name: ")
fobj = open(name)
print(fobj.read())
fobj.close()

运行程序:

cd /home/shiyanlou
chmod +x test.py
./test.py
Enter the file name: sample.txt
I love Python
I love shiyanlou

文件写入

让我们通过 write() 方法打开一个文件然后我们随便写入一些文本。

>>> fobj = open("ircnicks.txt", 'w')
>>> fobj.write('powerpork\n')
>>> fobj.write('indrag\n')
>>> fobj.write('mishti\n')
>>> fobj.write('sankarshan')
>>> fobj.close()

现在读取我们刚刚创建的文件。

>>> fobj = open('ircnicks.txt')
>>> s = fobj.read()
>>> fobj.close()
>>> print(s)
powerpork
indrag
mishti
sankarshan

文件操作示例程序

本部分实验将通过两个实例程序来实践之前学习的文件操作方法:

  1. 拷贝文件程序
  2. 文本文件信息统计程序

所有的实例程序都需要你在实验环境中完整的输入并执行。

拷贝文件

在这个例子里我们拷贝给定的文本文件到另一个给定的文本文件。

#!/usr/bin/env python3
import sys
if len(sys.argv) < 3:
    print("Wrong parameter")
    print("./copyfile.py file1 file2")
    sys.exit(1)
f1 = open(sys.argv[1])
s = f1.read()
f1.close()
f2 = open(sys.argv[2], 'w')
f2.write(s)
f2.close()

运行程序:

图片描述

你可以看到我们在这里使用了一个新模块 syssys.argv 包含所有命令行参数。这个程序的功能完全可以使用 shell 的 cp 命令替代:在 cp 后首先输入被拷贝的文件的文件名,然后输入新文件名。

sys.argv 的第一个值是命令自身的名字,下面这个程序打印命令行参数。

#!/usr/bin/env python3
import sys
print("First value", sys.argv[0])
print("All values")
for i, x  in enumerate(sys.argv):
    print(i, x)

运行程序:

./argvtest.py Hi there
First value ./argvtest.py
All values
0 ./argvtest.py
1 Hi
2 there

这里我们用到了一个新函数 enumerate(iterableobject),在序列中循环时,索引位置和对应值可以使用它同时得到。

文本文件相关信息统计

让我们试着编写一个程序,对任意给定文本文件中的制表符、行、空格进行计数。

代码写入文件 /home/shiyanlou/parsefile.py

#!/usr/bin/env python3

import os
import sys


def parse_file(path):
    """
    分析给定文本文件,返回其空格、制表符、行的相关信息

    :arg path: 要分析的文本文件的路径

    :return: 包含空格数、制表符数、行数的元组
    """
    fd = open(path)
    i = 0
    spaces = 0
    tabs = 0
    for i,line in enumerate(fd):
        spaces += line.count(' ')
        tabs += line.count('\t')
    # 现在关闭打开的文件
    fd.close()

    # 以元组形式返回结果
    return spaces, tabs, i + 1

def main(path):
    """
    函数用于打印文件分析结果

    :arg path: 要分析的文本文件的路径
    :return: 若文件存在则为 True,否则 False
    """
    if os.path.exists(path):
        spaces, tabs, lines = parse_file(path)
        print("Spaces {}. tabs {}. lines {}".format(spaces, tabs, lines))
        return True
    else:
        return False


if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(sys.argv[1])
    else:
        sys.exit(-1)
    sys.exit(0)

运行程序:

此处输入图片的描述

你可以看到程序有两个函数,main() 和 parse_file() ,parse_file 函数真正的分析文件并返回结果,然后在 main() 函数里打印结果。通过分割代码到一些更小的单元(函数)里,能帮助我们组织代码库并且也更容易为函数编写测试用例。

使用 with 语句

在实际情况中,我们应该尝试使用 with 语句处理文件对象,它会在文件用完后会自动关闭,就算发生异常也没关系。它是 try-finally 块的简写:

>>> with open('sample.txt') as fobj:
...     for line in fobj:
...         print(line, end = '')
...
I love Python
I love shiyanlou

实现 lscpu 命令

在 Linux 下你可以使用 lscpu 命令来查看当前电脑的 CPU 相关信息,如下图:

此处输入图片的描述

实际上 lscpu 命令是读取 /proc/cpuinfo 这个文件的信息并美化输出,现在你可以自己写一个 Python 程序以只读模式读取 /proc/cpuinfo 这个文件,然后打印出来,这样你就有自己的一个 Python 版本的 lscpu 命令了 :)

记得一行一行的读取文本文件,不要一次性读取整个文件,因为有时候你读取的文件可能比可用内存还大。

总结

回顾本节实验知识点:

  • 文件打开模式
  • 文件读取与写入
  • with 语句

本实验我们学习了文件的打开与读写,在读写完毕后一定要记得关闭文件,或者使用 with 语句也是极好的。在 Linux 中你还需要注意你操作的文件的权限。Linux 有一个思想是“一切皆文件”,这在实验最后的 lscpu 的实现中得到了体现。

挑战:字符串操作

介绍

我们需要实现一个程序用来提取文件中的字符串中的数字,然后打印输出。

目标

首先,打开 Xfce 终端,下载所需的测试文件:

cd /tmp
wget https://labfile.oss-internal.aliyuncs.com/courses/790/String.txt

这个文件 /tmp/String.txt 中存储了一个很长的字符串,需要读取并进行处理。

然后,我们在 /home/shiyanlou/Code 创建 FindDigits.py Python 脚本:

cd /home/shiyanlou/Code
touch FindDigits.py

FindDigits.py 中,我们需要完成以下任务:

  1. 使用 open 打开文件 /tmp/String.txt 并读取其中的字符串
  2. 提取字符串中的所有数字,并组合成一个新的字符串,然后打印输出

程序执行过程如下:

python3 /home/shiyanlou/Code/FindDigits.py

提示语

  • 可以使用循环来访问字符串中的单个字符
  • 可以使用 isdigit() 来判断字符是否为数字
  • 使用 print() 把新的数字组成的字符串输出
  • 代码文件必须保存在 /home/shiyanlou/Code/FindDigits.py

知识点

  • 文件读取
  • for 循环
  • 字符串操作

参考代码

# 打开并读取文件里的字符串
with open('/tmp/String.txt') as f:
    s = f.read()
    res = ""
    # 循环字符串里的每个字符,判断是否为数字
    for char in s:
        if char.isdigit():
            res += char
    print(res)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/746566.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过颜色传感器控制机械臂抓物体

目录 1 绪论 2整体设计方案 2.1 系统的介绍 2.2 抓取模块 2.2.1 机械臂的定义 2.2.2 机械臂的分类 2.2.3 机械臂的选用 2.3 颜色识别模块 2.3.1 颜色传感器识别原理 2.3.2 TCS3200简介 2.4 整体控制方案 3 颜色识别抓取系统的硬件设计 3.1 单片机选型及参数 3.2 系…

13.爬虫---PyMongo安装与使用

13.PyMongo安装与使用 1.安装 PyMongo2.使用PyMongo2.1连接数据库和集合2.2增加数据2.3修改数据2.4查询数据2.5删除数据 3.总结 MongoDB 安装可以看这篇文章MongoDB安装配置教程&#xff08;详细版&#xff09; 1.安装 PyMongo PyMongo 是Python中用于连接MongoDB数据库的库&a…

适用于 Windows 11 的 5 大数据恢复软件 [免费和付费]

为什么我们需要Windows 11数据恢复软件&#xff1f; 计算机用户经常遇到的一件事就是数据丢失&#xff0c;这种情况随时可能发生。错误地删除重要文件和文件夹可能会非常令人担忧&#xff0c;但幸运的是&#xff0c;有一种方法可以恢复 PC 上丢失的数据。本文将向您展示可用于…

AI引领创意潮流:高效生成图片,参考图助力,一键保存到指定文件夹

在这个数字与创意交融的时代&#xff0c;我们迎来了AI绘画的新纪元。借助先进的AI技术&#xff0c;我们不仅能够高效生成图片&#xff0c;还能在参考图的启发下&#xff0c;激发无限创意&#xff0c;让您的想象力在数字世界中自由翱翔。 首助编辑高手软件中的魔法智能绘图板块&…

路径规划算法--DFS

文章目录 一、DFS二、DFS伪代码三、DFS做全覆盖路径 一、DFS DFS&#xff08;Depth First Search&#xff09;为深度优先搜索&#xff0c;是一种用于遍历或搜索树或图的搜索算法。DFS是从当前点出发&#xff0c;沿着一个方向一直搜索&#xff0c;如果搜索完成且未搜索到目标点…

C++系列-String(三)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” assign 这个接口的目的是用一个新的值代替之前的那个值 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string> #include<list> #include&l…

HTTP协议中的各种请求头、请求类型的作用以及用途

目录 一、http协议介绍二、http协议的请求头三、http协议的请求类型四、http协议中的各种请求头、请求类型的作用以及用途 一、http协议介绍 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一种用于分布式、协作式和超媒体信息系统的应…

计算机组成原理 | CPU子系统(3)MIPS32指令架构

MIPS32架构指令格式 MIPS32架构寻址方式 指令的编码与功能

突破内存限制:Jamba模型的高效文本处理能力

在当今信息爆炸的时代&#xff0c;处理和理解海量文本数据的需求日益增长。自然语言处理&#xff08;NLP&#xff09;领域的研究者们一直在探索如何构建更高效、更强大且更灵活的语言模型来应对这一挑战。然而&#xff0c;现有的大型语言模型&#xff0c;尤其是基于Transformer…

昇思25天学习打卡营第8天|保存与加载

一、简介&#xff1a; 上一章节主要介绍了如何调整超参数&#xff0c;并进行网络模型训练。在训练网络模型的过程中&#xff0c;实际上我们希望保存中间和最后的结果&#xff0c;用于微调&#xff08;fine-tune&#xff09;和后续的模型推理与部署&#xff0c;本章节我们将介绍…

关于Vite+Vue+Ts WebStorm路径别名的问题

一、准备一个项目 二、在 vite.config.js 中添加 resolve: {alias: {: /src}} 三、tsconfig.app.json中添加代码 //添加代码"baseUrl": ".","paths": {"/*": ["src/*"]}把src的一个文件修改路径为开头 四、安装插件 npm i …

【嵌入式Linux】i.MX6ULL GPIO 中断服务函数注册与编写

文章目录 1 外部中断初始化与中断服务函数1.2 外部中断初始化函数 exti_init1.2.1 GPIO引脚配置1.2.2 中断使能与注册1.2.3 GIC_EnableIRQ()函数的分析 1.3 中断服务函数 gpio1_io20_irqhandler1.3.1 消抖处理1.3.2 中断事件处理1.3.3 清除中断标志 2 BUG处理2.1 问题描述2.2 解…

打破数据分析壁垒:SPSS复习必备(九)

有序定性资料统计推断 1.分类 单向有序行列表 双向有序属性相同行列表 双向有序属性不同行列表 2.单向有序行列表 秩和检验 ① 两组单向有序分类资料 ②多组单向有序定性资料 步骤&#xff1a; 1.建立检验假设和确定检验水准 2.编秩 3.求秩和 4.确定检验统计量 5…

Sora:探索AI视频模型的无限可能

随着人工智能技术的飞速发展&#xff0c;AI在视频处理和生成领域的应用正变得越来越广泛。Sora&#xff0c;作为新一代AI视频模型&#xff0c;展示了前所未有的潜力和创新能力。本文将深入探讨Sora的功能、应用场景以及它所带来的革命性变化。 一、Sora的核心功能 1.1 视频生…

一年Java|16K|同程艺龙面经

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 背景 公司&#xff1a;同程艺龙成都BU,现场部门老大面 之前的同程艺龙电话一面过了&#xff0c;然后通知到同程艺龙成都办公地点现场进行部门老大…

宠物空气净化器热卖爆款,希喂、小米、352猫用空气净化器真实PK

相信大漫天多数养猫家庭都会有一个烦恼&#xff1a;猫咪们的猫实在是太多了&#xff0c;无法忍受家里面漫天飞舞的浮毛和难闻的猫猫便臭。作为养猫多年的过来人我尝试过很多种方法清理这些猫浮毛和异味&#xff0c;但都以失败告终。 直到后面看到一个宠物博主推荐的宠物空气净…

【干货】【全网最全】【全网最详细】 javaWeb关于Thymeleaf+SpringBoot 的学习教程,看这一篇就够了。

大家好&#xff0c;我是DX3906 第一部分&#xff1a;介绍 Thymeleaf 简介 1.什么是Thymeleaf Thymeleaf是一个用于Java和Java EE平台的服务器端模板引擎&#xff0c;它可以用来在服务端生成HTML、XML、JavaScript、CSS甚至纯文本的输出。Thymeleaf适用于需要快速开发和维护Web…

React+TS前台项目实战(十六)-- 全局常用组件Pagination封装

文章目录 前言Pagination组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 [PC端&手机端] 总结 前言 在上篇文章中&#xff0c;我们封装了表格组件Table&#xff0c;本文则继续封装配套使用的分页器组件。想看Table表格组件的&#xff0c;可自行查看全局常用组件Tab…

【渗透测试】小程序反编译

前言 在渗透测试时&#xff0c;除了常规的Web渗透&#xff0c;小程序也是我们需要重点关注的地方&#xff0c;微信小程序反编译后&#xff0c;可以借助微信小程序开发者工具进行调试&#xff0c;搜索敏感关键字&#xff0c;或许能够发现泄露的AccessKey等敏感信息及数据 工具…

工业液晶屏G065VN01 V2规格书简介

G065VN01 V2 背面实物图 2. 概述 G065VN01 V2 专为 VGA &#xff08;640 x RGB x 480&#xff09; 分辨率和 16.2M&#xff08;RGB 6 位 FRC&#xff09;或 262k 色&#xff08;RGB 6 位&#xff09;的工业显示应用而设计。它由TFT-LCD面板、驱动IC、控制和电源电路板以及包括…
最新文章