文章目录
第一章 环境配置
一、Python环境配置
1、Python环境下载
登录Python官网:
https://www.python.org/

直接找到Donloads,然后点击下载最近的一个版本即可

2、Python指定版本环境下载
但是我还是推荐使用Python 3.8.0的版本(顺带来说说其他的Python版本怎么下载),上图中我们点击下载后,右上角就会弹出一个下载框,我们对准正在下载的Python安装包,右键复制下载链接

复制过来的下载链接如下:
https://www.python.org/ftp/python/3.12.0/python-3.12.0-amd64.exe
这个时候我们可以删掉点内容,就可以找到其他版本的位置:
https://www.python.org/ftp/python/

然后按Ctrl + f搜索3.8.0即可找到对应的版本

然后点击进去,就会发现特别多的东西。按Ctrl + f搜索amd64.exe就找到了我们需要的安装文件了

然后点击下载,等待下载完成

3、Python环境安装
下载完成后双击打开python-3.8.0-amd64.exe

当你打开这个文件后会出现这个界面,有两个安装选项,Install Now(立即安装)和Customize installation(自定义安装)。
Install Now(立即安装)会直接给你安装到C:\Users\Administrator\AppData\Local\Programs\Python\Python38
如果你不想安装到C盘的话可以选择自定义安装。
当然,选择以上这两个选项的话请先勾上Add Python 3.8 to PATH。这个选项会把你的Python加入到你的环境变量当中去,如果不勾选的话在安装完成后就要手动添加到环境变量当中。
(1)选择Install Now(立即安装)
静候它安装完成即可。

如果出现以下界面

Disable path length limit
Changes your machine configuration to allow programs, including Python, to bypass the 260 character "MAX_PATH"limitation.
禁用路径长度限制
更改计算机配置以允许程序(包括 Python)绕过 260 个字符的“MAX_PATH”限制。
这说明你的电脑对Python做出了一些限制,直接点击Disable path length limit(去除环境变量长度限制)即可。点击后它会消失。

(2)选择Customize installation(自定义安装)

选择自定义安装的话它会安装一些操作的东西,最重要的就是pip,因为pip是维护Python里面一些模块的管理工具,后续学一些框架的话,都会用到pip进行安装,所以pip一定要勾选的,其他的默认可以不动了,紧接着下一步。

到了这一步就可以选择安装的路径,但是安装的路径不能有中文,如果路径有中文的话会对路径产生一些问题,然后这些问题比较恶心也比较难解决,所以要养成好习惯,像这样一类的路径就不要有中文了,然后可以开始Install了。
4、Python环境验证
我们使用Win + r打开命令提示符


然后输入python,如果出现一下画面就代表python环境安装成功了

5、pip配置清华源
还是在命令提示符中执行以下命令:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

Microsoft Windows [版本 10.0.19045.3086]
(c) Microsoft Corporation。保留所有权利。
C:\Users\Administrator>pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
Writing to C:\Users\Administrator\AppData\Roaming\pip\pip.ini
C:\Users\Administrator>
二、Visual Studio Code(VSCode)配置
1、VSCode下载
进入到官网进行下载:
https://code.visualstudio.com/

直接点击右上角的Download,页面跳转后,选择Windows

页面跳转后稍等一会就会开始自动下载(如果觉得慢的话可以复制下载链接用迅雷下载)

2、VSCode安装
直接一路默认就好啦

3、VSCode配置
安装完成后第一次打开

(1)下载中文扩展
全英文的,可以通过下载模块解决

重启VSCode即可获得中文界面

(2)下载和配置Code Runner模块
搜索Code Runner模块后直接安装

安装完成后,在左上角找到:文件 → 首选项 → 设置

在搜索设置中搜索Run In Terminal,然后将相关选项前打上√:

三、测试Hello world !

没有任何问题,环境配置基本完成。
第二章 Python程序
一、Python介绍
1、什么是计算机语言
例如,要跟哪个国家的人沟通,就需要你用什么样的语言;如果你要跟计算机沟通,就要用计算机语言。例如C、C++、Java、Python等。
Python相对于其他计算机语言,语法比较简单,非常适合小白入门,其次就是Python能干的事情特别多。
2、Python的历史
略。Python最近几年才开始流行是因为人工智能的领域,人工智能的领域很多使用Python做的,所以带动了Python的爆火。
3、Python能干什么
1、测试。比如软件的测试
2、运维。
3、后端。
4、数据分析。
5、人工智能
6、安全
7、密码
4、Python的版本
3.x和2.x。
5、Python的优缺点
优点:
简单、适合小白学习
缺点:
效率没有C、C++、Java等语言高
6、Python社区
Django框架、Flask框架等
二、Python初识
1、第一个Python程序
print()语法
print() 是 Python 中的一个内置函数,用于将信息输出到控制台。当你想要显示某些数据或者结果时,你可以使用 print() 函数。这个函数非常灵活,可以输出字符串、数字、变量以及更复杂的数据类型,如列表或字典。下面是一些 print() 函数的基本用法:
打印简单的文本消息:
print("Hello, World!")
打印变量的值:
message = "Hello, World!"
print(message)
打印多个项,用逗号分隔,会自动在项之间添加空格:
name = "Alice"
age = 25
print(name, "is", age, "years old.")
使用字符串格式化打印变量:
name = "Bob"
age = 30
print(f"{name} is {age} years old.") # f-string 格式化
打印多行文本,使用三引号:
print("""
This is a multi-line text.
It spans multiple lines.
""")
使用 end 参数来指定 print() 结束后应该添加的字符,默认是换行符 \n:
print("Hello,", end=" ")
print("World!")
输出:Hello, World! 而不是在两个单词之间换行。
使用 sep 参数来指定打印多个项时它们之间应该使用的分隔符,默认是空格:
print("2023", "04", "01", sep="-")
输出:2023-04-01
使用转义字符:
在字符串中,你可以使用反斜杠\来引入所谓的"转义字符",这些字符可以表示一些特殊的字符或者效果。例如,\n表示换行,\t表示制表符(tab),\"表示双引号,\\表示反斜杠本身。
print("Hello\nWorld") # 输出两行
print("Hello\tWorld") # 输出一个制表符
print("Hello\"World") # 输出包含双引号的字符串
print("Hello\\World") # 输出包含反斜杠的字符串
使用flush参数:
print()函数的flush参数可以控制输出是否立即刷新到屏幕。默认情况下,flush参数为False,这意味着Python可能会缓存输出而不是立即显示。如果你希望立即看到输出,可以将flush参数设为True。
import time
print("Waiting...", end="", flush=True)
time.sleep(2) # 等待2秒
print("Done!")
在这个例子中,"Waiting…“会立即显示,然后程序会等待2秒,最后显示"Done!”。
打印复杂的数据结构:
print()函数可以打印各种复杂的数据结构,如列表、元组、字典、集合等。
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_dict = {"name": "Alice", "age": 25}
my_set = {1, 2, 3, 4, 5}
print(my_list)
print(my_tuple)
print(my_dict)
print(my_set)
f-string(格式化字符串字面量)
在Python 3.6及以上版本中,f-string(格式化字符串字面量)是一种新的字符串格式化机制,它通过在字符串前面加上字母f或F来使用。f-string提供了一种非常快捷和直观的方式来嵌入Python表达式到字符串常量中。
f-string的基本语法如下:
f'your string here {expr}'
其中{expr}是Python表达式,它会被计算并插入到字符串中。下面是一些f-string的用法示例:
- 基本用法:
name = 'Alice'
age = 25
greeting = f'Hello, my name is {name} and I am {age} years old.'
print(greeting)
- 表达式计算:
x = 10
y = 5
result = f'The result of {x} * {y} is {x * y}.'
print(result)
- 调用函数:
def to_uppercase(input):
return input.upper()
name = 'Bob'
print(f'Your name in uppercase is {to_uppercase(name)}.')
- 使用字典:
person = {'name': 'Carol', 'age': 30}
print(f"The person's name is {person['name']} and their age is {person['age']}.")
- 格式化数字:
pi = 3.14159265
print(f'Pi rounded to two decimal places is {pi:.2f}.')
- 使用宽度和对齐:
number = 123
print(f'{number:10}') # 默认右对齐,宽度为10
print(f'{number:<10}') # 左对齐
print(f'{number:^10}') # 居中对齐
- 嵌套字段:
width = 10
precision = 4
value = 12.34567
print(f'result: {value:{width}.{precision}}') # 嵌套字段
f-string是一种非常强大的格式化字符串的方法,它使得字符串中变量的插入和格式化变得非常简单和直观。
格式化字符串%s %d %f
print函数用于输出信息到控制台。而%s,%d和%f是格式化字符串的一部分,它们分别代表字符串,整数和浮点数。
-
%s:用于格式化字符串。它会将后面的变量或值转换为字符串并插入到%s的位置。 -
%d:用于格式化整数。它会将后面的变量或值转换为整数并插入到%d的位置。 -
%f:用于格式化浮点数。它会将后面的变量或值转换为浮点数并插入到%f的位置。
name = "Alice"
age = 20
height = 1.68
print("Hello, %s. You are %d years old. Your height is %f meters." % (name, age, height))
在这个例子中,%s被替换为name变量的值(“Alice”),%d被替换为age变量的值(20),%f被替换为height变量的值(1.68)。所以,这行代码的输出将会是:
Hello, Alice. You are 20 years old. Your height is 1.680000 meters.
注意,%f默认会显示小数点后6位,如果你想控制小数点后的位数,可以在%和f之间添加.2,例如%.2f,这样就会保留两位小数。例如:
print("Your height is %.2f meters." % height)
这行代码的输出将会是:
Your height is 1.68 meters.
str.format()函数进行格式化
str.format()函数是Python中的一个内置函数,用于创建格式化的字符串。函数的语法是:
str.format(*args, **kwargs)
在这里,*args是一个非关键字参数的元组,**kwargs是一个关键字参数的字典。
下面是一些使用str.format()函数的例子:
- 位置参数:
print("Hello, {}. You are {} years old.".format("Alice", 25))
输出:Hello, Alice. You are 25 years old.
在这个例子中,"Alice"和25是位置参数,它们分别替换了{}中的内容。
- 关键字参数:
print("Hello, {name}. You are {age} years old.".format(name="Bob", age=30))
输出:Hello, Bob. You are 30 years old.
在这个例子中,name="Bob"和age=30是关键字参数,它们分别替换了{name}和{age}。
- 混合使用位置参数和关键字参数:
print("Hello, {0}. You are {age} years old.".format("Charlie", age=35))
输出:Hello, Charlie. You are 35 years old.
在这个例子中,"Charlie"是位置参数,age=35是关键字参数。
- 使用字典进行格式化:
person = {'name': 'Daniel', 'age': 40}
print("Hello, {name}. You are {age} years old.".format(**person))
输出:Hello, Daniel. You are 40 years old.
在这个例子中,我们使用了一个字典person,并通过**操作符将其解包为关键字参数。
- 格式化数字:
print("The value of pi is approximately {0:.3f}.".format(3.14159))
输出:The value of pi is approximately 3.142.
在这个例子中,:.3f表示我们希望格式化的浮点数保留三位小数。
2、注释
(1)单行注释
在Python中,单行注释是用来给代码加上说明的,这样可以让其他人(或者是未来的你)更容易理解代码的意图。注释不会被Python解释器执行,因此它不会影响程序的运行结果。
要写一个单行注释,你只需要在注释文本前面加上一个井号(#)。Python解释器会忽略掉#以及其后面的所有内容。这就是单行注释。
比如:
# 这是一个单行注释
print("Hello, World!") # 这后面的也是注释
在这个例子中,第一行是一个单行注释,它不会有任何输出。第二行代码后面的内容也是注释,尽管它在一行代码的后面,但它同样不会影响print函数的执行。
注释是编程中的好习惯,它可以帮助你记住某段代码的作用,或者解释一些复杂的逻辑,对于提高代码的可读性非常有帮助。
(2)多行注释
在Python中,多行注释通常使用三个连续的单引号(‘’')或者三个连续的双引号(“”")来包围注释内容。虽然严格来说,这种方式实际上是创建了一个多行字符串,但如果这些字符不是赋值给变量或者不是函数的一部分,那么它们就可以作为多行注释使用,因为Python解释器会忽略掉这样的字符串。
下面是一个使用三个单引号的多行注释的例子:
'''
这是一个多行注释的例子
这里可以写很多行文字
解释器会忽略这部分内容
'''
print("Hello, World!")
同样,也可以使用三个双引号来实现多行注释:
"""
另一个多行注释的例子
使用三个双引号
这些内容同样不会被执行
"""
print("Hello, World!")
在这两个例子中,print函数下面的多行文本都不会影响程序的执行,它们仅仅是为了给人阅读代码的时候提供帮助。记住,虽然这种方式在技术上不是官方的多行注释语法,但在实践中,它被广泛地用作多行注释。
3、运算符
当然可以,Python中的运算符用于执行各种计算和操作。下面是你提到的那些运算符的详细解释:
+(加法运算符):用于将两个或多个数值(或字符串)相加。
print(3 + 5) # 输出:8
print('Hello ' + 'World') # 输出:Hello World
-(减法运算符):用于从一个数中减去另一个数。
print(10 - 3) # 输出:7
*(乘法运算符):用于将两个数相乘,或者将字符串重复指定的次数。
print(4 * 7) # 输出:28
print('Hello ' * 3) # 输出:Hello Hello Hello
/(除法运算符):用于将一个数除以另一个数。结果是浮点数,即使两个操作数都是整数。
print(20 / 5) # 输出:4.0
//(整除运算符):用于将一个数除以另一个数,然后返回结果的整数部分。
print(17 // 5) # 输出:3
%(取余运算符):用于返回除法的余数。
print(17 % 5) # 输出:2
**(幂运算符):用于计算一个数的幂。
print(2 ** 3) # 输出:8
这些运算符可以用于执行各种基本的数学运算,它们是编程中的基础工具。记住,Python中的运算符优先级和数学中的一样,乘法和除法优先于加法和减法,可以使用括号()来改变执行顺序。
4、赋值运算符
当然可以。在Python中,赋值运算符用于将值分配给变量。以下是各种赋值运算符的详细说明:
=:基本赋值运算符。
- 用法:
x = value - 功能:将右侧的值赋给左侧的变量x。
x = 10
print(x) # 输出:10
+=:加法赋值运算符。
- 用法:
x += value等同于x = x + value - 功能:将右侧的值加到左侧变量的当前值上,然后将结果重新赋给左侧的变量。
x = 5
x += 3 # 等同于 x = x + 3
print(x) # 输出:8
-=:减法赋值运算符。
- 用法:
x -= value等同于x = x - value - 功能:从左侧变量的当前值中减去右侧的值,然后将结果重新赋给左侧的变量。
x = 5
x -= 3 # 等同于 x = x - 3
print(x) # 输出:2
*=:乘法赋值运算符。
- 用法:
x *= value等同于x = x * value - 功能:将左侧变量的当前值与右侧的值相乘,然后将结果重新赋给左侧的变量。
x = 5
x *= 3 # 等同于 x = x * 3
print(x) # 输出:15
/=:除法赋值运算符。
- 用法:
x /= value等同于x = x / value - 功能:将左侧变量的当前值除以右侧的值,然后将结果重新赋给左侧的变量。
x = 10
x /= 2 # 等同于 x = x / 2
print(x) # 输出:5.0
%=:模除赋值运算符。
- 用法:
x %= value等同于x = x % value - 功能:将左侧变量的当前值除以右侧的值后取余数,然后将余数重新赋给左侧的变量。
x = 10
x %= 3 # 等同于 x = x % 3
print(x) # 输出:1
**=:幂赋值运算符。
- 用法:
x **= value等同于x = x ** value - 功能:将左侧变量的当前值提升到右侧值的幂,然后将结果重新赋给左侧的变量。
x = 2
x **= 3 # 等同于 x = x ** 3
print(x) # 输出:8
//=:整除赋值运算符。
- 用法:
x //= value等同于x = x // value - 功能:将左侧变量的当前值与右侧的值进行整除,然后将结果重新赋给左侧的变量。
x = 10
x //= 3 # 等同于 x = x // 3
print(x) # 输出:3
练习
使用
math库Python的
math库提供了许多数学函数和常数。
import math
# 计算平方根
sqrt_val = math.sqrt(25)
print("25的平方根:",sqrt_val)
# 计算阶乘
factorial_val = math.factorial(5)
print("5的阶乘:",factorial_val)
# 计算正弦值
sin_val = math.sin(math.radians(30)) # 注意:math.sin()函数接受的是弧度值,所以我们使用math.radians()函数将30°转化成弧度
print("30度的正弦值为:",sin_val)
更多使用库的说明:
1、官方文档:
https://docs.python.org/3/library/math.html
2、使用
help()函数
import math
help(math)
练习1
假设需要设计一个程序来计算一个圆的面积和周长。用户会输入圆的半径,您的程序需要返回该圆的面积和周长
要求:
使用Python的Math库
面积计算公式:
s=Πr² 周长计算公式:
c=2Πr
在math库中“Π”的替代变量为pi.math 用户输入半径后,程序应打印出面积和周长的值。
输出示例:
请输入圆的半径:
圆的面积为:......
圆的周长为:......
.py
# 方法1:
import math
# 获取用户输入的半径
r=eval(input("请输入圆的半径:"))
# pi=math.pi
# 计算圆的面积
area = math.pi*r**2
# 计算周长
circumference = 2*math.pi*r
print("圆的面积为:",area)
print("圆的周长:",circumference)
#方法2:
import numpy as np
# 获取用户输入的半径
r=eval(input("请输入圆的半径:"))
# pi=math.pi
# 计算圆的面积
area = np.pi*r**2
# 计算周长
circumference = 2*np.pi*r
print("圆的面积为:",area)
print("圆的周长:",circumference)
练习2
设计一个程序,计算三角形面积。用户输入三角形的三个边长,程序返回三角形的面积。
要求:
使用python的
math库。 计算公式:半周长s
s = a + b + c 2 s = \frac{a+b+c}{2} s=2a+b+c
面积area:
a r e a = s ( s − a ) ( s − b ) ( s − c ) area = \sqrt{s(s-a)(s-b)(s-c)} area=s(s−a)(s−b)(s−c)
输出示例:
请输入三角形的第1条边长:
请输入三角形的第2条边长:
请输入三角形的第3条边长:
三角形的面积为:
.py
import math
# 用户输入数据
a=eval(input("请输入第1条边长:"))
b=eval(input("请输入第2条边长:"))
c=eval(input("请输入第3条边长:"))
# 求半周长
s=(a+b+c)/2
# 海伦公式求面积
area=math.sqrt(s*(s-a)*(s-b)*(s-c))
# 输出
print("三角形的面积为:",area)
print(f"三角形的面积为:{area}")
print("三角形的面积为:{}".format(area))
练习3:一元二次方程求两根
import math
# 定义一元二次方程系数
a = 1
b = -3
c = 2
D = b**2 - 4*a*c
if D < 0:
print("此方程无解")
if D == 0:
x = -b/(2*a)
x = math.fabs(x)
print("该方程只有一个实根:x=±",x)
else:
x1 = (-b+math.sqrt(D))/(2*a)
x2 = (-b-math.sqrt(D))/(2*a)
print("该方程的两根为:")
print("x1=",x1)
print("x2=",x2)
第三章 变量
一、变量介绍
1、变量是什么?
在编程中,你可以把变量想象成一个储物柜。这个储物柜可以存放各种东西,比如数字、文本、列表等等。每个储物柜都有一个唯一的名字,这个名字就是变量名。当你需要存放或者取出东西时,你只需要通过这个名字就可以找到对应的储物柜。
比如,你可以创建一个名为"age"的变量,然后把数字"20"存放进去。这就像你把一个标签"age"贴在储物柜上,然后把"20"这个物品放进去。当你需要知道"age"是多少时,你只需要查看这个储物柜就可以了。
在Python中,可以这样创建和使用变量:
age = 20 # 创建一个名为"age"的变量,并把数字"20"存放进去
print(age) # 打印出变量"age"的值,结果会显示"20"
2、初始化变量
初始化变量是编程中的一个术语,它指的是在变量被使用之前,给它赋予一个初始值的过程。这个初始值可以是一个数字、一个字符串、一个列表或者任何其他数据类型的值。
初始化变量的目的是确保变量在使用前有一个已知的值。这样可以避免程序中出现错误,因为如果尝试使用一个未初始化的变量,程序可能会遇到不可预测的行为或者直接崩溃。
在Python中,变量的初始化通常在声明变量的同时进行,就像这样:
number = 0 # 初始化变量number,赋予初始值0
name = "Alice" # 初始化变量name,赋予初始值"Alice"
在这个例子中,number和name都是在创建的同时被初始化了。number被初始化为整数0,而name被初始化为字符串"Alice"。这样,在后续的代码中就可以安全地使用这些变量了。
3、变量命名规范
在Python中,变量的命名需要遵循一些规则和约定。以下是一些主要的规则:
-
变量名只能包含字母、数字和下划线:变量名可以以字母或下划线开头,但不能以数字开头。例如,
variable1是有效的,但1variable是无效的。 -
变量名是区分大小写的:Python是区分大小写的语言,这意味着
Variable和variable会被视为两个不同的变量。 -
变量名不能是Python的关键字:Python有一些保留的关键字,如
for、if、else、and、or、not、is等,这些关键字不能用作变量名。 -
变量名应该是描述性的:虽然这不是强制的规则,但是为了代码的可读性,变量名应该是有意义的,能够描述变量的用途。例如,如果你有一个变量用来存储用户的年龄,那么
user_age就是一个好的变量名,而a或x就不是。 -
避免使用单字符变量名:除非在某些特定的情况下(如在小的循环中使用
i或j作为索引变量),否则应避免使用单字符的变量名。 -
使用下划线来分隔单词:在Python中,习惯上使用下划线来分隔变量名中的单词,这被称为蛇形命名法。例如,
user_name、my_variable等。 -
不要使用Python内置函数名作为变量名:Python有一些内置的函数名,如
print、type、list等,你应该避免使用这些作为你的变量名,否则会覆盖这些函数。
可以使用以下方式查看不能使用的变量:
import keyword
print(keyword.kwlist)
# ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
当我们编写代码时,我们的目标之一是使代码尽可能易于理解。这不仅对于别人阅读和理解你的代码很重要,也对于你自己在未来回顾代码时很有帮助。一个重要的方法是给变量选择有意义的名称。
变量的名称应该反映它所代表的数据的内容或用途。例如,如果你有一个变量用来存储用户的年龄,那么一个好的变量名可能是user_age,而不是a或x。这样,当你或其他人阅读代码时,就可以立即知道这个变量是用来存储用户的年龄的。
让我们看一些例子:
-
不好的命名:
s,sd,s1,str,string,a23等。这些名称没有提供关于变量用途的任何信息。 -
好的命名:
student_name,course_name,initial_temperature,max_height等。这些名称清楚地表明了变量的用途。
有一些额外的建议可以帮助你选择好的变量名:
-
避免使用过于通用的名字,如
data或info。虽然这些名字在技术上是有意义的,但它们并没有提供关于变量内容的具体信息。 -
避免使用可能引起混淆的缩写。如果缩写不是广为人知的,那么它可能会使代码更难理解。
-
当变量名需要由两个或更多的单词组成时,使用下划线(
_)来分隔单词,如student_name。这种命名方式在Python中非常常见,被称为蛇形命名法。 -
变量名应该尽可能短,但不应该牺牲清晰度。如果一个名字可以清楚地表达变量的用途,那么就没有必要使它更长。
总的来说,选择有意义的变量名是编写清晰、易于理解代码的重要部分。
二、变量的类型
在Python中,变量可以被视为数据的容器。你可以把它想象成一个盒子,你可以在里面放入不同类型的东西。这些“东西”就是数据,而数据的“类型”就是我们要讨论的变量类型。
Python是一种动态类型语言,这意味着你不需要事先声明变量的类型,解释器会在运行时自动推断出变量的类型。下面是一些最常见的变量类型:
- 整数(Integers) - 这是用于存储整数的变量类型,例如:1, 42, -5等。
age = 25 # 这里age是一个整数类型的变量
- 浮点数(Floats) - 用于存储带有小数点的数字,例如:3.14, -0.001等。
temperature = 36.6 # temperature是一个浮点数类型的变量
- 字符串(Strings) - 用于存储文本,你可以将其视为一系列字符。字符串需要用引号括起来,可以是单引号或双引号。
name = "Alice" # name是一个字符串类型的变量
- 布尔值(Booleans) - 这种类型只有两个值:True和False。它们通常用于条件和逻辑表达式。
is_student = True # is_student是一个布尔类型的变量
- 列表(Lists) - 列表是一种用于存储一系列有序项目的复合数据类型。列表中的项目可以是不同的数据类型。
fruits = ["apple", "banana", "cherry"] # fruits是一个列表类型的变量
- 元组(Tuples) - 元组与列表类似,但是一旦创建,元组中的项目就不能修改。
coordinates = (10.0, 20.0) # coordinates是一个元组类型的变量
- 字典(Dictionaries) - 字典用于存储键值对。每个键都是唯一的,并且映射到一个值。
person = {"name": "Bob", "age": 30} # person是一个字典类型的变量
- 集合(Sets) - 集合是一个无序的、不包含重复元素的集合。
unique_numbers = {1, 2, 3, 4, 5} # unique_numbers是一个集合类型的变量
在Python中,可以使用type()函数来查看任何变量的类型:
print(type(age)) # 将输出: <class 'int'>
理解变量的类型对于编程来说非常重要,因为不同的类型有不同的属性和方法。此外,某些操作只对特定类型的变量有效。例如,你可以将两个字符串相加(这在Python中称为连接),但是你不能将一个字符串和一个整数相加,因为它们是不同的类型。
三、类型转换
在Python中,变量就像是用来存储信息的容器。这些信息有不同的类型,比如数字、文本、列表等。有时候,我们需要将一种类型的数据转换成另一种类型,这就是类型转换。
类型转换分为两种:隐式类型转换和显式类型转换。
1、隐式类型转换
这种转换是自动发生的,不需要我们直接介入。例如,当你在整数和浮点数进行数学运算时,Python会自动将整数转换为浮点数,以避免信息丢失。
num_int = 123 # 这是一个整数
num_float = 1.23 # 这是一个浮点数
# 进行加法运算
new_num = num_int + num_float
# 输出结果及其类型
print(new_num) # 输出: 124.23
print(type(new_num)) # 输出: <class 'float'>
在上面的例子中,num_int是一个整数,num_float是一个浮点数。当它们相加时,num_int被隐式地转换为浮点数,以便与num_float相加,结果是一个浮点数。
2、显式类型转换
这种转换需要我们直接指定要转换的类型。Python提供了一些内置函数来帮助我们进行显式类型转换,如int(), float(), str(), list(), tuple(), set()等。
num_str = "456" # 这是一个字符串
# 将字符串转换为整数
num_int = int(num_str)
# 输出结果及其类型
print(num_int) # 输出: 456
print(type(num_int)) # 输出: <class 'int'>
在这个例子中,num_str是一个表示数字的字符串。我们使用int()函数将其显式转换为一个整数。
需要注意的是,并不是所有的类型转换都是可能的。例如,如果你尝试将一个包含非数字字符的字符串转换为整数,Python会抛出一个错误。
num_str = "abc"
num_int = int(num_str) # 这会引发ValueError: invalid literal for int() with base 10: 'abc'
在实际编程中,类型转换是非常常见的操作,它可以帮助我们解决不同类型数据之间的兼容性问题。掌握类型转换,可以让你在编写Python程序时更加得心应手。
3、数据类型转换
通常指的是将一个数据类型的值转换为另一个数据类型的值。
-
int():将一个值转换为整数。- 如果是浮点数,会去掉小数部分。
- 如果是字符串,字符串必须包含可转换为整数的文本(例如,不能有字母)。
- 也可以指定转换的进制。
print(int(2.8)) # 输出: 2 print(int('3')) # 输出: 3 print(int('100', 2)) # 输出: 4,这里'100'被当作二进制处理 -
float():将一个值转换为浮点数。- 如果是整数,会添加小数点和一个零。
- 如果是字符串,字符串必须包含可转换为浮点数的文本。
print(float(5)) # 输出: 5.0 print(float('3.14')) # 输出: 3.14 -
str():将一个值转换为字符串。- 几乎任何类型的值都可以转换为字符串。
print(str(10)) # 输出: '10' print(str(3.14)) # 输出: '3.14' print(str([1, 2, 3])) # 输出: '[1, 2, 3]' -
list():将一个序列转换为列表。- 可以将字符串、元组、集合等转换为列表。
print(list('hello')) # 输出: ['h', 'e', 'l', 'l', 'o'] print(list((1, 2, 3))) # 输出: [1, 2, 3] -
tuple():将一个序列转换为元组。- 可以将列表、字符串、集合等转换为元组。
print(tuple([1, 2, 3])) # 输出: (1, 2, 3) print(tuple('abc')) # 输出: ('a', 'b', 'c') -
set():将一个序列转换为集合。- 可以将列表、字符串、元组等转换为集合。
- 集合中的元素是唯一的,转换过程中会移除重复的元素。
print(set([1, 2, 2, 3])) # 输出: {1, 2, 3} print(set('banana')) # 输出: {'a', 'b', 'n'} -
dict():将包含键值对的序列转换为字典。- 可以将包含元组的列表转换为字典。
print(dict([('a', 1), ('b', 2)])) # 输出: {'a': 1, 'b': 2} -
bool():将一个值转换为布尔值。- 几乎任何类型的值都可以转换为布尔值。
- 一般来说,值为0、空序列或None会被转换为
False,其他值被转换为True。
print(bool(0)) # 输出: False print(bool("")) # 输出: False print(bool("Some text"))# 输出: True
在进行类型转换时,需要注意的是,不是所有的转换都是合法的或有意义的。如果尝试进行一个无效的转换,Python会抛出一个ValueError或TypeError异常。例如,尝试将包含字母的字符串转换为整数就会导致ValueError:
num_str = "abc"
num_int = int(num_str) # 这会引发ValueError
4、str与int和float之间的数据类型转换
- str转int和float:如果一个字符串是数值形式,我们可以使用内置的int()和float()函数将其转换为整数或浮点数。例如:
s = "123"
i = int(s) # i现在是整数123
f = float(s) # f现在是浮点数123.0
但是,如果字符串不是数值形式,这种转换将会引发ValueError。
- int和float转str:我们可以使用内置的str()函数将整数或浮点数转换为字符串。例如:
i = 123
s = str(i) # s现在是字符串"123"
f = 123.0
s = str(f) # s现在是字符串"123.0"
- int转float和float转int:我们可以使用内置的int()和float()函数来进行转换。但是要注意,从float转到int会丢失小数部分。例如:
i = 123
f = float(i) # f现在是浮点数123.0
f = 123.45
i = int(f) # i现在是整数123,小数部分.45被丢弃
5、其他数据类型转布尔类型
在Python中,几乎所有的对象都可以被转换为布尔值。Python提供了一个内置函数bool(),可以将其他类型的数据转换为布尔值。以下是一些常见的规则:
-
对于数字类型(
int、float、complex):- 数值为0(或0.0,或0j)的数字会被转换为
False。 - 所有非零的数字都会被转换为
True。
print(bool(0)) # 输出: False print(bool(1)) # 输出: True print(bool(-1)) # 输出: True print(bool(0.0)) # 输出: False print(bool(0.1)) # 输出: True print(bool(0j)) # 输出: False print(bool(1j)) # 输出: True - 数值为0(或0.0,或0j)的数字会被转换为
-
对于字符串类型(
str):- 空字符串会被转换为
False。 - 所有非空字符串都会被转换为
True。
print(bool('')) # 输出: False print(bool('Hello')) # 输出: True - 空字符串会被转换为
-
对于序列类型(
list、tuple、set、dict):- 空序列会被转换为
False。 - 所有非空序列都会被转换为
True。
print(bool([])) # 输出: False print(bool([1, 2, 3])) # 输出: True print(bool(())) # 输出: False print(bool((1, 2, 3))) # 输出: True print(bool({})) # 输出: False print(bool({1, 2, 3})) # 输出: True print(bool(dict())) # 输出: False print(bool({'a': 1})) # 输出: True - 空序列会被转换为
-
对于
None类型:None会被转换为False。
print(bool(None)) # 输出: False -
对于自定义对象:
- 如果对象实现了
__bool__()或__len__()方法,那么bool()会调用这些方法来获取布尔值。如果__bool__()返回True,或者__len__()返回的长度不为0,那么对象会被转换为True,否则会被转换为False。 - 如果对象没有实现
__bool__()或__len__()方法,那么它会被转换为True。
- 如果对象实现了
这些规则可以帮助你理解Python中的真值测试。在实际编程中,这些规则经常被用于if语句和其他需要布尔表达式的地方。
四、不常用的变量类型:复数
复数是Python中的一种内置数据类型,非常适合于处理工程、科学和数学问题。
在Python中,复数由两部分组成:实部和虚部。这两部分都是浮点数,虚部后面跟着一个"j"或"J"。例如:
x = 3.14 + 2.73j
在这个例子中,x是一个复数,它的实部是3.14,虚部是2.73。
你可以使用real和imag属性来获取复数的实部和虚部:
x = 3.14 + 2.73j
print(x.real) # 输出: 3.14
print(x.imag) # 输出: 2.73
Python还提供了一个内置函数complex(),可以用来创建复数。complex()函数接受两个参数:实部和虚部。如果只提供一个参数,那么这个参数将作为实部,虚部将为0:
x = complex(3.14, 2.73)
print(x) # 输出: (3.14+2.73j)
y = complex(3.14)
print(y) # 输出: (3.14+0j)
Python支持对复数进行各种数学运算,包括加法、减法、乘法和除法。例如:
x = 3.14 + 2.73j
y = 1.0 + 2.0j
print(x + y) # 输出: (4.14+4.73j)
print(x - y) # 输出: (2.14+0.73j)
print(x * y) # 输出: (-4.46+8.87j)
print(x / y) # 输出: (1.57+0.365j)
Python还提供了一个模块cmath,提供了一些处理复数的函数,如求平方根、对数、正弦、余弦等。
import cmath
x = 3.14 + 2.73j
print(cmath.sqrt(x)) # 输出: (1.8426149773176359+0.7400789501074124j)
在这个例子中,我们使用cmath.sqrt()函数计算了复数x的平方根。
除了基本的算术运算,Python的cmath模块提供了许多专门用于复数的数学函数。这些函数可以处理复数的各种数学问题,例如:
phase():返回复数的相位角(也称为辐角),这是复数在复平面上与正实轴之间的角度。polar():将复数转换为极坐标形式,即返回一个包含模(magnitude)和相位角(phase)的元组。rect():将极坐标形式转换为复数形式。
import cmath
# 创建一个复数
z = 3 + 4j
# 获取它的相位角
print(cmath.phase(z)) # 输出: 0.9272952180016122
# 将复数转换为极坐标形式
print(cmath.polar(z)) # 输出: (5.0, 0.9272952180016122)
# 从极坐标形式转换回复数
magnitude, phase = cmath.polar(z)
print(cmath.rect(magnitude, phase)) # 输出: (3+4j)
- 复数的共轭
复数的共轭是将虚部的符号反转得到的另一个复数。在Python中,可以使用复数对象的conjugate()方法来获取它的共轭:
z = 3 + 4j
print(z.conjugate()) # 输出: (3-4j)
- 复数的应用
复数在电子学、量子物理、信号处理等领域都有广泛的应用。例如,在交流电路分析中,复数可以用来表示交变电流和电压,使得计算功率和阻抗变得简单。在信号处理中,复数则用于表示振幅和相位,这对于分析和处理波形信号至关重要。
注意事项
在Python中使用复数时,需要注意以下几点:
- 不要使用
math模块中的函数来处理复数,因为math模块只适用于实数。应该使用cmath模块。 - 当打印复数时,Python会使用小括号将其括起来,以区分实部和虚部。
- 在进行复数运算时,如果涉及到实数和复数的混合,Python会自动将实数转换为复数的形式,例如将
2转换为2 + 0j
五、输入
input() 是 Python 中的一个内置函数,用于接收用户的文本输入。当 Python 执行到 input() 函数时,程序会暂停,等待用户输入一些文本,并按回车键。用户按下回车键后,input() 函数会读取输入的内容(作为字符串)并将其返回。这样,你就可以将用户的输入存储在一个变量中,以便之后在程序中使用。
input() 函数的基本用法:
# 无提示信息的输入
user_input = input()
print("你输入了:", user_input)
# 带有提示信息的输入
user_input = input("请输入一些文本: ")
print("你输入了:", user_input)
在第一个例子中,程序会等待用户输入,没有任何提示信息。在第二个例子中,input() 函数提供了一个字符串作为提示信息,这个提示信息会在用户输入之前显示。
input() 函数总是返回一个字符串,即使你输入的是数字。如果你需要将用户输入的内容转换为其他类型,比如整数或浮点数,你需要使用类型转换函数,如 int() 或 float()。例如:
# 获取整数输入
user_input = int(input("请输入一个整数: "))
print("你输入的整数是:", user_input)
# 获取浮点数输入
user_input = float(input("请输入一个浮点数: "))
print("你输入的浮点数是:", user_input)
在上面的例子中,如果用户输入的不是有效的整数或浮点数,程序将会抛出一个错误。因此,在实际应用中,你可能需要添加错误处理机制来确保用户输入的是有效的数据。
第四章 分支语句
一、比较运算符
-
==:这是一个等于运算符。如果两个操作数的值相等,则条件变为真。例如,5 == 5的结果是True。 -
!=:这是一个不等于运算符。如果两个操作数的值不相等,则条件变为真。例如,5 != 3的结果是True。 -
<:这是一个小于运算符。如果左操作数小于右操作数,则条件变为真。例如,3 < 5的结果是True。 -
>:这是一个大于运算符。如果左操作数大于右操作数,则条件变为真。例如,5 > 3的结果是True。 -
<=:这是一个小于或等于运算符。如果左操作数小于或等于右操作数,则条件变为真。例如,3 <= 5和5 <= 5的结果都是True。 -
>=:这是一个大于或等于运算符。如果左操作数大于或等于右操作数,则条件变为真。例如,5 >= 3和5 >= 5的结果都是True。
二、逻辑运算符
-
and运算符:and运算符用于检查两个条件是否都为真。只有当所有条件都为真时,整个表达式才为真。- 例如,如果我们有两个条件
condition1和condition2,表达式condition1 and condition2只有在两个条件都为真时才会返回True。 - 如果任何一个条件为假,整个表达式就会返回假(False)。
- 举例:
(5 > 3) and (2 < 4)会返回True,因为两个比较都是真的。
-
or运算符:or运算符用于检查至少一个条件是否为真。如果任何一个条件为真,整个表达式就为真。- 例如,
condition1 or condition2会返回True,如果condition1或condition2中至少有一个为真。 - 只有当所有条件都为假时,
or表达式才会返回假(False)。 - 举例:
(5 > 3) or (2 > 4)会返回True,因为至少有一个比较是真的(5确实大于3)。
-
not运算符:not运算符用于反转条件的布尔值。如果条件为真,则not运算符会返回假(False),反之亦然。- 它只需要一个操作数。例如,
not condition1会返回False,如果condition1为真。 not运算符经常用于构建否定条件。- 举例:
not (5 > 3)会返回False,因为5 > 3是真的,not运算符将其反转为假。
三、条件语句
在Python中,我们使用if,else和elif关键字来创建条件语句,这些语句允许我们根据特定条件执行代码。下面是每个关键字的详细说明:
-
if语句:if语句是最基本的条件语句。它后面跟着一个条件,如果该条件为真(True),则执行if语句块中的代码。- 例如:
在这个例子中,因为5确实大于3,所以"5 is greater than 3"会被打印出来。if 5 > 3: print("5 is greater than 3")
-
else语句:else语句在if语句的条件为假(False)时执行。它不需要条件,只是在所有前面的条件都不满足时执行。- 例如:
在这个例子中,因为5并不小于3,所以"5 is not less than 3"会被打印出来。if 5 < 3: print("5 is less than 3") else: print("5 is not less than 3")
-
elif语句:elif是"else if"的缩写。它允许我们在前面的if或elif语句的条件为假(False)时检查一个新的条件。elif语句必须在if语句之后,但可以有多个elif语句。一旦找到一个为真(True)的条件,就会执行该条件下的代码块,然后跳过所有剩余的elif和else。- 例如:
在这个例子中,因为5并不小于3,所以if 5 < 3: print("5 is less than 3") elif 5 == 5: print("5 is equal to 5") else: print("5 is greater than 3")if语句的条件为假。然后,程序检查elif语句的条件,发现5确实等于5,所以"5 is equal to 5"会被打印出来。然后,程序跳过了else语句。
这些条件语句是控制Python程序流程的重要工具,理解它们的工作原理对于编写有效的代码至关重要。
四、条件语句嵌套
条件语句的嵌套是指在一个条件语句内部再包含另一个条件语句。这种结构允许我们根据多个层次的条件来执行不同的代码块。嵌套条件语句可以是if内部嵌套if,if内部嵌套elif,或者任何类似的组合。下面是一些例子来详细说明嵌套条件语句的使用:
- 示例 :简单的嵌套
if
age = 20
has_license = True
if age >= 18:
if has_license:
print("You are allowed to drive.")
else:
print("You are not allowed to drive without a license.")
else:
print("You are too young to drive.")
在这个例子中,我们首先检查一个人的年龄是否大于或等于18岁。如果这个条件为真,我们进入第一个if语句的代码块,然后在这个代码块内部,我们进行另一个检查,看这个人是否有驾驶执照。根据是否有驾驶执照,会打印出不同的信息。
- 示例 :
if-elif-else嵌套
score = 85
if score >= 60:
if score >= 70:
if score >= 80:
if score >= 90:
print("Grade: A")
else:
print("Grade: B")
else:
print("Grade: C")
else:
print("Grade: D")
else:
print("Grade: F")
在这个例子中,我们根据分数来判断等级。这是一个多层嵌套的例子,其中每个等级的判断都是基于前一个条件成立的情况下进行的。这种方式虽然可以工作,但并不是最优的写法,因为它使得代码难以阅读和维护。通常,我们会使用elif来避免这种深层嵌套。
- 示例 :使用
elif改进的分数等级判断
score = 85
if score >= 90:
print("Grade: A")
elif score >= 80:
print("Grade: B")
elif score >= 70:
print("Grade: C")
elif score >= 60:
print("Grade: D")
else:
print("Grade: F")
这个例子展示了如何使用elif来避免不必要的嵌套。每个elif都是在前一个条件不满足时才会被检查的,这使得代码更加清晰和简洁。
- 示例 :复杂的嵌套条件
username = "admin"
password = "secret"
if username == "admin":
if password == "secret":
print("Welcome, admin!")
else:
print("Incorrect password for admin.")
else:
if password == "secret":
print("Welcome, user!")
else:
print("Incorrect username and password.")
在这个例子中,我们检查用户名和密码。首先检查用户名是否是"admin",如果是,再检查密码是否正确。如果用户名不是"admin",则不管密码是否正确,都不会给予管理员权限。这个例子展示了如何根据多个条件来控制程序的流程。
嵌套条件语句是在一个条件语句内部使用另一个条件语句。这可以让我们根据更复杂的逻辑来执行不同的代码块。结合逻辑运算符和比较运算符,我们可以创建更加详细和精细控制的条件。下面是一些例子,展示了如何在实践中使用嵌套条件语句:
- 示例 :用户登录验证
username = input("Enter username: ")
password = input("Enter password: ")
if username == "user1":
if password == "password123":
print("Login successful!")
else:
print("Invalid password!")
else:
print("Invalid username!")
在这个例子中,我们首先检查用户名是否正确。如果用户名正确,我们再检查密码是否正确。如果用户名不正确,我们不再检查密码,直接输出无效用户名。
- 示例 :分数等级判断与评语
score = 75
if score >= 90:
print("Grade: A")
if score >= 95:
print("Excellent performance!")
else:
print("Good job, but you can do better.")
elif score >= 80:
print("Grade: B")
print("You've done well, but there's room for improvement.")
elif score >= 70:
print("Grade: C")
print("Decent effort, but you should aim higher.")
elif score >= 60:
print("Grade: D")
print("You passed, but consider studying more.")
else:
print("Grade: F")
print("You failed. It's important to work harder next time.")
在这个例子中,我们不仅根据分数给出等级,还根据分数的范围给出相应的评语。注意,我们在A等级的情况下使用了嵌套的if语句来给出更具体的反馈。
- 示例 :电影票价计算
age = 35
member = True
if age < 18:
print("Ticket price: $8")
elif age >= 60:
print("Ticket price: $10")
else:
if member:
print("Ticket price: $12")
else:
print("Ticket price: $15")
在这个例子中,我们根据年龄和会员状态来决定电影票的价格。首先,我们检查年龄是否符合特定的折扣条件。如果不符合,我们再检查是否为会员,以决定最终的票价。
- 示例 :多条件组合判断
temperature = 30
weather = "Sunny"
wind_speed = 15
if temperature > 25:
if weather == "Sunny":
if wind_speed < 20:
print("It's a great day for a picnic!")
else:
print("It's too windy for a picnic.")
else:
print("It might not be a good idea for a picnic due to the weather.")
else:
print("It's too cold for a picnic.")
在这个例子中,我们根据温度、天气和风速来决定是否是野餐的好天气。我们首先检查温度,然后是天气,最后是风速。每个条件都是基于前一个条件为真的情况下进行的。
- 示例:餐厅折扣政策
total_bill = 250
is_weekday = True
number_of_diners = 4
# 周一至周五提供团体折扣
if is_weekday:
if number_of_diners >= 4 and total_bill >= 200:
discount = 0.1 # 10% discount
elif number_of_diners >= 2 and total_bill >= 100:
discount = 0.05 # 5% discount
else:
discount = 0
else:
discount = 0
final_bill = total_bill - (total_bill * discount)
print(f"Final bill amount: ${final_bill:.2f}")
在这个例子中,我们首先检查是否是工作日。如果是,并且顾客人数大于等于4且账单总额大于等于200,他们将获得10%的折扣。如果人数大于等于2且账单总额大于等于100,他们将获得5%的折扣。如果不是工作日,则没有折扣。
- 示例:年龄和身高限制的游乐设施
age = 12
height_cm = 130
if age >= 10 and height_cm >= 120:
if age >= 16 or (age >= 12 and height_cm >= 150):
print("Allowed on all rides.")
else:
print("Allowed on intermediate rides.")
else:
print("Allowed on children's rides only.")
在这个例子中,我们首先检查年龄是否大于等于10岁且身高大于等于120厘米。如果满足这些条件,我们再检查是否满足所有游乐设施的年龄和身高限制。如果不满足,但满足了中级游乐设施的条件,那么就只允许玩中级游乐设施。否则,只允许玩儿童游乐设施。
- 示例:智能家居温度控制
temperature = 22
weather_outside = "cold"
is_night = True
if weather_outside == "cold":
if is_night and temperature < 20:
print("Heating on: Increasing temperature for nighttime comfort.")
elif not is_night and temperature < 18:
print("Heating on: Increasing temperature for daytime warmth.")
else:
print("Heating off: Comfortable temperature maintained.")
else:
print("Heating off: No need for heating in warm weather.")
在这个例子中,我们根据室外天气、是否是夜晚以及室内温度来控制加热。如果天气寒冷,我们会根据是白天还是晚上以及当前温度来决定是否需要加热。
- 示例:信用评分和贷款利率
credit_score = 720
loan_amount = 50000
if credit_score > 700:
if loan_amount > 50000:
interest_rate = 0.05 # 5% interest
else:
interest_rate = 0.04 # 4% interest
else:
if loan_amount > 50000:
interest_rate = 0.06 # 6% interest
else:
interest_rate = 0.05 # 5% interest
print(f"Interest rate for the loan is: {interest_rate*100}%")
在这个例子中,我们根据信用评分和贷款金额来决定利率。信用评分高于700的借款人获得较低的利率,而贷款金额超过50000的借款人则获得较高的利率。
这些例子展示了如何使用比较和逻辑运算符来创建复杂的条件语句,以及如何通过嵌套条件语句来实现更精细的逻辑控制。在编写这样的条件语句时,应该注意保持代码的清晰和可读性,以便其他开发者(或未来的你)能够轻松理解和维护代码。
练习
练习:电影票价格计算
背景:某电影院根据观众的年龄和会员身份给予不同的票价折扣,票价规则如下:
12岁以下的儿童票价为50元,会员价为40元。
12岁至18岁的青少年票价为80元,会员价为60元。
18岁以上为成人,票价为100元,会员价为80元。
要求:编写一个程序,用户输入年龄和是否为会员(是/否),然后输出应付的电影票价格。
提示:先判断年龄段,再在每个年龄段内判断是否为会员。使用嵌套的if语句完成此题。
age = int(input("请输入年龄:"))
svip = input("您是否为会员(是/否):")
if age < 12:
if svip == "是":
print("电影票价为:40元")
else:
print("电影票价为:50元")
# if 12 <= age < 18:
if age >= 12 and age < 18:
if svip == "是":
print("电影票价为:60元")
else:
print("电影票价为:80元")
if age >= 18:
if svip == "是":
print("电影票价为:80元")
else:
print("电影票价为:100元")
练习:贷款利率计算
背景:某银行为鼓励长期存款,提供了不同的存款期限和金额对应的利率,利率规则如下:
存款小于10000元:
1年期:3.5%
3年期:4.0%
5年期以上:4.5%
存款金额在10000~50000之间:
1年期:4.0%
3年期:4.5%
5年期以上:5.0%
存款金额超过50000元:
1年期:4.5%
3年期:5.0%
5年期以上:5.5%
要求:编写一个程序,用户输入存款金额和存款期限(年),然后输出对应的年利率
提示:先判断存款金额范围,再在灭个金额范围内判断存款期限。
# 方法1:
money = float(input("请输入存款:"))
dur = int(input("请输入存款年限(年):"))
if dur == 1 or dur == 3 or dur == 5 or dur > 5:
dur = dur
else:
print("输入不合法请重新输入!")
if money < 10000:
if dur == 1:
print("年利率:3.5%")
if dur == 3:
print("年利率:4.0%")
if dur >= 5:
print("年利率:4.5%")
if 10000 <= money < 50000:
if dur == 1:
print("年利率:4.0%")
if dur == 3:
print("年利率:4.5%")
if dur >= 5:
print("年利率:5.0%")
if money >= 50000:
if dur == 1:
print("年利率:4.5%")
if dur == 3:
print("年利率:5.0%")
if dur >= 5:
print("年利率:5.5%")
# 方法2:
money = float(input("请输入存款:"))
dur = int(input("请输入存款年限(年):"))
if money < 10000:
if dur == 1:
rt=3.5
if dur == 3:
rt=4.0
if dur >= 5:
rt=4.5
else:
rt = None
if 10000 <= money < 50000:
if dur == 1:
rt=4.0
if dur == 3:
rt=4.5
if dur >= 5:
rt=5.0
else:
rt = None
if money >= 50000:
if dur == 1:
rt=4.5
if dur == 3:
rt=5.0
if dur >= 5:
rt=5.5
else:
rt = None
if rt:
print(f"年利率为:{rt}%")
else:
print("输入不合法!请重新输入!")
题目:判断大小并输出
要求:用户输入两个数字,对数字进行排序,从小到大输出。并且不使用任何函数。
a = int(input("请输入第1个数字:"))
b = int(input("请输入第2个数字:"))
if a > b:
t = a
a = b
b = t
print(a,b)
题目:判断大小并输出
要求:用户输入3个数字,对数字进行排序,从小到大输出。并且不使用任何函数。
a = int(input("请输入第1个数字:"))
b = int(input("请输入第2个数字:"))
c = int(input("请输入第3个数字:"))
if a > b:
t = a
a = b
b = t
if a > c:
t = a
a = c
c = t
if b > c:
t = b
b = c
c = t
print(a,b,c)
题目:机票价格优化计算
背景:某航空公司为了吸引更多的乘客,提供了复杂的机票价格优惠策略。价格规则如下:
基础价格根据飞行距离计算:每公里0.5元。
如果乘客是VIP,整体享受9折优惠。
如果乘客购票时间距离起飞时间超过3个月,可以享受95折优惠。
周末购票的乘客可享受98折优惠。
如果乘客是学生,并且购票时间是学期间(9月1日至次年7月1日),可以享受92折优惠。
注意:
所有的折扣是可以叠加的。
输入的日期格式为:YYYY-MM-DD
要求:
编写一个程序,用户输入飞行距离、是否为VIP、购票日期、飞行时间、是否为学生,然后输出应付的机票价格。
提示:
使用多个if语句进行折扣的叠加计算。
注意日期的处理,可以使用Python的
datetime模块
1、导入模块
from datetime import datetime
2、获取用户输入
distance = int(input("请输入飞行距离(公里):"))
is_vip = input("是我否为vip(是/否):")
purchase_date = input("请输入购票日期(格式:YYYY-MM-DD):")
flight_date = input("起飞日期(格式:YYYY-MM-DD):")
is_student = input("学生?(是/否)")
3、计算基础价格
base_price = distance * 0.5
4、初始化折扣
discount = 1
5、VIP折扣
if is_vip == "是":
discount = discount * 0.9
6、提前购票的折扣
purchase_date = datetime.strptime(purchase_date,"%Y-%m-%d")
flight_date = datetime.strptime(flight_date,"%Y-%m-%d")
if (flight_date-purchase_date).days > 90:
discount = discount * 0.95
7、计算周末折扣
if purchase_date.weekday() >= 5:
discount = discount * 0.98
8、学生折扣
if is_student == "是" and 9 <= purchase_date.mount <= 7:
discount = discount * 0.92
9、计算最终价格
final_price = base_price * discount
print(f"应付机票价格为{final_price}")
完整代码:
from datetime import datetime
distance = int(input("请输入飞行距离(公里):"))
is_vip = input("是我否为vip(是/否):")
purchase_date = input("请输入购票日期(格式:YYYY-MM-DD):")
flight_date = input("起飞日期(格式:YYYY-MM-DD):")
is_student = input("学生?(是/否)")
base_price = distance * 0.5
discount = 1
if is_vip == "是":
discount = discount * 0.9
purchase_date = datetime.strptime(purchase_date,"%Y-%m-%d")
flight_date = datetime.strptime(flight_date,"%Y-%m-%d")
if (flight_date-purchase_date).days > 90:
discount = discount * 0.95
if purchase_date.weekday() >= 5:
discount = discount * 0.98
if is_student == "是" and 9 <= purchase_date.month <= 7:
discount = discount * 0.92
final_price = base_price * discount
print(f"应付机票价格为{final_price}")
用户输入一个整数。然后,程序会输出该整数是
正数还是负数,并且还要判断用户输入的数字是偶数还是奇数。
# 获取用户输入
number_input=int(input("请输入一个整数:"))
# 判断正负数
if number_input > 0:
print("这玩意是个正数")
elif number_input == 0:
print("输入的是:0")
else:
print("这玩意是个负数")
# 判断奇偶性
if number_input%2 == 0:
print(f"{number_input}是偶数")
else:
print(f"{number_input}是奇数")
用户输入三个整数,判断其中数值最大的数。
a=int(input("请输入第1个整数:"))
b=int(input("请输入第2个整数:"))
c=int(input("请输入第3个整数:"))
if a > b and a>c:
t=a
elif b > a and b > c:
t=b
else:
t=c
print("三个数的最大值为:",t)
用户输入2个整数,从大到小输出。
a=int(input("请输入第1个整数:"))
b=int(input("请输入第2个整数:"))
if a>b:
print(a,b)
if b>a:
print(b,a)
用户输入3个整数,从大到小输出。
a=int(input("请输入第1个整数:"))
b=int(input("请输入第2个整数:"))
c=int(input("请输入第3个整数:"))
if a<b:
t=a
a=b
b=t
if a<c:
t=a
a=c
c=t
if b<c:
t=b
b=c
c=t
print(a,b,c)
会员折扣系统
编写一个Python程序,模拟一个简单的会员折扣系统,该系统根据用户的会员等级和购买金额来计算最终的支付金额。
要求如下:
1、用户输入他们的会员等级(非会员、银卡会员、金卡会员、钻石会员)和购买金额。
2、非会员没有折扣
3、银卡会员购买金额超过100元享受10%的折扣。
4、金卡会员购买金额超过200元享受15%的折扣。
5、钻石会员购买金额超过300元享受20%的折扣。
6、如果输入的会员等级无效,程序应提示用户输入无效
7、请输出最终的金额
print('''
会员等级有:非会员、银卡会员、金卡会员、钻石会员
''')
level=input("请输入您的会员等级:")
amount=float(input("请输入购买金额:"))
if level == "非会员":
final_amount=amount
elif level == "银卡会员":
if amount > 100:
final_amount=amount*0.9
else:
final_amount=amount
elif level == "金卡会员":
if amount > 200:
final_amount=amount*0.85
else:
final_amount=amount
elif level == "钻石会员":
if amount > 300:
final_amount=amount*0.8
else:
final_amount=amount
else:
print("您输入的会员等级无效!")
final_amount=None
print("您最终需支付金额为:", final_amount)
第五章 循环语句
循环语句是编程中非常重要的一个概念。在Python编程中,我们主要使用两种类型的循环语句:for循环和while循环。
一、While语句
while语句在Python中用于执行一个代码块,只要给定的条件为真(True)。它的基本结构如下:
while 条件表达式:
# 执行代码块
只要条件表达式的结果为True,while循环就会继续执行其中的代码块。一旦条件表达式为False,循环就会结束。这里需要特别注意的是,如果条件始终为True,那么while循环将变成一个无限循环,可能导致程序无法正常继续执行。
- 例:计数器
# 初始化计数器
counter = 0
# 当计数器小于3时,执行循环体
while counter < 3:
print("Counter is:", counter)
# 更新计数器
counter += 1
输出:
Counter is: 0
Counter is: 1
Counter is: 2
- 例:使用
while循环读取列表
# 定义一个列表
my_list = [1, 2, 3, 4, 5]
index = 0
# 当索引小于列表长度时,执行循环体
while index < len(my_list):
print(my_list[index])
index += 1
输出:
1
2
3
4
5
- 例:无限循环
# 无限循环
while True:
response = input("输入'exit'来结束循环: ")
if response == 'exit':
break # 如果输入的是'exit',则使用break语句退出循环
print("你输入了: " + response)
这个例子中,while True创建了一个无限循环,它会一直运行,直到用户输入’exit’,break语句被执行,从而退出循环。
- 例:
while循环与else子句
# 初始化计数器
counter = 0
# 当计数器小于3时,执行循环体
while counter < 3:
print("Counter is:", counter)
counter += 1
else:
print("循环结束,counter的值是:", counter)
输出:
Counter is: 0
Counter is: 1
Counter is: 2
循环结束,counter的值是: 3
在这个例子中,else子句在while循环正常结束后执行,即没有遇到break语句跳出循环时。如果循环因为break而终止,else子句不会执行。
这些例子展示了while循环在不同情况下的使用方法,包括基本的计数器逻辑、处理集合元素、处理无限循环以及与else子句结合使用的情况。在实际编程中,while循环非常有用,尤其是在你不知道需要执行多少次循环时。
while语句嵌套
在Python中,while语句嵌套指的是在一个while循环内部再放置一个或多个while循环。这种结构通常用于处理多层次的循环任务,比如在二维数据结构中进行操作,或者当需要在一个循环内部根据条件执行另一个循环时。
嵌套while循环的基本结构如下:
while 外层条件:
# 外层循环的代码块
while 内层条件:
# 内层循环的代码块
在这种结构中,每次外层循环执行时,内层循环都会完整地执行直到其条件不再满足。一旦内层循环结束,外层循环才会继续到下一个迭代。
- 例:打印乘法表
# 初始化外层循环的计数器
i = 1
# 外层循环控制乘法表的行
while i <= 5:
# 初始化内层循环的计数器
j = 1
# 内层循环控制乘法表的列
while j <= 5:
print(f"{i} * {j} = {i * j}", end='\t') # 打印乘法表的一个条目
j += 1
print() # 每完成一行后换行
i += 1
输出:
1 * 1 = 1 1 * 2 = 2 1 * 3 = 3 1 * 4 = 4 1 * 5 = 5
2 * 1 = 2 2 * 2 = 4 2 * 3 = 6 2 * 4 = 8 2 * 5 = 10
3 * 1 = 3 3 * 2 = 6 3 * 3 = 9 3 * 4 = 12 3 * 5 = 15
4 * 1 = 4 4 * 2 = 8 4 * 3 = 12 4 * 4 = 16 4 * 5 = 20
5 * 1 = 5 5 * 2 = 10 5 * 3 = 15 5 * 4 = 20 5 * 5 = 25
- 例:查找第一个同时满足两个条件的数
# 外层循环的计数器
i = 1
# 外层循环条件
while i <= 100:
# 内层循环的计数器
j = 1
# 内层循环条件
while j <= 100:
# 检查条件是否满足
if i % 2 == 0 and j % 3 == 0:
print(f"第一个既能被2整除又能被3整除的数对是: ({i}, {j})")
break # 找到后退出内层循环
j += 1
if i % 2 == 0 and j % 3 == 0:
break # 找到后退出外层循环
i += 1
输出:
第一个既能被2整除又能被3整除的数对是: (2, 3)
- 例:创建一个简单的登录系统
# 假设的用户名和密码
correct_username = "user1"
correct_password = "pass123"
# 最大尝试次数
max_attempts = 3
attempts = 0
# 外层循环控制尝试次数
while attempts < max_attempts:
# 获取用户输入
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 验证用户名和密码
if username == correct_username and password == correct_password:
print("登录成功!")
break
else:
print("用户名或密码错误,请再次尝试。")
attempts += 1
# 内层循环提供额外的信息
while True:
continue_login = input("是否继续尝试?(y/n): ")
if continue_login.lower() == 'n':
print("退出登录尝试。")
attempts = max_attempts # 设置尝试次数达到上限以退出外层循环
break
elif continue_login.lower() == 'y':
break
else:
print("无效的输入,请输入'y'或'n'。")
在这些例子中,我们可以看到while嵌套循环如何在不同的情况下使用,以及如何通过内层循环和外层循环的相互作用来实现复杂的逻辑。嵌套循环是一个强大的工具,但也需要小心使用,以避免造成程序的不必要复杂性和性能问题。
二、for语句
在Python中,for语句是一种迭代器,用于遍历序列(如列表、元组、字符串)或其他可迭代对象。for循环允许你对序列中的每个元素执行代码块。
for循环的基本语法如下:
for 变量 in 可迭代对象:
# 执行代码块
在这里,可迭代对象可以是任何Python中的序列或者任何迭代器。每次循环时,变量会被赋值为序列中的一个元素,然后执行代码块。
- 例:遍历列表
# 定义一个列表
fruits = ["apple", "banana", "cherry"]
# 遍历列表
for fruit in fruits:
print(fruit)
输出:
apple
banana
cherry
- 例:使用
range()函数
range()函数常用于生成一个整数序列,经常与for循环一起使用。
# 使用range()生成一个从0到4的数字序列
for i in range(5):
print(i)
输出:
0
1
2
3
4
- 例:遍历字符串
# 定义一个字符串
for char in "Hello":
print(char)
输出:
H
e
l
l
o
- 例:遍历字典
当遍历字典时,默认情况下是遍历字典的键。你也可以遍历字典的值或者键-值对。
# 定义一个字典
capitals = {"France": "Paris", "Germany": "Berlin", "Italy": "Rome"}
# 遍历字典的键
for country in capitals:
print(country)
# 遍历字典的值
for city in capitals.values():
print(city)
# 遍历字典的键-值对
for country, city in capitals.items():
print(f"The capital of {country} is {city}")
输出:
France
Germany
Italy
Paris
Berlin
Rome
The capital of France is Paris
The capital of Germany is Berlin
The capital of Italy is Rome
range()方法
在Python中,range()函数是一个内置函数,用于生成一个不可变的数字序列。这个函数通常用于循环来重复执行某个操作特定的次数。
range()函数有三种形式:
range(stop): 生成一个从0开始,到stop - 1的整数序列。range(start, stop): 生成一个从start开始,到stop - 1的整数序列。range(start, stop, step): 生成一个从start开始,到stop - 1的整数序列,其中每个数字间的间隔为step。
注意,range()函数生成的序列包含起始值,但不包含结束值。
- 例:使用一个参数
# 使用一个参数(只指定结束值)
for i in range(5):
print(i)
输出:
0
1
2
3
4
- 例:使用两个参数
# 使用两个参数(指定开始值和结束值)
for i in range(5, 10):
print(i)
输出:
5
6
7
8
9
- 例:使用三个参数
# 使用三个参数(指定开始值、结束值和步长)
for i in range(0, 10, 2):
print(i)
输出:
0
2
4
6
8
- 例:生成一个递减的序列
# 使用三个参数生成一个递减的序列
for i in range(10, 0, -2):
print(i)
输出:
10
8
6
4
2
range()函数在Python中非常常用,尤其是在需要进行特定次数的循环操作时。虽然range()生成的是一个不可变序列,但实际上它并不会一次性生成所有的元素,而是在每次迭代时生成下一个元素,这样可以节省内存,特别是在处理大规模数据时。
for语句嵌套
首先,让我们理解什么是for循环。在Python中,for循环用于遍历任何序列(例如列表或字符串),或者其他可迭代对象。
基本的for循环语法如下:
for variable in sequence:
# 执行的代码块
在这里,"variable"是变量,用于存储当前序列中的元素。"sequence"是你想要遍历的序列或可迭代对象。
现在,让我们来看看什么是嵌套for循环。嵌套for循环就是在一个for循环内部再放置一个或多个for循环。这在处理二维数据结构(如列表的列表)或需要多重迭代的情况下非常有用。
嵌套for循环的基本语法如下:
for variable1 in sequence1:
for variable2 in sequence2:
# 执行的代码块
在这里,外部循环遍历"sequence1",而对于外部循环的每次迭代,内部循环都会遍历整个"sequence2"。
例:打印二维列表的所有元素
# 二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 嵌套for循环
for row in matrix:
for element in row:
print(element)
在这个例子中,外部循环遍历列表的每一行,而内部循环则遍历每一行的每个元素。
例:打印乘法表
# 打印乘法表
for i in range(1, 10):
for j in range(1, i+1):
print(f"{j}x{i}={i*j}", end="\t")
print()
在这个例子中,外部循环控制乘法表的行数,内部循环控制每行的列数。end="\t"用于在每次打印后添加一个制表符,使输出更整齐。
三、break和continue
break语句用于在其所在的最内层循环中提前结束循环。当执行到break语句时,循环将立即结束,程序将继续执行循环后面的代码。
for i in range(10):
if i == 5:
break
print(i)
在这个例子中,当i等于5时,break语句将被执行,循环将立即结束。所以,这段代码将只打印出0到4。
continue语句用于在其所在的最内层循环中跳过当前的这一次循环,直接开始下一次循环。当执行到continue语句时,当前的这一次循环将立即结束,程序将继续执行下一次循环。
for i in range(10):
if i == 5:
continue
print(i)
在这个例子中,当i等于5时,continue语句将被执行,当前的这一次循环将立即结束,程序将继续执行下一次循环。所以,这段代码将打印出0到4和6到9,但不包括5。
例:搜索列表中的特定元素,并在找到后退出循环
# 给定一个数字列表和一个目标值
numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
target = 6
# 使用for循环遍历列表
for number in numbers:
if number == target:
print(f"Found the target number {target}!")
break # 找到目标,退出循环
else:
continue # 没找到,继续下一个迭代
在这个例子中,我们遍历列表,寻找目标数字。一旦找到,我们打印一条消息并使用break退出循环。如果当前数字不是目标数字,continue将被执行,但在这种情况下它不是必需的,因为它是循环的最后一条语句。
例:跳过偶数,打印第一个大于10的奇数后退出循环
# 使用while循环
i = 0
while i < 20:
i += 1
if i % 2 == 0:
continue # 跳过偶数
if i > 10:
print(f"The first odd number greater than 10 is {i}.")
break # 找到符合条件的数,退出循环
在这个例子中,我们使用while循环从1数到20。如果当前数字是偶数,我们使用continue跳过它。如果当前数字是一个大于10的奇数,我们打印一条消息并使用break退出循环。
例:在二维数据结构中查找元素,找到后退出所有循环
# 二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
target = 8
found = False
# 嵌套for循环
for row in matrix:
for element in row:
if element == target:
print(f"Found the target number {target}!")
found = True
break # 找到目标,退出内层循环
if found:
break # 找到目标,退出外层循环
在这个例子中,我们使用嵌套的for循环来遍历二维列表。当找到目标元素时,我们打印一条消息并设置found变量为True,然后使用break退出内层循环。外层循环检查found变量,如果为True,也使用break退出。
练习
练习:计算1+2+3+……+100的和
a = 1
sum = 0
while a <= 100:
sum = sum + a
a = a + 1
print(sum)
练习:计算2+4+6+……+100的和
a = 2
sum = 0
while a <= 100:
sum = sum + a
a = a + 2
print(sum)
练习:打印1~100所有的奇数,数与数之间用空格隔开
a=0
while a<100:
a=a+1
if a%2 != 0:
print(a, end=" ")
练习:计算
1*2*3*……*8*9*10
a=1
sum=1
while a <= 10:
sum=sum*a
a=a+1
print(sum)
计算阶乘(例如:
5!=5*4*3*2*1)要求:用户输入一个不大于10(包括10)的数,数字的范围必须是1~10,否则重新输入,如果输入不符合要求超过三次(包括三次),则自动退出程序,要求要判断输入的数据类型
c=0
while True:
c=c+1
if c>3:
print("多次输入无效!程序关闭!")
break
num=input("请在1~10中选择一个数字输入:")
if num.isdigit() == False:
print("输入不合法")
continue
else:
num=int(num)
if num<1:
print("输入不合法")
continue
if num>10:
print("输入不合法")
continue
s=1
for i in range(1,num+1):
s=s*i
print(s)
break
第六章 列表
列表是Python中一种非常灵活的数据结构,它可以存储序列化的数据项集合。在Python中,列表使用方括号[]表示。其中的元素可以是不同的数据类型,包括数字、字符串甚至是其他列表。
一、基本操作
1、创建列表
a=[1,2,3,4]
2、访问列表
print(a[0])
3、修改列表中的元素
a=[1,2,3,4]
a[0]=100
print(a)
4、列表切片
允许访问列表的一部分,可以指定起始和结束以及步长
number=[0,1,2,3,4,5,6,7,8,9]
# 取第2~5个元素
print(number[2:6])
# 在第2~7个元素中,每隔一个取1个元素
print(number[2:8:2])
5、列表的循环
可以使用 for循环来遍历列表中的每一个元素
number=[0,1,2,3,4,5,6,7,8,9]
for i in number:
print(i)
6、列表推导式
一种简洁的构建列表的方法。
s=[i for i in range(11)]
print(s)
二、添加元素
1、在末尾使用 append()方法插入元素
a=[1,2,3,4]
print(a)
a.append(100)
print(a)
2、使用 insert()方法在指定位置插入一个元素
a=[1,2,3,4]
print(a)
a.insert(1,666)
print(a)
3、使用extend()函数。
在Python中,extend()函数用于将一个可迭代对象的元素添加到列表的末尾。可迭代对象可以是列表、元组、字符串或其他可迭代的数据类型。
下面是extend()函数的使用示例:
my_list = [1, 2, 3]
another_list = [4, 5, 6]
my_list.extend(another_list)
print(my_list)
输出结果:
[1, 2, 3, 4, 5, 6]
在这个例子中,extend()函数将another_list中的元素添加到my_list的末尾,使得my_list包含了两个列表的所有元素。
需要注意的是,extend()函数会修改原始的列表,而不会创建一个新的列表。如果你想要创建一个新的列表,可以使用+运算符来连接两个列表。
三、删除元素
1、使用 remove()方法删除指定的列表项
a=[1,2,666,3,4]
print(a)
a.remove(666)
print(a)
2、使用 pop()方法删除指定列表项(默认是最后一个)
a=[1,2,666,3,4,520]
print(a)
a.pop(2)
print(a)
a.pop()
print(a)
3、使用del()
del是Python中的一个关键字,用于删除对象。它可以用来删除列表中的元素、删除变量以及删除对象的属性。
对于列表来说,del关键字可以用来删除列表中的一个或多个元素,或者删除整个列表。
删除列表中的一个元素:
my_list = [1, 2, 3, 4, 5]
del my_list[2]
print(my_list)
输出结果:
[1, 2, 4, 5]
在这个例子中,del my_list[2]将删除列表my_list中索引为2的元素。
删除整个列表:
my_list = [1, 2, 3, 4, 5]
del my_list
在这个例子中,del my_list将删除整个列表my_list。
除了用于删除列表元素,del关键字还可以用于删除变量和对象的属性。例如:
x = 10
del x # 删除变量x
class MyClass:
def __init__(self):
self.my_attribute = 42
obj = MyClass()
del obj.my_attribute # 删除对象obj的属性my_attribute
需要注意的是,del关键字只是删除了对象的引用,而不是真正地销毁对象。Python的垃圾回收机制会负责在适当的时候销毁不再被引用的对象。
四、列表查找
1、通过索引查找:
可以使用索引来直接访问列表中的元素。索引从0开始,可以使用方括号([])来获取指定位置的元素。
my_list = [1, 2, 3, 4, 5]
print(my_list[2]) # 输出: 3
2、使用in关键字进行成员检查:
可以使用in关键字来检查一个元素是否存在于列表中。如果存在,返回True;否则,返回False。
my_list = [1, 2, 3, 4, 5]
print(3 in my_list) # 输出: True
print(6 in my_list) # 输出: False
3、使用index()方法查找元素的索引:
可以使用index()方法来查找列表中某个元素的索引。如果元素存在于列表中,返回第一个匹配的索引;否则,会抛出ValueError异常。
my_list = [1, 2, 3, 4, 5]
print(my_list.index(3)) # 输出: 2
4、使用循环遍历列表查找元素:
可以使用循环结构(如for循环)遍历列表,逐个比较元素,以查找特定的元素。
my_list = [1, 2, 3, 4, 5]
target = 4
for i in range(len(my_list)):
if my_list[i] == target:
print(f"元素 {target} 的索引为 {i}")
break
5、使用count()方法统计元素出现的次数:
可以使用count()方法来统计列表中某个元素出现的次数。
my_list = [1, 2, 2, 3, 4, 2, 5]
print(my_list.count(2)) # 输出: 3
6、使用列表解析(List comprehension)查找满足条件的元素:
可以使用列表解析来筛选出满足特定条件的元素。
my_list = [1, 2, 3, 4, 5]
even_numbers = [x for x in my_list if x % 2 == 0]
print(even_numbers) # 输出: [2, 4]
7、使用filter()函数查找满足条件的元素:
可以使用filter()函数结合lambda表达式来筛选出满足特定条件的元素。
my_list = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, my_list))
print(even_numbers) # 输出: [2, 4]
五、列表排序
1. 使用sort()方法
Python中的sort()方法可以直接在原列表上进行排序,不返回新的列表。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
numbers.sort()
print(numbers) # 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
2. 使用sorted()函数
sorted()函数会返回一个新的列表,原列表不会改变。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
3. 排序顺序
可以通过sort()方法和sorted()函数的reverse参数来控制排序是升序还是降序。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
numbers.sort(reverse=True)
print(numbers) # 输出: [9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]
4. 自定义排序
可以通过传递一个函数给key参数来自定义排序的规则。
# 按照每个元素的绝对值排序
numbers = [3, -1, 4, -1, 5, -9, 2, 6, -5, 3, -5]
numbers.sort(key=abs)
print(numbers) # 输出: [-1, -1, 2, 3, 3, -5, -5, 4, 5, 6, -9]
自定义排序是指在排序时,根据特定的规则或者属性来对列表中的元素进行排序。在Python中,这通常是通过sort()方法或sorted()函数的key参数来实现的。key参数接受一个函数,这个函数会在每个元素上被调用,其返回值将作为排序的依据。
(1)自定义排序的步骤:
定义排序规则:首先需要定义一个函数,这个函数接受列表中的一个元素作为输入,返回一个值作为排序的依据。
应用排序规则:在调用sort()或sorted()时,将这个函数作为key参数的值传入。
执行排序操作:Python的排序算法会根据key函数返回的值来对元素进行排序。
(2)按字符串长度排序
假设我们有一个字符串列表,我们想按照字符串的长度进行排序。
strings = ['banana', 'apple', 'cherry', 'date']
strings.sort(key=len)
print(strings) # 输出: ['date', 'apple', 'banana', 'cherry']
在这个例子中,key=len告诉sort()方法使用内置的len()函数来获取每个字符串的长度,并根据这个长度来排序。
(3)按字典中的值排序
假设我们有一个字典列表,我们想按照字典中特定键的值进行排序。
people = [
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 20}
]
people.sort(key=lambda person: person['age'])
print(people)
# 输出: [{'name': 'Charlie', 'age': 20}, {'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}]
在这个例子中,我们使用了一个匿名函数(lambda表达式),它接受一个字典(代表一个人)并返回这个字典中'age'键对应的值。排序算法使用这个值来决定排序的顺序。
(4)按复合条件排序
有时候我们可能需要根据多个条件来排序,比如先按年龄排序,年龄相同的情况下按名字排序。
people = [
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 25}
]
people.sort(key=lambda person: (person['age'], person['name']))
print(people)
# 输出: [{'name': 'Alice', 'age': 25}, {'name': 'Charlie', 'age': 25}, {'name': 'Bob', 'age': 30}]
在这个例子中,key函数返回了一个元组,排序算法首先比较元组的第一个元素(年龄),如果相同,则比较第二个元素(名字)。
(5)key参数
在Python中进行列表自定义排序时,key参数可以接受任何可以调用的对象,通常是一个函数,这个函数会接收列表中的每个元素作为输入,并返回一个用于排序的值。这个值可以是任何可以进行比较的数据类型。以下是一些常见的key参数值的例子:
内置函数
len:用于按长度排序,适用于字符串、列表等序列类型。
words = ['banana', 'pie', 'Washington', 'book']
words.sort(key=len)
abs:用于按绝对值排序,适用于数字列表。
numbers = [-5, -3, 1, 4]
numbers.sort(key=abs)
str.lower:将字符串转换为小写,常用于不区分大小写的字符串排序。
words = ['Apple', 'banana', 'Cherry', 'date']
words.sort(key=str.lower)
str.upper:将字符串转换为大写,同样可以用于不区分大小写的字符串排序。
# 假设我们有一个包含大小写混合的字符串列表
words = ['apple', 'Banana', 'cherry', 'Date']
# 使用 str.upper 作为 key 函数进行排序
# 这会将所有字符串转换为大写,并按照转换后的大写字符串进行排序
words.sort(key=str.upper)
print(words)
# 输出: ['apple', 'Banana', 'cherry', 'Date']
在这个例子中,str.upper 函数被用作 key 参数,它会将列表中的每个字符串转换为大写形式,然后根据这些大写字符串进行排序。这样,原始列表中的字符串就会按照不区分大小写的顺序进行排序。注意,虽然排序依据是大写字符串,但排序结果中字符串的大小写保持不变。
int:将一个数值或字符串转换为整数,可以用于将字符串数字排序为数值。
numbers_as_strings = ['2', '11', '5', '23']
numbers_as_strings.sort(key=int)
float:将一个字符串或整数转换为浮点数,用于将字符串数字排序为浮点数值。
# 假设我们有一个包含数字字符串的列表
numbers_as_strings = ['3.14', '100.5', '1.0', '0.99', '10.1']
# 使用 float 作为 key 函数进行排序
# 这会将字符串转换为浮点数,并按照它们的数值大小进行排序
numbers_as_strings.sort(key=float)
print(numbers_as_strings)
# 输出: ['0.99', '1.0', '3.14', '10.1', '100.5']
在这个例子中,列表中的每个字符串都被 float 函数转换为了对应的浮点数值,然后列表根据这些浮点数值进行排序。这确保了数字的排序是按照它们的实际数值大小,而不是按照字符串的字典顺序,这在字符串的字典顺序与数值大小顺序不一致时尤其重要。
ord:返回单个字符的Unicode码点,常用于按字符的ASCII值排序。
chars = ['b', 'a', 'd', 'c']
chars.sort(key=ord)
sum:对序列中的所有项求和,可以用于按元素总和排序。
lists = [[10, 20], [40, 5], [30]]
lists.sort(key=sum)
min:获取序列中的最小值,可以用于按最小元素排序。
lists = [[10, 20], [40, 5], [30]]
lists.sort(key=min)
max:获取序列中的最大值,可以用于按最大元素排序。
lists = [[10, 20], [40, 5], [30]]
lists.sort(key=max)
bool:将值转换为布尔值,虽然不常直接用于排序,但有时可以用于将元素按布尔值分类。
# 假设我们有一个混合列表,包含了不同类型的元素
items = [0, '', 'Word', [], {}, 42, ['list'], ' ', None, False, True]
# 使用 bool 作为 key 函数进行排序
# False 的布尔值为 0,True 的布尔值为 1
# 因此,所有布尔值为 False 的元素会被放在前面
items.sort(key=bool)
print(items)
# 输出: [0, '', [], {}, None, False, 'Word', 42, ['list'], ' ', True]
在这个例子中,0, ''(空字符串), [](空列表), {}(空字典), None, 和 False 都是布尔值为 False 的元素,而其他的如 'Word', 42, ['list'], ' '(空格), 和 True 是布尔值为 True 的元素。使用 bool 作为 key 函数将会根据元素的布尔值进行排序。
自定义函数
可以定义一个函数,该函数可以处理更复杂的逻辑,然后返回一个值用于排序。
def get_second_element(tup):
return tup[1]
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=get_second_element)
匿名函数(lambda表达式)
对于简单的自定义排序规则,可以使用匿名函数(lambda表达式)。
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda x: x[1])
属性和方法
attrgetter: 当列表中的元素是对象时,用于按对象的属性排序。methodcaller: 当列表中的元素是对象,并且需要调用对象的方法来获取排序依据时使用。
from operator import attrgetter, methodcaller
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def get_age(self):
return self.age
people = [Person('Alice', 30), Person('Bob', 25), Person('Charlie', 35)]
people.sort(key=attrgetter('age'))
# 或者如果你需要调用方法
people.sort(key=methodcaller('get_age'))
元组
当需要根据多个条件进行排序时,可以返回一个包含这些条件的元组。
students = [
{'name': 'John', 'grades': (88, 92)},
{'name': 'Jane', 'grades': (90, 90)}
]
students.sort(key=lambda x: (x['grades'][0], x['grades'][1]))
itemgetter
当列表中的元素是字典时,可以使用itemgetter来按字典的键排序。
from operator import itemgetter
people = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
people.sort(key=itemgetter('age'))
5. 复杂数据结构排序
对于包含复杂数据结构的列表,比如列表包含字典,可以使用key参数来指定排序的依据。
# 假设我们有一个字典列表,我们想根据每个字典中的'age'键来排序
people = [
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 20}
]
people.sort(key=lambda person: person['age'])
print(people)
# 输出: [{'name': 'Charlie', 'age': 20}, {'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}]
六、其他操作
1、反转
反转列表意味着将列表中的元素顺序颠倒,使得原来的第一个元素变成最后一个元素,原来的最后一个元素变成第一个元素,以此类推。以下是几种在Python中反转列表的方法:
(1)使用循环手动反转:
虽然不是最高效的方法,但手动反转列表可以帮助你更好地理解反转的过程。
# 示例
original_list = [1, 2, 3, 4, 5]
reversed_list = []
for item in original_list:
reversed_list.insert(0, item)
print(reversed_list) # 输出: [5, 4, 3, 2, 1]
在这个例子中,我们使用insert()方法在列表的开头插入每个元素,从而实现反转。
(2)使用列表的reverse()方法:
这是一个直接修改原列表的方法,它不返回任何值(返回None),而是直接在原地改变列表的顺序。
# 示例
original_list = [1, 2, 3, 4, 5]
original_list.reverse()
print(original_list) # 输出: [5, 4, 3, 2, 1]
(3)使用切片:
切片是Python中非常强大的特性之一,可以用来访问序列类型的子集。通过使用切片,我们可以创建一个反转的列表副本。
# 示例
original_list = [1, 2, 3, 4, 5]
reversed_list = original_list[::-1]
print(reversed_list) # 输出: [5, 4, 3, 2, 1]
这里的[::-1]是切片的语法,表示从开始到结束,步长为-1,即反向。
(4)使用reversed()函数:
reversed()函数返回一个反转的迭代器。你可以使用list()函数将其转换为列表。
# 示例
original_list = [1, 2, 3, 4, 5]
reversed_list = list(reversed(original_list))
print(reversed_list) # 输出: [5, 4, 3, 2, 1]
2、长度
列表长度指的是它包含的元素数量。
(1)使用len()函数获取列表长度:
len()函数是Python中的内置函数,用于获取列表中元素的数量。
# 示例
my_list = [10, 20, 30, 40, 50]
length = len(my_list)
print(length) # 输出: 5
在这个例子中,len()函数返回列表my_list中的元素个数,即5。
(2)列表长度与索引:
列表的索引从0开始,所以最后一个元素的索引将是列表长度减去1。
# 示例
my_list = ['a', 'b', 'c', 'd']
last_index = len(my_list) - 1
print(last_index) # 输出: 3
print(my_list[last_index]) # 输出: 'd'
这里,我们使用列表长度来确定最后一个元素的索引,并通过索引访问该元素。
(3)列表长度在循环中的应用:
列表长度常用于循环中,以确保遍历列表中的所有元素。
# 示例
my_list = [1, 2, 3, 4, 5]
for i in range(len(my_list)):
print(my_list[i])
这个例子中,range(len(my_list))生成一个从0到列表长度减去1的序列,用于在for循环中迭代列表的每个元素。
(4)列表长度的动态变化:
在对列表进行添加或删除元素的操作时,列表的长度会动态变化。
# 示例
my_list = [1, 2, 3]
print(len(my_list)) # 输出: 3
# 添加元素
my_list.append(4)
print(len(my_list)) # 输出: 4
# 删除元素
my_list.remove(2)
print(len(my_list)) # 输出: 3
在这个例子中,我们首先打印出列表的初始长度,然后添加一个元素并再次打印长度,接着删除一个元素后再次打印长度,观察到列表长度的变化。
(5)空列表的长度:
空列表是不包含任何元素的列表,其长度为0。
# 示例
empty_list = []
print(len(empty_list)) # 输出: 0
在这个例子中,我们创建了一个空列表,并使用len()函数验证其长度为0。
七、列表嵌套与二维及多维列表
列表嵌套是指一个列表内部包含另一个列表或多个列表。这是一个非常有用的特性,因为它允许我们构建更复杂的数据结构,比如矩阵或者可以表示更多维度数据的列表。
(1)创建嵌套列表:
可以通过将一个或多个列表作为元素放入另一个列表来创建嵌套列表。
# 一个包含三个列表的嵌套列表
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(2)访问嵌套列表中的元素:
要访问嵌套列表中的元素,你需要使用多个索引。
# 访问第二个子列表的第三个元素
element = nested_list[1][2] # 输出将是6
(3)修改嵌套列表中的元素:
与访问嵌套列表中的元素类似,你也可以修改它们。
# 将第三个子列表的第二个元素修改为10
nested_list[2][1] = 10 # 现在nested_list变为[[1, 2, 3], [4, 5, 6], [7, 10, 9]]
(4)遍历嵌套列表:
可以使用嵌套的循环来遍历嵌套列表中的所有元素。
# 遍历嵌套列表的每个元素
for sublist in nested_list:
for item in sublist:
print(item)
(5)列表推导式与嵌套列表:
列表推导式可以用来创建嵌套列表,或者从嵌套列表中生成新的列表。
# 使用列表推导式创建一个3x3的乘法表嵌套列表
multiplication_table = [[i * j for j in range(1, 4)] for i in range(1, 4)]
# 输出[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# 将嵌套列表扁平化为一个列表
flat_list = [item for sublist in nested_list for item in sublist]
# 输出[1, 2, 3, 4, 5, 6, 7, 10, 9]
(6)嵌套列表的复杂操作:
可以执行更复杂的操作,比如矩阵转置或者查找嵌套列表中的最大元素。
# 矩阵转置
transposed = [[nested_list[j][i] for j in range(len(nested_list))] for i in range(len(nested_list[0]))]
# 输出[[1, 4, 7], [2, 5, 10], [3, 6, 9]]
# 查找嵌套列表中的最大元素
max_element = max(max(sublist) for sublist in nested_list)
# 输出10
二维列表输出
l=[
[1,2,3],
[4,5,6],
[7,8,9]
]
# 1 2 3
# 4 5 6
# 7 8 9
# l[0][2]
for i in range(3):
for j in range(3):
print(l[i][j],end=' ')
print("\n")
第七章 字典
在Python中,字典是一种非常灵活的数据结构,你可以把它想象成一个现实生活中的字典,只不过这个字典里存储的是“键-值”对(key-value pairs),而不是单词和定义。
每个“键-值”对就像是一个条目,其中“键”(key)类似于单词,而“值”(value)则类似于单词的定义。在Python字典中,你可以通过“键”来快速找到对应的“值”。
让我们来看一个简单的例子:
# 创建一个字典
my_dict = {
'apple': 'a fruit',
'banana': 'a yellow fruit',
'cherry': 'a red fruit'
}
# 访问字典中的值
print(my_dict['apple']) # 输出: a fruit
在这个例子中,'apple'、'banana' 和 'cherry' 是字典的键,而它们对应的 'a fruit'、'a yellow fruit' 和 'a red fruit' 是这些键的值。
字典的特点包括:
1、键必须是唯一的:就像现实生活中的字典,每个单词只能有一个定义。
2、字典是无序的:你不能指望它们会按照特定的顺序排列条目,尽管在Python 3.7及以上版本中,字典保持插入顺序。
3、字典是可变的:你可以随时添加、删除或修改键值对。
一、增加
首先,Python字典是一种可变容器模型,可以存储任意类型对象。字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号 , 分割,整个字典包括在花括号{}中。键必须是唯一的,但值则不必。
1、添加单个元素
要向字典中添加一个元素,可以使用下标语法,如果键不存在于字典中,它会自动添加新的键值对。
# 创建一个空字典
my_dict = {}
# 添加一个键值对
my_dict['apple'] = 'red'
print(my_dict)
# 输出: {'apple': 'red'}
2、使用update()方法
如果想一次性添加多个键值对,可以使用update()方法。这个方法接受另一个字典或者一个可迭代对象的键值对。
# 已有的字典
my_dict = {'apple': 'red'}
# 使用另一个字典来更新
my_dict.update({'banana': 'yellow', 'cherry': 'red'})
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow', 'cherry': 'red'}
或者使用键值对序列来更新字典:
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 使用键值对序列来更新
my_dict.update([('grape', 'purple'), ('orange', 'orange')])
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow', 'cherry': 'red', 'grape': 'purple', 'orange': 'orange'}
3、使用setdefault()方法
setdefault()方法在某种程度上类似于下标语法,但是它在键不存在的时候插入键值对,并且可以设置一个默认值。
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red',
'grape': 'purple',
'orange': 'orange'
}
# 如果键不存在,则添加键并设置默认值
my_dict.setdefault('kiwi', 'green')
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow', 'cherry': 'red', 'grape': 'purple', 'orange': 'orange', 'kiwi': 'green'}
如果键已经存在,setdefault()不会改变字典:
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red',
'grape': 'purple',
'orange': 'orange'
}
# 如果键已经存在,不会改变其值
my_dict.setdefault('apple', 'green')
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow', 'cherry': 'red', 'grape': 'purple', 'orange': 'orange', 'kiwi': 'green'}
注意:字典中的键是唯一的,所以当添加一个已经存在的键时,其对应的值会被新的值覆盖。
二、删除
1、使用del语句
如果知道要删除的键,可以使用del语句来删除字典中的元素。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 删除键 'banana' 及其对应的值
del my_dict['banana']
print(my_dict)
# 输出: {'apple': 'red', 'cherry': 'red'}
如果尝试删除不存在的键,会引发KeyError。
2、使用pop()方法
pop()方法可以用来删除字典中的一个键值对。当使用pop()时,可以得到被删除的值。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 删除键 'cherry' 并获取其值
value = my_dict.pop('cherry')
print(value)
# 输出: 'red'
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow'}
如果尝试pop()一个不存在的键,可以提供一个默认值,这样不会引发错误。
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 尝试删除不存在的键,提供默认值
value = my_dict.pop('kiwi', 'No Fruit Found')
print(value)
# 输出: 'No Fruit Found'
3、使用popitem()方法
popitem()方法会删除字典中的最后一个插入的键值对(在Python 3.7+中,字典是有序的)。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 删除最后一个插入的键值对
item = my_dict.popitem()
print(item)
# 输出: ('cherry', 'red')
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow'}
4、使用clear()方法
如果想删除字典中的所有元素,可以使用clear()方法。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red'
}
# 删除所有元素
my_dict.clear()
print(my_dict)
# 输出: {}
注意:使用del和pop()时,如果不确定键是否存在,最好先检查字典中是否有该键,或者使用pop()的默认值参数,以避免程序因KeyError而中断。
三、修改
在Python中,修改字典是一个非常直接的操作。字典是可变的数据类型,这意味着我们可以在不创建新字典的情况下改变它们的内容。
1、修改已有键的值
如果想改变字典中某个键的值,可以直接通过键来访问并赋予一个新的值。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow'
}
# 修改键 'apple' 的值
my_dict['apple'] = 'green'
print(my_dict)
# 输出: {'apple': 'green', 'banana': 'yellow'}
将’apple’对应的值从’red’改为了’green’。
2、使用update()方法
update()方法不仅可以用来添加新的键值对,还可以用来修改已有的键值对。如果传递给update()方法的键已经存在于字典中,其对应的值将被更新。
my_dict = {
'apple': 'red',
'banana': 'yellow'
}
# 使用另一个字典来更新'apple'的值
my_dict.update({'apple': 'green'})
print(my_dict)
# 输出: {'apple': 'green', 'banana': 'yellow'}
使用update()方法将’apple’的值更新为’green’。
3、使用setdefault()方法
setdefault()方法通常用于获取某个键对应的值,如果键不存在,则在字典中添加该键并设置默认值。但是,如果键已经存在,setdefault()不会改变其值。
my_dict = {
'apple': 'red',
'banana': 'yellow'
}
# 尝试使用 setdefault() 修改一个存在的键的值
my_dict.setdefault('apple', 'green')
print(my_dict)
# 输出: {'apple': 'red', 'banana': 'yellow'}
my_dict.setdefault('520', '1314')
print(my_dict)
# 输出:{'apple': 'red', 'banana': 'yellow', '520': '1314'}
尽管我们尝试将’apple’的值设置为’green’,但因为’apple’键已经存在,所以setdefault()并没有修改它的值。
4、合并字典
在Python 3.5+中,你可以使用{**d1, **d2}的语法来合并两个字典,如果有重复的键,后面的字典中的键值对会覆盖前面的。
# 创建两个字典
dict1 = {
'apple': 'red',
'banana': 'yellow'
}
dict2 = {
'apple': 'green',
'cherry': 'red'
}
# 合并两个字典
merged_dict = {**dict1, **dict2}
print(merged_dict)
# 输出: {'apple': 'green', 'banana': 'yellow', 'cherry': 'red'}
dict2中的’apple’:‘green’覆盖了dict1中的’apple’:‘red’。
5、使用字典推导式修改
字典推导式可以用来通过现有字典创建一个新的字典,这个过程中可以修改键和/或值。
# 创建一个字典
my_dict = {
'apple': 'red',
'banana': 'yellow'
}
# 创建一个新的字典,将所有的值变为'colorful'
new_dict = {key: 'colorful' for key in my_dict}
print(new_dict)
# 输出: {'apple': 'colorful', 'banana': 'colorful'}
在这个例子中,我们没有修改原始字典,而是创建了一个新的字典,其中所有的值都被设置为’colorful’。
四、查找
在Python中,字典是一种非常灵活的数据结构,它存储的是键(key)和值(value)的配对。你可以把它想象成现实生活中的字典,你通过查找一个词(键)来获取它的定义(值)。在Python字典中,每个键都是唯一的,而且键和值可以是任何数据类型。
1、字典的创建
首先,创建一个字典。比如,我们要存储一个人的信息:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
在这个person字典里,"name", "age", "city"是键,而"Alice", 25, "New York"是它们对应的值。
2、查找操作
(1)获取值
如果想要获取Alice的年龄,可以使用以下代码:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
age = person["age"]
print(age)
# 输出:25
如果尝试获取一个不存在的键,比如person["birthday"],Python会抛出一个KeyError。
(2)安全获取值
为了避免KeyError,可以使用get方法。如果键不存在,get方法会返回None,或者指定的默认值。
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
birthday = person.get("birthday")
print(birthday)
# 返回 None,因为"birthday"键不存在
birthday = person.get("birthday", "Not Available")
print(birthday)
# 输出 "Not Available"
(3)检查键是否存在
可以使用in关键字来检查一个键是否存在于字典中:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
if "name" in person:
print("Name is available")
(4)更新和添加键值对
如果想更新Alice的城市,可以这样做:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
person["city"] = "Boston"
print(person["city"])
# 输出 "Boston"
如果想添加新的键值对,比如电话号码,可以直接赋值:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
person["phone"] = "123-456-7890"
print(person)
# 输出:{'name': 'Alice', 'age': 25, 'city': 'New York', 'phone': '123-456-7890'}
print(person["phone"])
# 输出 "123-456-7890"
(5)删除键值对
可以使用del语句来删除键值对:
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
del person["age"]
print(person)
# 输出 {'name': 'Alice', 'city': 'Boston', 'phone': '123-456-7890'}
或者使用pop方法,这个方法会返回被删除的值:
person = {
"name": "Alice",
"age": 25,
"city": "New York",
"phone": "123-456-7890"
}
phone = person.pop("phone")
print(phone)
# 输出 "123-456-7890"
print(person)
# 输出 {'name': 'Alice', 'city': 'Boston'}
(6)遍历字典
可以遍历字典中的键值对:
person = {
"name": "Alice",
"age": 25,
"city": "New York",
"phone": "123-456-7890"
}
for key, value in person.items():
print(f"Key: {key}, Value: {value}")
输出每个键值对。
(7)字典的键的要求
字典的键必须是不可变的类型,比如数字、字符串或元组。这意味着你不能使用列表作为键,因为列表是可变的。
3、使用get()方法
这是一个非常有用的方法,用于在字典中查找键对应的值。使用get()方法的好处是,如果你尝试查找的键不存在于字典中,它不会抛出KeyError,而是会返回None,或者你可以指定一个默认值,以防键不存在。
# 假设我们有这样一个字典
my_dict = {
'name': 'John',
'age': 30
}
# 使用get()方法查找存在的键
name = my_dict.get('name')
print(name)
# 输出 'John'
# 使用get()方法尝试查找不存在的键,返回None
address = my_dict.get('address')
print(address)
# 输出 None
# 使用get()方法尝试查找不存在的键,指定默认值
address = my_dict.get('address', 'No Address provided')
print(address)
# 输出 'No Address provided'
在这个例子中,我们首先查找了一个存在的键'name',然后尝试查找一个不存在的键'address'。在第二次查找中,我们没有提供默认值,所以get()方法返回了None。在第三次查找中,我们提供了一个默认值'No Address provided',所以当'address'键不存在时,get()方法返回了我们指定的默认值。
五、获取所有键
在Python中,字典是一种非常灵活的数据结构,它存储的是键(key)和值(value)的配对。想象一下,字典就像是一个真实的字典,你通过查找一个词(键)来获取它的定义(值)。在Python字典中,每个键都是唯一的,并且与一个值相关联。
1、使用keys()方法
这是获取字典所有键的最直接方法。keys()方法会返回一个包含字典所有键的视图,这个视图看起来像是一个列表,但它不是列表。你不能直接对它进行索引操作,但可以使用list()函数将其转换为列表。
# 创建一个字典
my_dict = {
'name': 'Alice',
'age': 25,
'city': 'New York'
}
# 使用keys()方法获取所有键
keys = my_dict.keys()
# 打印出所有的键
print(keys)
# 输出: dict_keys(['name', 'age', 'city'])
# 如果你想要一个真正的列表,可以这样做
keys_list = list(keys)
print(keys_list)
# 输出: ['name', 'age', 'city']
2、使用循环遍历
也可以通过遍历字典来获取所有的键。在一个循环中遍历字典时,实际上是在遍历它的键。
my_dict = {
'name': 'Alice',
'age': 25,
'city': 'New York'
}
# 遍历字典获取所有键
for key in my_dict:
print(key)
# 输出:
# name
# age
# city
3、使用列表推导式
列表推导式是Python中一种非常强大的特性,它可以从一个可迭代的对象中创建列表。可以使用列表推导式来获取所有的键。
my_dict = {
'name': 'Alice',
'age': 25,
'city': 'New York'
}
# 使用列表推导式获取所有键
keys = [key for key in my_dict]
print(keys)
# 输出: ['name', 'age', 'city']
六、获取所有值
1、使用values()方法
values()方法可以返回一个包含字典中所有值的视图。这个视图对象不是一个列表,但是在很多情况下,它的行为和列表很相似。可以迭代它,但不能对其进行索引或切片操作。如果需要一个真正的列表,可以使用list()函数将其转换。
my_dict = {
'name': 'Bob',
'age': 30,
'city': 'San Francisco'
}
# 使用values()方法获取所有值
values = my_dict.values()
# 打印出所有的值
print(values)
# 输出: dict_values(['Bob', 30, 'San Francisco'])
# 如果你想要一个真正的列表,可以这样做
values_list = list(values)
print(values_list)
# 输出: ['Bob', 30, 'San Francisco']
2、使用循环遍历
也可以通过遍历字典项来获取所有的值。这种方法稍微复杂一点,因为需要指定只想要值。
my_dict = {
'name': 'Bob',
'age': 30,
'city': 'San Francisco'
}
# 遍历字典获取所有值
for value in my_dict.values():
print(value)
# 输出:
# Bob
# 30
# San Francisco
3、使用列表推导式
和获取所有键类似,可以使用列表推导式来获取所有的值。这种方法非常简洁且强大。
my_dict = {
'name': 'Bob',
'age': 30,
'city': 'San Francisco'
}
# 使用列表推导式获取所有值
values = [value for value in my_dict.values()]
print(values)
# 输出: ['Bob', 30, 'San Francisco']
这些方法都是获取字典中所有值的有效方式。通常情况下,values()方法是最直接的,但如果需要对值进行进一步的处理或筛选,列表推导式可能会更加方便。在实际编程中,可以根据具体的需求选择最合适的方法。
七、获取所有键值对
在Python中,字典是由键值对组成的集合,每个键值对将一个键映射到一个值。获取字典中所有的键值对是一个常见的操作,可以帮助你了解字典的内容。
1、使用items()方法
items()方法返回一个包含所有字典键值对的视图。这个视图对象包含了元组,每个元组由两个元素组成:键和对应的值。可以迭代这个视图,就像迭代一个列表一样,但不能对其进行索引或切片操作。如果需要一个列表,可以使用list()函数将其转换。
my_dict = {
'name': 'Eva',
'age': 22,
'city': 'Berlin'
}
# 使用items()方法获取所有键值对
items = my_dict.items()
# 打印出所有的键值对
print(items)
# 输出: dict_items([('name', 'Eva'), ('age', 22), ('city', 'Berlin')])
# 如果想要一个真正的列表,可以这样做
items_list = list(items)
print(items_list)
# 输出: [('name', 'Eva'), ('age', 22), ('city', 'Berlin')]
2、使用循环遍历
可以通过遍历字典来获取所有的键值对。在Python中,直接遍历字典时,实际上是在遍历它的键。但是,如果在遍历时使用items()方法,能够同时访问键和值。
my_dict = {
'name': 'Eva',
'age': 22,
'city': 'Berlin'
}
# 遍历字典获取所有键值对
for key, value in my_dict.items():
print(f"Key: {key}, Value: {value}")
# 输出:
# Key: name, Value: Eva
# Key: age, Value: 22
# Key: city, Value: Berlin
3、使用列表推导式
如果想要以列表的形式获取所有键值对,可以使用列表推导式。这种方法可以快速创建一个包含所有键值对的列表。
my_dict = {
'name': 'Eva',
'age': 22,
'city': 'Berlin'
}
# 使用列表推导式获取所有键值对
items = [(key, value) for key, value in my_dict.items()]
print(items)
# 输出: [('name', 'Eva'), ('age', 22), ('city', 'Berlin')]
items()方法是最常用的,因为它简单直观。如果需要对键值对进行进一步的处理或筛选,列表推导式可能会更加方便。在编程实践中,选择哪种方法取决于具体需求和偏好。
第八章 元组
在Python中,元组(Tuple)是一种不可变的序列类型,它在很多方面类似于列表(List),但有几个关键的不同点。下面我会详细介绍元组的特性、用法以及与列表的区别。
1、元组的定义和创建
元组是由一系列用逗号分隔的值组成的,通常这些值被包含在圆括号()中,但圆括号不是必须的。例如:
# 创建元组的几种方式
tup1 = (1, 2, 3)
tup2 = "a", "b", "c"
tup3 = () # 空元组
tup4 = (4,) # 只有一个元素的元组,逗号不能省略
2、元组的不可变性
与列表不,同元组一旦创建,它的内容就不能被修改,这就是所谓的“不可变性”。这意味着你不能添加、删除或修改元组中的元素。尝试这样做会导致Python抛出一个TypeError异常。
tup = (1, 2, 3)
# tup[0] = 4 # 这会抛出TypeError
3、元组的索引和切片
尽管元组是不可变的,但你仍然可以通过索引和切片来访问它们的元素,就像列表一样。
tup = (1, 2, 3, 4, 5)
print(tup[0]) # 输出第一个元素
print(tup[1:3]) # 输出第二个到第三个元素,得到一个新的元组(2, 3)
4、元组的方法
由于元组是不可变的,它们的方法比列表要少。最常用的两个方法是count()和index()。
count(value):返回元组中值等于value的元素个数。index(value):返回元组中第一个值等于value的元素的索引。
tup = (1, 2, 3, 2, 4)
print(tup.count(2)) # 输出2,因为元组中有两个2
print(tup.index(3)) # 输出2,因为3在元组中的索引是2
5、元组与列表的转换
虽然元组是不可变的,但你可以通过转换来在列表和元组之间切换,这样就可以在需要可变性时使用列表,需要不可变性时使用元组。
list_to_tuple = tuple([1, 2, 3]) # 列表转换为元组
tuple_to_list = list((1, 2, 3)) # 元组转换为列表
6、元组的用途
元组的不可变性使得它们在某些情况下比列表更适用。例如:
- 作为字典的键(列表不能因为是可变的)。
- 函数返回多个值时,通常使用元组。
- 在需要保证数据不被修改的场景下使用元组。
第九章 集合
集合(Set)在Python中是一种基础的数据结构,它类似于数学上的集合概念。想象一下你有一个果篮,你可以往里面放各种水果,但是每种水果只能放一个,不会有重复的。Python中的集合也是这样,它是一个无序的、元素唯一的数据集合。
在Python中,可以使用大括号 {} 或者 set() 函数来创建一个集合。比如:
fruits = {"apple", "banana", "cherry"}
或者:
fruits = set(["apple", "banana", "cherry"])
这两种方式都创建了一个包含苹果、香蕉和樱桃的集合。
集合的几个关键特性包括:
-
唯一性:集合中的元素是唯一的,也就是说,即使你尝试添加两个相同的元素到集合中,集合仍然只会保留一个。
-
无序性:集合中的元素是无序的,你不能像列表那样通过索引来访问集合中的元素。
-
可变性:你可以随时添加或者删除集合中的元素。
集合支持多种操作,比如:
- 添加元素:使用
add()方法可以添加一个元素到集合中。 - 删除元素:使用
remove()方法可以删除集合中的一个特定元素,如果元素不存在会抛出错误;discard()方法也是用来删除元素,但如果元素不存在不会抛出错误。 - 清空集合:
clear()方法可以移除集合中的所有元素。 - 集合运算:可以进行并集(
union或|)、交集(intersection或&)、差集(difference或-)和对称差集(symmetric_difference或^)等运算。
一、增加元素
1、add()方法
如果想往集合中添加一个元素,可以使用 add() 方法。这很像我们在现实生活中把一张新的卡片放进一个卡片盒里。如果这张卡片已经在盒子里了,那么盒子就不会有任何变化,因为集合里不能有重复的卡片。下面是一个简单的例子:
my_set = {1, 2, 3}
my_set.add(4)
print(my_set)
# 输出:{1, 2, 3, 4}
2、update()方法
如果想一次性添加多个元素,或者添加另一个集合中的元素到当前集合,可以使用 update() 方法。这就像我们拿了一堆新卡片,或者另一个小卡片盒,然后把里面的卡片都放到我们的大盒子里。这里需要注意的是,即使你添加了多个重复的元素,集合中还是只会保留一个。这是一个例子:
my_set = {1, 2, 3}
my_set.update([3, 4, 5])
print(my_set)
# 输出:{1, 2, 3, 4, 5}
虽然列表 [3, 4, 5] 中的 3 已经在集合 my_set 中了,但它不会被重复添加。
二、删除元素
1、remove() 方法:
如果你知道要从集合中删除哪个元素,可以使用 remove 方法。这就像是从篮子中挑出一个特定的球并且扔掉。但是,如果你尝试移除一个不存在于集合中的元素,Python会抛出一个错误。这就好比你被告知去扔掉一个根本不在篮子里的球。
s = {1, 2, 3}
s.remove(2) # 集合变成 {1, 3}
# s.remove(5) # 这会引发 KeyError,因为5不在集合s中
2、discard() 方法:
discard 方法和 remove 方法很相似,它也是用来丢弃集合中的一个特定元素。不同之处在于,如果元素不存在于集合中,discard 不会抛出错误。这就像是你被告知去扔掉一个球,如果篮子里没有这个球,你就无视这个指令,什么也不做。
s = {1, 2, 3}
s.discard(3) # 集合变成 {1, 2}
s.discard(5) # 没有错误,集合仍然是 {1, 2}
3、pop() 方法:
如果你不关心要移除哪个元素,可以使用 pop 方法。这就像是闭上眼睛从篮子里随机拿出一个球。由于集合是无序的,你无法知道 pop 方法会移除哪个元素。如果集合为空,调用 pop 方法会抛出一个错误。
s = {1, 2, 3}
elem = s.pop() # 移除并返回集合中的一个随机元素
# 如果集合s为空,比如再调用两次pop后,再次调用pop会引发 KeyError
4、clear() 方法:
如果你想要一次性移除所有元素,可以使用 clear 方法。这就像是把篮子翻过来,让所有的球都滚出去。之后,你会得到一个空的篮子,或者在我们的例子中,是一个空的集合。
s = {1, 2, 3}
s.clear() # 集合变成空集合 {}
三、修改元素
在Python中,集合(Set)是一个无序的、不包含重复元素的集合数据类型。它非常适合用来进行成员关系测试、去除重复元素以及数学上的集合操作,比如并集、交集、差集等。
但是,需要注意的是,集合中的元素是不可变的,这意味着你不能修改集合中的元素。不过,你可以向集合中添加新元素或者从集合中移除元素。
四、集合长度及元素存在
集合的长度指的是集合中元素的数量。在Python中,你可以使用内置的len()函数来获取集合的长度。例如:
my_set = {1, 2, 3, 4, 5}
print(len(my_set)) # 输出将会是5,因为集合中有5个元素
检查元素是否存在于集合中可以使用in关键字。如果元素在集合中,它将返回True,否则返回False。这是一个例子:
my_set = {1, 2, 3, 4, 5}
print(3 in my_set) # 输出将会是True,因为3存在于集合中
print(6 in my_set) # 输出将会是False,因为6不在集合中
集合非常适合用来进行数学上的集合操作,比如并集、交集、差集等。但在我们的日常编程中,检查元素是否存在和获取集合大小是最常见的操作。
五、集合操作
假设我们有两个集合:
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
1、并集(Union)
并集是指包含两个集合中所有元素的集合,重复的元素只会出现一次。在Python中,你可以使用|运算符或者union()方法来求两个集合的并集。
# 使用 | 运算符
union_set = set_a | set_b
print(union_set) # 输出 {1, 2, 3, 4, 5, 6}
# 使用 union() 方法
union_set = set_a.union(set_b)
print(union_set) # 输出 {1, 2, 3, 4, 5, 6}
2、交集(Intersection)
交集是指包含两个集合中共同元素的集合。在Python中,你可以使用&运算符或者intersection()方法来求两个集合的交集。
# 使用 & 运算符
intersection_set = set_a & set_b
print(intersection_set) # 输出 {3, 4}
# 使用 intersection() 方法
intersection_set = set_a.intersection(set_b)
print(intersection_set) # 输出 {3, 4}
3、差集(Difference)
差集是指存在于第一个集合中但不在第二个集合中的元素组成的集合。在Python中,你可以使用-运算符或者difference()方法来求两个集合的差集。
# 使用 - 运算符
difference_set = set_a - set_b
print(difference_set) # 输出 {1, 2}
# 使用 difference() 方法
difference_set = set_a.difference(set_b)
print(difference_set) # 输出 {1, 2}
4、对称差集(Symmetric Difference)
对称差集是指包含两个集合中非共同元素的集合。在Python中,你可以使用^运算符或者symmetric_difference()方法来求两个集合的对称差集。
# 使用 ^ 运算符
symmetric_difference_set = set_a ^ set_b
print(symmetric_difference_set) # 输出 {1, 2, 5, 6}
# 使用 symmetric_difference() 方法
symmetric_difference_set = set_a.symmetric_difference(set_b)
print(symmetric_difference_set) # 输出 {1, 2, 5, 6}
5、其他操作和方法
子集(Subset)
可以检查一个集合是否是另一个集合的子集,即所有来自第一个集合的元素都存在于第二个集合中。
set_c = {1, 2}
set_d = {1, 2, 3, 4}
# 使用 issubset() 方法
is_subset = set_c.issubset(set_d)
print(is_subset) # 输出 True
超集(Superset)
与子集相反,可以检查一个集合是否是另一个集合的超集,即第一个集合包含第二个集合的所有元素。
# 使用 issuperset() 方法
is_superset = set_d.issuperset(set_c)
print(is_superset) # 输出 True
添加元素(Add)
可以向集合添加一个中元素。
set_c.add(5)
print(set_c) # 输出 {1, 2, 5}
移除元素(Remove)
可以移除集合中的一个特定元素。如果元素不存在,remove() 方法会引发一个错误。
set_c.remove(5)
print(set_c) # 输出 {1, 2}
或者使用 discard() 方法,它不会在元素不存在时引发错误。
set_c.discard(5) # 即使5不在set_c中,也不会引发错误
清空集合(Clear)
可以移除集合中的所有元素。
set_c.clear()
print(set_c) # 输出 set()
集合的复制(Copy)
可以创建集合的浅拷贝。
set_e = set_c.copy()
print(set_e) # 输出 {1, 2}
集合推导式(Set Comprehensions)
类似于列表推导式,集合推导式提供了一种简洁的方式来创建集合。
set_f = {x * 2 for x in range(5)}
print(set_f) # 输出 {0, 2, 4, 6, 8}
第十章 可变类型数据与不可变类型数据
如果我们在这里面定一个i = 1的时候,并不是把1指向i,也就是1在内存中存储的应该是140737441142432这样一个信息,140737441142432其实就是1的内存地址。
可变类型数据:当数据发生改变的时候,地址不发生改变。
不可变类型数据:当数据发生改变的时候,地址发生改变。
在Python中,数据类型可以分为可变类型和不可变类型。这个分类基于对象(即数据)在内存中的行为。
首先,我们来聊聊不可变类型。不可变类型的对象一旦创建,它的内容就不能改变。如果你尝试修改它,Python实际上会创建一个新的对象。
常见的可变类型包括:列表、字典、集合(set)。
常见的不可变类型包括:整数(int)、浮点数(float)、字符串(str)和元组(tuple)。
比如说,你有一个字符串:
greeting = "Hello"
如果你尝试改变字符串的一部分,比如:
greeting[0] = "M"
这是不允许的,Python会报错,因为字符串是不可变的。如果你想要一个新的字符串,你需要创建一个新的字符串对象,比如:
new_greeting = "M" + greeting[1:]
现在,new_greeting 的值是 "Mello",但原来的 greeting 依然是 "Hello"。
接下来是可变类型。可变类型的对象可以在它们创建后改变。列表(list)和字典(dict)是Python中常见的可变类型。
例如,你有一个列表:
fruits = ["apple", "banana", "cherry"]
你可以改变列表中的元素:
fruits[0] = "apricot"
现在 fruits 的内容变成了 ["apricot", "banana", "cherry"]。列表的内容被直接修改了,没有创建新的列表。
简单来说,不可变类型就像是写在石头上的文字,一旦刻下,就不能改变;而可变类型就像是写在白板上的文字,你可以用橡皮擦掉它们,再写上新的内容。这种区别在编程中非常重要,因为它影响了代码的行为,特别是在处理数据结构或者在函数间传递参数时。
第十一章 字符串的常用方法
字符串是编程中非常基础且重要的数据类型,它们就像文字一样,可以被操作和变换以满足不同的需求。
1、count()方法
用于统计字符串里某个字符或子字符串出现的次数。
s = "Hello, world! World is wonderful."
print(s.count("World")) # 输出 1,因为区分大小写
print(s.count("world")) # 输出 2,因为不区分大小写
2、replace()方法
用于替换字符串中的某个子字符串为另一个字符串。
s = "Hello, world! World is wonderful."
print(s.replace("world", "Python")) # 输出 "Hello, Python! World is wonderful."
print(s.replace("World", "Python", 1)) # 输出 "Hello, world! Python is wonderful.",只替换第一次出现的
3、split()方法
用于将字符串分割成一个列表,可以指定分隔符,默认为所有的空白字符,包括空格、换行(\n)、制表符(\t)等。
s = "Hello, world! World is wonderful."
print(s.split()) # 输出 ['Hello,', 'world!', 'World', 'is', 'wonderful.']
print(s.split(", ")) # 输出 ['Hello', 'world! World is wonderful.']
4、startswith()方法
用于检查字符串是否以指定的子字符串开始。
s = "Hello, world!"
print(s.startswith("Hello")) # 输出 True
print(s.startswith("world", 7)) # 输出 True,从索引7开始检查
5、endswith()方法
用于检查字符串是否以定的指子字符串结束。
s = "Hello, world!"
print(s.endswith("world!")) # 输出 True
print(s.endswith("Hello", 0, 5)) # 输出 True,检查索引0到5之间的子字符串
6、strip()方法
用于移除字符串头尾指定的字符(默认为空格)。
s = " Hello, world! "
print(s.strip()) # 输出 "Hello, world!",移除了头尾的空格
print(s.strip("! ")) # 输出 "Hello, world",移除了头尾的空格和感叹号
7、join()方法
用于将序列中的元素以指定的字符连接生成一个新的字符串。
s = "-"
seq = ["2023", "03", "15"]
print(s.join(seq)) # 输出 "2023-03-15"
print(" ".join(["Hello", "world!"])) # 输出 "Hello world!"
8、find()方法
用于查找子字符串在字符串中的位置,如果找不到子字符串,返回-1。
s = "Hello, world!"
print(s.find("world")) # 输出 7
print(s.find("Python")) # 输出 -1,因为找不到
9、index()方法
与 find() 类似,用于查找子字符串在字符串中的位置,不同的是,如果找不到子字符串,会抛出一个异常。
s = "Hello, world!"
print(s.index("world")) # 输出 7
# 下面的代码会抛出异常 ValueError: substring not found
# print(s.index("Python"))
第十二章 函数
到目前为止,Python的知识已经学得差不多了。
在Python中,函数是一种组织代码的方式,它允许你将代码块封装起来,使其可以重复使用。函数可以接收输入参数,并可以返回一个或多个结果。使用函数可以使代码更加模块化、易于阅读和维护。
函数的基本结构如下:
def function_name(parameters):
# 函数体
...
return result
-
def关键字:这是定义函数的关键字。 -
function_name:这是你给函数起的名字,应该具有描述性,以便了解函数的作用。 -
parameters:这些是传递给函数的变量,函数可以通过这些参数接收输入值。 -
函数体:这是函数的主要部分,包含了执行特定任务的代码。
-
return语句:函数使用它来返回一个值。当函数执行到return语句时,它会立即结束并返回指定的值。return语句不是必须的,如果没有它,函数默认返回None。
下面是一个简单的Python函数示例:
def greet(name):
return "Hello, " + name + "!"
print(greet("Python"))
# 输出:Hello, Python!
在这个例子中,greet是一个函数,它接受一个参数name,并返回一个问候语字符串。
一、声明函数和调用函数
1、声明函数
声明函数,或者说定义函数,就像是写一个食谱。你在食谱上写下制作某道菜所需要的原料和步骤,但是这个时候你还没有开始做菜。在Python中,你使用def关键字来声明一个函数,然后给它一个名字,后面跟着一对圆括号和一个冒号。圆括号里可以包含参数,参数就像是食谱中的原料,它们是函数工作时需要的信息。最后,你会在下一行缩进并写下函数应该执行的代码。
比如,我们来定义一个简单的函数,它的任务是打印出“Hello, World!”:
def say_hello():
print("Hello, World!")
这里,say_hello是函数的名字,而print("Hello, World!")是函数体,即函数被调用时会执行的代码。
2、调用函数
调用函数,就像是按照食谱做菜。你已经有了食谱(函数定义),现在你按照食谱来实际准备这道菜(执行函数)。在Python中,你只需要写下函数的名字和一对圆括号就可以调用这个函数了。如果函数需要参数,你还需要在圆括号中提供这些参数。
我们来调用上面定义的say_hello函数:
say_hello()
当你运行这行代码时,Python会执行say_hello函数中的代码,也就是打印出“Hello, World!”。
二、形参和实参
首先,我们需要明白函数是什么。函数可以看作是一段可重复使用的代码,它可以接收输入(我们称之为参数),并返回一个结果。
在Python中,函数的参数分为两种:形参和实参。
-
形参(Formal Parameters):形参是在定义函数时,函数名后面括号中的变量,它在函数被调用时才有具体的值。你可以把形参看作是一个占位符,它代表了将来要传入的一个值。比如在函数定义
def add(a, b):中,a和b就是形参。 -
实参(Actual Parameters):实参是在调用函数时,传递给函数的具体值。比如在函数调用
add(2, 3)中,2和3就是实参。
1、形参
举个例子,假设我们有一个函数,它的作用是计算两个数的和:
def add(a, b): # 这里的a和b就是形参
return a + b
当我们调用这个函数时,比如add(2, 3),这里的2和3就是实参,它们会被传递给函数,替换掉形参a和b,然后函数会执行return 2 + 3,最后返回结果5。
所以,简单来说,形参就像是函数的输入接口,它定义了函数可以接收什么样的输入;而实参就是我们实际给函数的输入,它决定了函数在具体执行时会如何运行。
2、实参
假设我们有一个函数,它用来打印一条欢迎信息,这个函数需要一个名字作为输入。
首先,我们定义这个函数,它有一个形参name:
def welcome_message(name): # 这里的name是形参
print(f"Welcome, {name}!")
现在,我们来调用这个函数,并传递一个实际的名字作为实参:
welcome_message("Alice") # 这里的"Alice"是实参
在这个例子中,当我们调用welcome_message函数时,我们传递了字符串"Alice"作为实参。这个实参会替换函数定义中的形参name,然后函数内部的代码会使用这个值来执行,打印出Welcome, Alice!。
我们也可以用不同的实参再次调用这个函数:
welcome_message("Bob") # 这里的"Bob"是另一个实参
这次,实参"Bob"被传递给了函数,结果会打印出Welcome, Bob!。
实参是在函数调用时确定的,它们是传递给函数的具体值,让函数能够用这些具体的值来执行操作。
三、函数参数
想象一下,你有一个机器,这个机器可以制作不同口味的冰淇淋。你告诉机器你想要什么口味,机器就会给你制作出来。在这个例子中,你告诉机器的口味信息就像是函数的参数。
在Python中,函数就像是一个小型的机器或者工具,它可以执行特定的任务。为了完成这个任务,你有时候需要给这个函数提供一些信息,这些信息就是参数。参数是你传递给函数的值,函数会用这些值来执行它的任务。
比如说,我们有一个函数叫做make_icecream,它的任务是制作冰淇淋。如果我们想要不同口味的冰淇淋,我们就需要告诉这个函数我们想要的口味。这里的口味就是一个参数。
在Python代码中,这可能看起来像这样:
def make_icecream(flavor):
print("制作一个" + flavor + "口味的冰淇淋!")
make_icecream("巧克力")
make_icecream("草莓")
在这个例子中,flavor是make_icecream函数的参数,当我们调用函数时,我们提供了一个实际的值(比如"巧克力"或"草莓"),这些实际的值被称为实参。函数会使用这个实参来执行它的任务,在这里就是打印出我们要制作的冰淇淋口味。
1、必须参数
“必须参数”(也称为“位置参数”)。想象一下,你去了一家餐厅点餐,服务员会问你要点什么。在这里,“要点什么”就是一个必须回答的问题,如果你不告诉服务员,他们就不知道该给你准备什么食物。
在Python中,函数也有类似的“问题”——这些就是必须参数。当你定义一个函数时,你可以指定一些参数,这些参数是调用该函数时必须提供的。如果你调用一个函数但没有提供所有必须的参数,Python会报错,因为它不知道缺失的参数应该是什么值。
这里有一个简单的例子:
def greet(name):
print(f"Hello, {name}!")
在这个例子中,greet 是一个函数,它有一个必须参数 name。当你调用这个函数时,你必须提供一个名字,比如 greet("Alice"),这样函数就会打印出 “Hello, Alice!”。如果你只写 greet() 而不提供任何参数,Python会报错,因为它期待你提供一个名字。
必须参数就像是一个函数的“订单”,你在调用函数时必须要“下单”,否则函数就不知道该怎么正确执行它的任务了。
2、关键字参数
关键字参数在函数中的作用可以类比于你在网上订购披萨时的选项。比如,你可能会说:“我想要一个大号披萨,加辣椒,不要洋葱。”在这个例子中,你不仅告诉了披萨店你想要什么,还指定了你的特殊要求。关键字参数就是这样,它们允许你在调用函数时指定参数的名称和值。
这样做的好处是,你不必担心参数的顺序,只要提供正确的名称,Python就能知道每个值对应哪个参数。此外,关键字参数还可以有默认值,这意味着如果你在调用函数时没有指定它们,它们就会使用默认值。
让我们来看一个例子:
def make_pizza(size, topping='cheese'):
print(f"Making a {size} pizza with {topping}.")
在这个 make_pizza 函数中,size 是一个必须参数,而 topping 是一个关键字参数,它有一个默认值 'cheese'。这意味着如果你在调用函数时没有指定 topping,它就会自动使用 'cheese'。
你可以这样调用函数:
make_pizza('large') # 使用默认的 topping
make_pizza('medium', topping='mushrooms') # 指定 topping
第一次调用中,我们只提供了 size 参数,所以 topping 将使用默认值 'cheese'。第二次调用中,我们使用了关键字参数的名称来明确指定 topping 的值。
关键字参数就像是你在订购东西时可以自定义的选项,它们让函数调用更灵活,更清晰,而且你还可以有默认值作为“备选”,以防你不想指定所有的细节。
3、默认参数
函数的参数可以有默认值,这种参数就被称为“默认参数”。默认参数的特点是,当我们调用函数时,如果没有为这个参数提供值,那么就会使用定义函数时给定的默认值。
假设你经营一家咖啡店,你的招牌是拿铁咖啡。大多数顾客都喜欢标准的拿铁咖啡,但是也有一些顾客可能会要求加糖或者加香草糖浆。为了满足所有顾客的需求,你可以设置一个默认的咖啡配方,但也允许顾客定制他们的咖啡。
在Python中,你可以这样定义一个函数来表示这个过程:
def make_coffee(size, flavor='latte'):
print(f"Making a {size} {flavor} coffee.")
在这个函数中,size 是一个必须参数,flavor 是一个默认参数,它的默认值是 'latte'。这意味着如果顾客没有指定咖啡的口味,那么就会制作拿铁咖啡。
你可以这样调用这个函数:
make_coffee('large') # 输出 "Making a large latte coffee."
make_coffee('medium', 'vanilla') # 输出 "Making a medium vanilla coffee."
第一次调用中,我们没有提供 flavor 参数,所以使用了默认的 'latte'。第二次调用中,我们提供了 flavor 参数,所以制作了香草口味的咖啡。
函数的默认参数就像是你的默认服务或产品,它可以满足大多数人的需求,但如果有人想要定制,你也可以提供这种可能性。这使得你的函数更加灵活,更能适应不同的使用场景。
2+3、关键字参数与默认参数的区别
关键字参数和默认参数是Python函数定义中的两个概念,它们有时会让人混淆,但实际上它们是有区别的。下面我会用简单的语言解释这两者的区别。
关键字参数(Keyword Arguments):
- 关键字参数指的是在调用函数时,你通过“参数名=值”的形式来指定参数,这样做的好处是不需要考虑参数的顺序。
- 你可以为任何参数提供关键字参数,无论它是否有默认值。
- 使用关键字参数可以提高代码的可读性。
例如:
def print_info(name, age):
print(f"Name: {name}, Age: {age}")
# 使用关键字参数调用函数
print_info(name="Alice", age=30)
print_info(age=30, name="Alice") # 参数顺序可以任意
默认参数(Default Arguments):
- 默认参数指的是在定义函数时,你可以为参数设置一个默认值,这样在调用函数时如果没有传递这个参数,就会自动使用默认值。
- 只有在函数定义时才指定默认值,调用时可以选择性地覆盖这个默认值。
例如:
def print_info(name, age=35):
print(f"Name: {name}, Age: {age}")
# 调用函数时没有提供age参数,将使用默认值35
print_info(name="Bob")
# 调用函数时提供了age参数,将覆盖默认值
print_info(name="Bob", age=40)
总结一下:
- 关键字参数是关于如何调用函数的,你可以使用参数名来指定参数值,这样做的好处是调用时不用考虑参数顺序。
- 默认参数是关于如何定义函数的,你可以为参数设置默认值,这样做的好处是调用时可以省略这些参数,让函数调用更简洁。
在实际使用中,关键字参数和默认参数经常一起使用,提供了函数调用的极大灵活性。
4、可变参数
可变参数允许你在调用函数时传递任意数量的参数,这就像是一个魔术盒子,你可以放入任意多的东西。
想象一下,你是一位聚会的组织者,你需要准备一份参加聚会的人员名单。问题是,你事先不知道会有多少人参加。在Python中,你可以使用可变参数来解决这个问题,它允许你创建一个函数,可以接受任意数量的参与者名字。
在Python中,你可以使用星号(*)来定义一个可变参数。这个星号意味着“把接下来的东西打包成一个元组(tuple)”,元组是Python中的一种数据结构,可以容纳多个值。
让我们来看一个例子:
def party_invite(*guests):
print("Party Invitation List:")
for guest in guests:
print(guest)
在这个 party_invite 函数中,*guests 就是一个可变参数。你可以传递任意数量的名字给这个函数。
party_invite('Alice', 'Bob', 'Charlie')
当你调用这个函数时,所有的名字都会被打包进一个元组,然后函数会遍历这个元组,打印出每个名字。
输出会是:
Party Invitation List:
Alice
Bob
Charlie
如果你没有朋友参加聚会,你也可以不传递任何参数:
party_invite()
这次没有输出名单,因为没有名字被传递给函数。
可变参数就像是一个可以根据需要扩展的容器,你可以根据实际情况放入任意多的内容。这在你需要处理不确定数量的输入时非常有用。
5、组合参数
在Python中,你可以在一个函数中使用多种类型的参数,包括必须参数、默认参数、可变参数等,这就是所谓的参数组合。
假设你正在组织一个生日派对,你需要知道派对的日期(必须参数),你可能还想知道是否需要提供素食选项(默认参数),并且你需要一份所有受邀请者的名单(可变参数)。
在Python中,你可以这样定义一个函数来表示这个过程:
def plan_party(date, vegetarian_option=False, *guests):
print(f"Party Date: {date}")
print(f"Vegetarian Option: {vegetarian_option}")
print("Guest List:")
for guest in guests:
print(guest)
在这个函数中,date 是一个必须参数,vegetarian_option 是一个默认参数,它的默认值是 False,*guests 是一个可变参数。
你可以这样调用这个函数:
plan_party('2022-12-31', True, 'Alice', 'Bob', 'Charlie')
输出会是:
Party Date: 2022-12-31
Vegetarian Option: True
Guest List:
Alice
Bob
Charlie
在这个例子中,我们提供了日期,选择了素食选项,并且提供了三个受邀请者的名字。
需要注意的是,参数的顺序很重要。在定义和调用函数时,必须遵循以下顺序:必须参数、默认参数、可变参数。这是因为Python解释器需要通过位置来识别参数,如果打乱了顺序,它可能会混淆。
参数组合就像是你在组织活动时需要考虑的各种因素,每种参数都有其特定的作用,通过合理地组合它们,你可以使你的函数更加灵活和强大。
四、嵌套调用
首先,我们需要明白什么是函数。函数就像是一个小工厂,你给它一些原料(这些原料在编程中被称为参数),它会根据你的指示处理这些原料,然后产出结果(也就是返回值)。
在Python中,我们可以在一个函数内部调用另一个函数,这就是所谓的函数嵌套调用。
让我们通过一个简单的例子来理解这个概念:
def add(a, b):
return a + b
def multiply(a, b):
return a * b
def calculate(a, b):
sum_result = add(a, b)
multiply_result = multiply(a, b)
return sum_result, multiply_result
在这个例子中,我们定义了三个函数:add,multiply和calculate。add函数接受两个参数并返回它们的和,multiply函数接受两个参数并返回它们的乘积。
calculate函数则是我们的主函数,它接受两个参数,然后在内部调用add和multiply函数处理这两个参数,并返回两个函数的结果。
这就是函数嵌套调用的基本概念。通过这种方式,我们可以将复杂的问题分解成一系列更简单的子问题,然后通过函数的方式分别解决这些子问题,最后再将结果组合起来,这大大提高了代码的可读性和可维护性。
五、全局变量与局部变量
在Python中,根据变量的作用范围,我们可以将变量分为全局变量和局部变量。
-
全局变量:全局变量是在函数外部定义的变量,它可以在程序的任何地方被访问。你可以把全局变量想象成一个公共的游泳池,无论你在哪里,都可以看到它,也可以跳进去游泳。
-
局部变量:局部变量是在函数内部定义的变量,它只能在该函数内部被访问。你可以把局部变量想象成你家的浴缸,只有在你家里,你才能看到它,也只有在你家里,你才能使用它。
让我们通过一个例子来理解这个概念:
# 这是一个全局变量
g_var = 10
def test():
# 这是一个局部变量
l_var = 5
print("全局变量g_var =", g_var)
print("局部变量l_var =", l_var)
test()
print("全局变量g_var =", g_var)
print("局部变量l_var =", l_var) # 这里会报错,因为l_var是局部变量,只能在test函数内部访问
在这个例子中,g_var是一个全局变量,我们可以在test函数内部和外部都可以访问它。而l_var是一个局部变量,我们只能在test函数内部访问它,当我们试图在函数外部访问它时,Python会抛出一个错误,告诉我们l_var在这里是未定义的。
六、函数返回值
在Python中,函数是用来执行特定任务的代码块。当函数完成其任务后,它可以返回一个值。这个值被称为函数的返回值。
你可以把函数想象成一个魔法机器:你把一些东西(这些东西在编程中被称为参数)放进去,然后机器会按照你的指示处理这些东西,最后吐出一个东西(也就是返回值)。
在Python中,我们使用return关键字来指定函数的返回值。如果函数没有return语句,那么函数的返回值默认为None。
让我们通过一个简单的例子来理解这个概念:
def add(a, b):
return a + b
result = add(3, 4)
print(result) # 输出:7
在这个例子中,add函数接受两个参数a和b,然后返回它们的和。当我们调用add(3, 4)时,函数会计算3和4的和,然后返回这个结果。我们可以将这个结果赋值给变量result,然后打印出来。
七、递归函数
递归函数是一种特殊的函数,它可以调用自身来解决问题。你可以把递归想象成一种自我复制的魔法。在递归函数中,你有一个基本情况(也称为停止条件),它会停止魔法,防止无限复制。同时,你也有一个递归情况,它会继续施展魔法,让函数继续调用自己,但每次都是以更接近基本情况的形式。
让我们用一个经典的例子来说明递归函数:计算阶乘。阶乘是一个数乘以它下面所有的数的乘积,例如5的阶乘是5 * 4 * 3 * 2 * 1。
def factorial(n):
# 基本情况:如果n等于1,我们知道1的阶乘就是1
if n == 1:
return 1
# 递归情况:否则,n的阶乘就是n乘以n-1的阶乘
else:
return n * factorial(n-1)
print(factorial(5)) # 输出:120
在这个factorial函数中,我们首先检查基本情况,即n是否为1。如果是,就返回1,因为1的阶乘是1。如果不是,我们就进入递归情况,函数调用自己,但这次是用n-1作为参数。这样每次递归调用都会使问题规模变小,直到达到基本情况。
八、匿名函数
在Python中,匿名函数是指那些没有明确名称的函数。我们通常使用lambda关键字来创建匿名函数。这种函数通常用于执行简单的任务,或者在需要将函数作为参数传递给另一个函数时。
你可以把匿名函数想象成一个快餐店里的快速小吃。它们是为了快速消费而设计的,通常只用一次,然后就扔掉了。
让我们通过一个例子来理解匿名函数:
# 使用lambda创建一个匿名函数,这个函数接受两个参数,并返回它们的和
add = lambda x, y: x + y
# 现在我们可以使用这个匿名函数
result = add(3, 5)
print(result) # 输出:8
在这个例子中,我们创建了一个匿名函数来计算两个数的和。lambda x, y: x + y这一行创建了一个匿名函数,其中x和y是参数,冒号后面的x + y是函数的返回值。
匿名函数通常用在需要函数作为参数的场合,比如高阶函数或者一些内置函数。例如,sorted函数可以接受一个函数作为参数来决定排序的方式:
# 假设我们有一个元组列表,我们想根据元组中第二个元素来排序这个列表
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
# 我们可以使用匿名函数作为sorted函数的key参数
pairs_sorted = sorted(pairs, key=lambda pair: pair[1])
print(pairs_sorted) # 输出:[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
在这个例子中,lambda pair: pair[1]创建了一个匿名函数,它告诉sorted函数根据列表中每个元组的第二个元素来排序。
匿名函数的优点是它们简洁,可以在不需要完整定义函数的情况下快速创建小函数。然而,由于它们的简洁性,匿名函数只能有一个表达式,这限制了它们的复杂性。对于更复杂的逻辑,我们通常还是会定义一个完整的函数。
第十三章 面向对象
一、什么是面向对象
面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式或者说是编程风格,它将重点放在创建对象而不是过程和函数上。在面向对象编程中,我们编写的程序被组织成对象的集合,个对象每都是某个类(Class)的实例(Instance)。
那么,什么是对象呢?你可以将对象想象成现实生活中的物体。比如,你的笔记本电脑是一个对象,它有一些属性(如品牌、颜色、价格等)和一些行为(如开机、关机、运行程序等)。在面向对象编程中,我们也定义对象,它们有属性(称为成员变量)和行为(称为方法)。
而类,你可以理解为对象的蓝图或者模板。比如,你的笔记本电脑是“笔记本电脑”这个类的一个实例。类定义了对象的结构,包括它的属性和方法。
面向对象编程的主要特点有三个,分别是封装、继承和多态。
-
封装:封装是指将数据(属性)和操作数据的方法绑定在一起,形成一个独立的对象。这样可以隐藏对象的内部实现细节,只对外提供有限的接口。
-
继承:继承是一种创建新类的方式,在新建的类中包含已存在的类的各种属性和行为。新建的类被称为子类,被继承的类被称为父类。子类除了继承父类的特性,还可以定义自己特有的特性。
-
多态:多态是指允许你使用父类的引用来指向子类的对象,从而改变自己的行为。这样,我们可以使多个类的对象都能接收相同的消息,这可以使类从细节代码中解耦出来。
让我们以一个简单的例子来说明Python中的面向对象编程。我们将创建一个名为"Student"的类,它代表一个学生。
class Student:
# 初始化方法或构造方法
def __init__(self, name, age):
self.name = name
self.age = age
# 方法
def study(self, course):
print(f'{self.name} is studying {course}.')
def birthday(self):
self.age += 1
print(f'{self.name} is now {self.age} years old.')
在这个例子中,"Student"是一个类,它有两个属性(name和age)和两个方法(study和birthday)。"init"是一个特殊的方法,称为类的初始化方法或构造方法,当我们创建类的新实例时,它会被自动调用。
现在,我们可以创建一个"Student"类的实例,并调用它的方法:
# 创建一个Student对象
bob = Student('Bob', 20)
# 调用对象的方法
bob.study('Python programming')
bob.birthday()
运行这段代码,你将看到以下输出:
Bob is studying Python programming.
Bob is now 21 years old.
二、类
在Python的面向对象编程中,类(Class)可以被理解为创建对象的模板或蓝图。你可以把它想象成是一个制造工厂,工厂里有制造产品的模具和流程,而产品就是这个类的实例,也就是对象。
类定义了一组属性(也就是数据)和方法(也就是函数)。属性是类的状态,方法是类的行为。比如,我们可以定义一个“汽车”类,它的属性可能包括品牌、颜色、速度等,它的方法可能包括启动、停止、加速等。
在Python中,我们使用关键字"class"来定义一个类。下面是一个简单的例子:
class Car:
# 初始化方法
def __init__(self, brand, color):
self.brand = brand
self.color = color
# 方法
def start(self):
print(f'The {self.color} {self.brand} is starting.')
def stop(self):
print(f'The {self.color} {self.brand} is stopping.')
在这个例子中,"Car"就是一个类,它有两个属性(brand和color)和两个方法(start和stop)。"init"是一个特殊的方法,称为类的初始化方法或构造方法,当我们创建类的新实例时,它会被自动调用。
现在,我们可以创建一个"Car"类的实例,并调用它的方法:
# 创建一个Car对象
my_car = Car('Tesla', 'red')
# 调用对象的方法
my_car.start()
my_car.stop()
运行这段代码,你将看到以下输出:
The red Tesla is starting.
The red Tesla is stopping.
三、对象
在现实世界中,你可以看到很多物体,比如一辆自行车、一本书、一台电脑等。这些物体都有自己的特征和功能。在编程中,我们用对象(Object)来模拟现实世界中的这些物体。
在Python的面向对象编程中,对象是类的实例。你可以把类比作是一个制造对象的模板,而对象就是根据这个模板制造出来的具体实例。每个对象都有自己的属性和方法,属性用来描述对象的特征,方法用来描述对象的行为。
让我们用一个简单的例子来说明:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
print(f"{self.name} says woof!")
在这个例子中,我们定义了一个名为Dog的类,它有两个属性:name和breed,还有一个方法:bark。
现在,我们可以用这个类来创建对象:
# 创建Dog类的一个对象
my_dog = Dog('Rex', 'Golden Retriever')
# 使用对象的方法
my_dog.bark()
当我们执行上面的代码时,我们创建了一个名为my_dog的对象,它是Dog类的一个实例。我们给它的name属性赋值为"Rex",breed属性赋值为"Golden Retriever"。然后我们调用了这个对象的bark方法,输出了"Rex says woof!"。
在这个例子中,my_dog就是一个对象,它具体代表了一个名叫Rex的金毛寻回犬。这个对象拥有Dog类定义的所有属性和方法,但它是一个具体的实例,拥有自己的属性值(Rex和Golden Retriever)。
四、魔法方法
在Python中,魔法方法(有时也被称为特殊方法)是一些内置的、以双下划线(__)开头和结尾的方法。它们有“魔法”之称,是因为你不需要直接调用它们,Python的解释器会在特定的时刻自动触发它们。这些方法允许我们实现和自定义对象的一些内置行为,比如加法运算、长度检查、字符串表示等。
让我们通过一些常见的魔法方法来了解它们是如何工作的:
-
__init__(self, ...)
这是一个初始化方法,当你创建一个类的新实例时,它会被自动调用。你可以在这个方法中设置对象初始化时的属性。 -
__str__(self)
当你尝试将对象转换为字符串时,比如使用print()函数时,这个方法会被调用。通常你会在这个方法中返回一个代表对象的友好字符串。 -
__repr__(self)
这个方法也是用于创建对象的字符串表示,但是它更侧重于开发者。它的目的是明确和一致,通常用于调试。 -
__add__(self, other)
当两个对象使用加号(+)运算符相加时,这个方法会被调用。你可以定义它来实现对象的自定义加法行为。 -
__len__(self)
当你使用内置的len()函数来获取对象的长度时,这个方法会被调用。
这些只是魔法方法的一小部分,Python中还有很多其他的魔法方法,用于不同的操作。
让我们用一个简单的例子来说明魔法方法的使用:
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return f"{self.title} by {self.author}"
def __len__(self):
return self.pages
def __del__(self):
print(f"The book {self.title} has been deleted from memory.")
在这个Book类中,我们定义了三个魔法方法:
__init__用于创建新的书籍实例。__str__用于打印书籍的标题和作者。__len__用于返回书籍的页数。__del__在对象被删除时调用,可以用来执行清理工作。
现在,我们可以创建一个Book类的实例,并使用这些魔法方法:
# 创建Book类的一个对象
my_book = Book('Python Programming', 'tan91', 500)
# 使用魔法方法
print(my_book) # 输出: Python Programming by tan91
print(len(my_book)) # 输出: 500
# 删除对象
del my_book # 输出: The book Python Programming has been deleted from memory.
五、类的访问权限
在Python中,类的访问权限涉及到类的属性和方法是否可以被外部直接访问。这是一种封装的概念,封装是面向对象编程的三大特性之一(另外两个是继承和多态)。封装的目的是确保对象的内部状态被保护,只能通过定义好的接口进行交互,这样可以防止外部代码随意改变对象内部的状态,从而可能导致错误。
在Python中,访问权限分为三种类型:
-
公有(Public):如果一个类的属性或方法被声明为公有,那么它可以被类的外部访问。在Python中,默认情况下,所有的类成员都是公有的。
-
私有(Private):如果你不希望类的属性或方法被外部访问,你可以将其声明为私有。在Python中,你可以通过在属性或方法名前加上两个下划线(
__)来声明它们为私有。 -
受保护的(Protected):受保护的属性或方法可以被类本身和子类访问,但不应该被外部直接访问。在Python中,受保护的成员可以通过在属性或方法名前加上一个下划线(
_)来声明。
让我们通过一个例子来说明这些概念:
class Car:
def __init__(self, brand, max_speed):
self.brand = brand # 公有属性
self.__max_speed = max_speed # 私有属性
def drive(self):
print(f"This {self.brand} is driving.")
def __update_software(self): # 私有方法
print("Updating software.")
def set_max_speed(self, speed):
if speed > 0:
self.__max_speed = speed
def get_max_speed(self):
return self.__max_speed
在这个Car类中:
brand是一个公有属性,可以被类的外部访问。__max_speed是一个私有属性,它不能被类的外部直接访问。我们通过公有方法set_max_speed和get_max_speed来修改和获取最大速度,这样我们就可以在这些方法中加入逻辑来保护__max_speed不被设置成无效值。drive是一个公有方法,可以被任何人调用。__update_software是一个私有方法,它只能在类的内部被调用,比如可以在drive方法中调用它。
1、私有属性
class Dog():
def __init__(self):
self.name='3月'
# 私有属性
self.__age=12
dog=Dog()
print(dog.name)
print(dog.__age)
# 输出:
# 3月
# Traceback (most recent call last):
# File "xxxxxxxx", line 9, in <module>
# print(dog.__age)
# AttributeError: 'Dog' object has no attribute '__age'
再例如:
class Dog():
def __init__(self):
self.name='3月'
# 私有属性
self.__age=12
def show_age(self):
print(self.__age)
dog=Dog()
dog.name='哈士奇'
dog.__age=13
print(dog.name)
print(dog.__age)
dog.show_age()
# 输出:
# 哈士奇
# 13
# 12
class Dog():
def __init__(self):
self.name='3月'
# 私有属性
self.__age=12
def show_age(self):
print(self.__age)
# 利用公有方法给私有属性赋值
def set_age(self, age):
# self.__age=age
if age > 0:
self.__age=age
# 利用共有方法返回私有属性的值
def get_age(self):
return self.__age
dog=Dog()
dog.set_age(-1)
print(dog.get_age())
我们可以利用共有方法可以对私有属性的值进行校验。
私有属性可以避免子类直接访问和名字冲突的问题。
2、私有方法
class Tmall():
def __buy(self): # 私有方法
return "购买成功"
# 利用公有方法调用私有方法
def check_money(self,money):
if money < 100:
print("金额不足")
else:
return self.__buy()
t=Tmall()
# print(t.__buy())
print(t.check_money(200))
私有方法可以直接避免被调用,也影响子类调用
六、类的属性
在Python的面向对象编程中,类的属性是用来描述类的状态或特性的。你可以把它们看作是类的变量,它们存储了关于对象的信息。比如,如果我们有一个类叫做Dog,那么这个类可能有一些属性,如name(名字)、breed(品种)和age(年龄)。
在Python中,类的属性可以分为两种:实例属性和类属性。
-
实例属性:这些属性是每个对象或实例特有的。每个对象都有自己的实例属性。我们通常在类的方法中,特别是在
__init__方法中定义实例属性。 -
类属性:这些属性是类的所有实例共享的。类属性定义在类定义的内部,但在任何方法之外。
让我们通过一个例子来理解这两种属性:
class Dog:
# 类属性
species = "Canis familiaris"
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
在这个例子中,species是一个类属性,所有的Dog实例都会共享这个属性。name和age是实例属性,每个Dog实例都有自己的name和age。
我们可以这样创建Dog类的实例,并访问它们的属性:
# 创建Dog类的实例
dog1 = Dog("Buddy", 9)
dog2 = Dog("Rex", 3)
# 访问实例属性
print(dog1.name) # 输出: Buddy
print(dog2.age) # 输出: 3
# 访问类属性
print(Dog.species) # 输出: Canis familiaris
print(dog1.species) # 输出: Canis familiaris
print(dog2.species) # 输出: Canis familiaris
注意,尽管我们可以通过实例来访问类属性,但是我们不能通过实例来修改类属性。如果我们试图通过实例来修改类属性,Python实际上会为那个实例创建一个新的实例属性。
七、静态方法
在Python的面向对象编程中,我们通常在类中定义方法,这些方法可以访问和操作类的实例属性。然而,有时候我们可能需要在类中定义一些与类的实例无关的方法,这就是静态方法的用武之地。
静态方法是一种特殊的方法,被@staticmethod装饰器标记。它不接收特殊的第一个参数(如self或cls),这意味着它不能访问类的实例属性或类属性。由于这个限制,静态方法可以看作是属于类的普通函数。
静态方法的一个常见用途是作为工具函数,当你需要执行一些与类的实例和类本身无关的任务时,可以使用静态方法。
让我们通过一个例子来理解静态方法:
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
在这个例子中,MathUtils类有两个静态方法:add和multiply。这两个方法都不需要访问任何实例或类属性,所以它们被定义为静态方法。
我们可以这样使用这些静态方法:
print(MathUtils.add(5, 3)) # 输出: 8
print(MathUtils.multiply(5, 3)) # 输出: 15
注意,我们没有创建MathUtils的实例,而是直接在类上调用了静态方法。
八、类方法
在Python中,除了我们常见的实例方法和静态方法之外,还有一种叫做类方法的方法。类方法与静态方法相似,但有一个关键的区别:类方法接收一个参数,通常命名为cls,它指向类本身,而不是类的实例。这意味着类方法可以访问类属性,但不能访问实例属性。
类方法使用@classmethod装饰器来标识。它们通常用于以下场景:
-
当你需要一个方法,它操作类属性或需要访问类本身的信息时。
-
当你需要一个工厂方法,用于创建类的实例。工厂方法是一种设计模式,用于创建对象,而不需要指定将要创建的对象的具体类。
让我们通过一个例子来理解类方法:
class Employee:
raise_amount = 1.04 # 类属性,全体员工的年薪涨幅
def __init__(self, first, last, pay):
self.first = first # 实例属性
self.last = last # 实例属性
self.pay = pay # 实例属性
@classmethod
def set_raise_amount(cls, amount):
cls.raise_amount = amount # 访问类属性
@classmethod
def from_string(cls, emp_str):
first, last, pay = emp_str.split('-')
return cls(first, last, int(pay)) # 创建并返回一个新的实例
# 使用类方法修改类属性
Employee.set_raise_amount(1.05)
# 使用类方法创建一个新的Employee实例
emp_str = "John-Doe-70000"
new_emp = Employee.from_string(emp_str)
print(new_emp.first) # 输出: John
print(new_emp.pay) # 输出: 70000
在这个例子中,set_raise_amount是一个类方法,它接收cls作为第一个参数,并用它来修改类属性raise_amount。from_string也是一个类方法,它接收一个字符串,然后解析这个字符串并用解析后的数据来创建一个新的Employee实例。
注意,我们没有创建Employee的实例来调用这些方法。相反,我们直接在类上调用了类方法。
九、属性方法
在Python的面向对象编程中,属性方法(也称为属性器)是一种特殊类型的方法,它使得你可以将一个类的方法访问看起来像是访问一个属性。这种方法使用@property装饰器来创建。
属性方法的一个常见用途是,当你想要提供一个只读属性,或者在设置属性值时需要执行一些额外的操作时。使用属性方法可以让你在不改变类接口的情况下,增加一些额外的逻辑处理。
让我们通过一个例子来理解属性方法:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""获取圆的半径"""
return self._radius
@radius.setter
def radius(self, value):
"""设置圆的半径,同时确保半径为正数"""
if value >= 0:
self._radius = value
else:
raise ValueError("半径必须是正数")
@property
def area(self):
"""计算并返回圆的面积"""
return 3.14159 * self._radius ** 2
在这个Circle类中:
- 我们有一个私有属性
_radius,它是圆的半径。 radius方法被@property装饰器装饰,这使得我们可以像访问属性一样访问radius方法(例如,通过c.radius来获取半径,其中c是Circle类的一个实例)。radius方法还有一个对应的setter方法,它允许我们设置半径的值,同时确保半径不会被设置成负数。这个setter方法也被@radius.setter装饰器装饰。area方法同样被@property装饰器装饰,它计算并返回圆的面积。注意,area属性是只读的,因为我们没有为它定义setter方法。
现在,我们可以这样使用这个类:
c = Circle(5)
print(c.radius) # 输出: 5
print(c.area) # 输出: 78.53975
c.radius = 10 # 设置新的半径
print(c.radius) # 输出: 10
print(c.area) # 输出: 314.159
# 下面这行将会抛出错误,因为我们试图设置一个负数半径
# c.radius = -2 # 抛出ValueError
通过使用属性方法,我们提供了一个简洁的接口来访问和设置半径,同时还能在设置半径时加入验证逻辑,而不需要调用者直接调用一个方法。
十、封装
在Python的面向对象编程中,封装是一种隐藏对象内部实现细节的机制,只对外提供有限的接口进行交互。封装的主要目的是增强安全性并简化编程,用户只需要知道对象提供了哪些方法,而不需要知道这些方法是如何实现的。
封装有两个主要方面:
-
将数据(属性)和行为(方法)包装到单个实体(即类)中。当你创建一个对象(类的实例)时,你可以使用这个对象的方法来操作它的数据。
-
隐藏类的内部实现细节,只对外提供有限的访问接口。在Python中,我们通常使用私有属性和私有方法来实现这一点。
让我们通过一个例子来理解封装:
class BankAccount:
def __init__(self, balance=0):
self._balance = balance # 私有属性,存储账户余额
def deposit(self, amount):
"""存款"""
if amount > 0:
self._balance += amount
return self._balance
else:
raise ValueError("存款金额必须是正数")
def withdraw(self, amount):
"""取款"""
if 0 < amount <= self._balance:
self._balance -= amount
return self._balance
else:
raise ValueError("取款金额必须是正数且不能超过账户余额")
def check_balance(self):
"""查看账户余额"""
return self._balance
在这个BankAccount类中:
- 我们有一个私有属性
_balance,它存储了账户的余额。这个属性被封装在类内部,外部不能直接访问。 - 我们提供了三个公共方法(
deposit、withdraw和check_balance)来操作账户余额。这些方法是类对外提供的接口,用户可以通过这些方法来存款、取款和查看余额,而不需要直接操作_balance属性。
十一、继承
在Python的面向对象编程中,继承是一种创建新类的方式,新创建的类可以继承一个或多个父类的属性和方法。继承使得我们可以重用代码,并建立起类之间的关系。
继承的关键好处包括:
-
代码重用:子类可以继承父类的方法和属性,无需重新编写相同的代码。
-
创建层次结构:通过继承,我们可以创建一个层次结构,这有助于组织和管理代码,以及理解不同类之间的关系。
-
可扩展性:如果需要在现有类的基础上添加新的功能,可以通过继承创建子类,而不是修改原有的类。
让我们通过一个例子来理解继承:
# 这是一个父类(基类)
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def start_engine(self):
print("引擎启动了。")
# 这是一个继承自Vehicle的子类
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model) # 调用父类的构造方法
self.doors = doors # 新增属性
def lock_doors(self):
print("车门上锁了。")
# 创建Car类的实例
my_car = Car("Toyota", "Corolla", 4)
# 我们可以使用父类Vehicle的方法
my_car.start_engine() # 输出: 引擎启动了。
# 我们也可以使用子类Car新增的方法
my_car.lock_doors() # 输出: 车门上锁了。
在这个例子中,Vehicle是一个基类,它有基本的属性和方法。Car是从Vehicle继承而来的子类,它继承了Vehicle的所有属性和方法,并添加了自己的属性doors和方法lock_doors。
使用super().__init__(brand, model),Car类的构造方法调用了其父类Vehicle的构造方法,这样Car实例就包含了brand和model属性。
通过继承,我们可以创建一个通用的父类,然后通过子类来扩展或定制额外的功能。这样做可以使代码更加模块化,易于理解和维护。
十二、调用父类方法
在Python的面向对象编程中,调用父类方法是一个常见的操作,尤其是在使用继承时。当我们创建一个子类并继承自一个父类,我们有时候想要扩展或者修改父类的行为。在这种情况下,我们可能需要调用父类的方法。
调用父类方法可以使用super()函数。super()函数返回一个代表父类的临时对象,通过这个临时对象,我们可以调用父类的方法。
这里有一个简单的例子来说明如何调用父类方法:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现这个方法")
# Dog类继承自Animal类
class Dog(Animal):
def speak(self):
# 调用父类Animal的speak方法
super().speak()
# 添加Dog类特有的行为
print(f"{self.name} says Woof!")
# 创建Dog类的实例
dog = Dog("Buddy")
dog.speak() # Buddy says Woof!
在这个例子中,Animal是一个父类,它有一个name属性和一个speak方法。speak方法在Animal类中并没有实现具体的功能,而是抛出了一个NotImplementedError,这是一种常见的做法,用来指示这个方法应该在子类中被覆盖和实现。
Dog类继承自Animal类,并覆盖了Animal类的speak方法。在Dog类的speak方法中,我们首先调用了父类的speak方法,这是通过super().speak()实现的。虽然在这个例子中父类的speak方法并没有做任何事情,但是在实际的应用中,父类的方法可能会执行一些重要的初始化操作或者设置一些必要的状态。
然后,Dog类添加了自己特有的行为,打印出狗叫的声音。
使用super()调用父类方法的好处是,即使在多重继承的情况下,也能正确地处理方法调用顺序。此外,如果将来更改了类的继承关系,使用super()可以确保调用的正确性,因为它会自动解析父类。
十三、多态
在Python的面向对象编程中,多态是指不同的对象可以对同一个方法调用做出不同的响应。换句话说,多态允许我们用一个统一的接口来操作不同的基础形式(数据类型)。
多态的关键点在于,子类可以有自己独特的行为,但是也可以被视为是父类的一个实例。这意味着子类的对象可以被当作父类的对象来使用,而实际上它们可能会执行不同的方法。
这里有一个简单的例子来说明多态:
class Animal:
def speak(self):
pass # 定义一个空的speak方法,具体实现将在子类中定义
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def animal_sound(animal):
print(animal.speak())
# 创建Dog和Cat类的实例
dog = Dog()
cat = Cat()
# 调用函数,传入不同的对象
animal_sound(dog) # 输出: Woof!
animal_sound(cat) # 输出: Meow!
在这个例子中,我们定义了一个父类Animal和两个子类Dog和Cat。每个子类都有自己的speak方法实现,分别返回不同的叫声。
我们还定义了一个函数animal_sound,它接受一个Animal类型的对象作为参数,并调用该对象的speak方法。当我们分别传入Dog和Cat对象时,尽管animal_sound函数不知道具体的对象类型,但是多态允许它正确地调用对应对象的speak方法,输出正确的叫声。
多态的好处是,我们可以编写出更加通用和灵活的代码。我们可以定义一个接口或者使用一个父类方法,然后通过传入不同的子类对象来获得不同的行为。这样,我们的代码可以在不知道具体对象类型的情况下,对不同类型的对象进行操作。
十四、isinstance()
在Python中,isinstance()是一个内置函数,用来检查一个对象是否是一个类的实例,或者是一个子类的实例。这个函数非常有用,因为它可以让我们在运行时检查对象的类型。
isinstance()函数的使用非常简单,它接受两个参数:第一个参数是要检查的对象,第二个参数是一个类或者包含多个类的元组。如果对象是给定类或元组中任一类的实例,或者是这些类的子类的实例,isinstance()会返回True,否则返回False。
让我们通过一个例子来理解isinstance()的用法:
class Fruit:
pass
class Apple(Fruit):
pass
class Banana(Fruit):
pass
# 创建Apple和Banana类的实例
apple = Apple()
banana = Banana()
# 检查apple是否是Apple类的实例
print(isinstance(apple, Apple)) # 输出: True
# 检查apple是否是Fruit类的实例
print(isinstance(apple, Fruit)) # 输出: True
# 检查banana是否是Apple类的实例
print(isinstance(banana, Apple)) # 输出: False
# 检查banana是否是Fruit类的实例
print(isinstance(banana, Fruit)) # 输出: True
# 检查apple是否是Apple或Banana类的实例
print(isinstance(apple, (Apple, Banana))) # 输出: True
在这个例子中,我们定义了一个父类Fruit和两个子类Apple和Banana。我们创建了一个Apple类的实例apple和一个Banana类的实例banana。
使用isinstance()函数,我们可以检查apple是否是Apple类的实例,结果是True。同样,因为Apple是Fruit的子类,所以apple也是Fruit类的实例,isinstance(apple, Fruit)也返回True。但是,banana不是Apple类的实例,所以isinstance(banana, Apple)返回False。最后,我们检查apple是否是Apple或Banana类的实例,因为apple是Apple的实例,所以结果是True。
isinstance()函数在编写需要处理多种类型的对象时特别有用,它可以帮助我们确保对象是正确的类型,或者在需要时执行类型特定的操作。
十五、单例模式
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在Python中实现单例模式可以有多种方法,但核心思想都是相同的:确保一个类在整个程序运行期间只创建一个实例。
让我们用一个简单的例子来说明单例模式:
想象一下,你有一个学校,而这个学校在任何时候只能有一个校长。不管你尝试多少次去“创建”一个校长,你都会得到同一个人。这就是单例模式的概念。
在Python中,我们可以通过定义一个类来确保这个类只有一个实例,并且提供一个方法来创建或获取这个实例。这里有一个常见的单例实现方式:
class Singleton:
_instance = None # 创建一个类变量,用于存储单例实例
def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
# 这里可以添加任何你想在创建时执行的代码
return cls._instance
# 使用Singleton类创建对象
singleton1 = Singleton()
singleton2 = Singleton()
# 检查两个对象是否相同
print(singleton1 is singleton2) # 输出: True
在这个例子中,我们定义了一个名为Singleton的类。这个类有一个类变量_instance,用于存储这个类的唯一实例。我们覆盖了__new__方法,这是一个特殊的方法,它在创建对象之前被调用。在__new__方法中,我们检查_instance是否为None,如果是,说明还没有创建实例,我们就创建一个新的实例并将其存储在_instance变量中。如果_instance已经有了值,我们直接返回这个值。
当我们尝试创建Singleton的多个实例时,无论我们创建多少次,_instance都会返回同一个实例,这就保证了我们只有一个Singleton实例。
单例模式在某些情况下非常有用,比如当你需要控制资源的访问,或者当你想要所有的请求都通过一个共享的资源时。然而,它也可能导致代码之间的耦合增加,并且在并发环境中可能需要额外的同步机制。
练习1
题目:设计一个简单的学生信息管理系统
要求:
-
使用面向对象的编程方式,设计一个学生类,包含属性:姓名、年龄、性别、学号,使用魔法方法__init__初始化这些属性。
-
在学生类中,设计一个私有方法,用于打印学生的详细信息。
-
设计一个公有方法,用于调用这个私有方法,打印学生的详细信息。
-
设计一个静态方法,用于验证输入的学号是否符合规范(例如,长度为8位)。
-
设计一个类方法,用于统计创建的学生对象的总数。
-
设计一个属性方法,用于返回学生的姓名和学号。
-
设计一个函数,接受学生对象作为参数,调用学生对象的方法,打印学生的详细信息。
-
设计一个函数,接受一个学生列表作为参数,调用学生对象的方法,打印所有学生的详细信息。
-
设计一个函数,接受一个学生对象和一个学生列表作为参数,将学生对象添加到学生列表中。
-
设计一个函数,接受一个学生对象和一个学生列表作为参数,从学生列表中删除指定的学生对象。
-
使用递归函数,实现对学生列表的排序(可以按照学号排序)。
-
使用匿名函数,实现对学生列表的排序(可以按照年龄排序)。
class Student:
# 类属性,用于统计学生数量
student_count = 0
def __init__(self, name, age, gender, student_id):
self.name = name
self.age = age
self.gender = gender
self.student_id = student_id
Student.student_count += 1
# 私有方法,打印学生详细信息
def __print_info(self):
print(f"Name: {self.name}, Age: {self.age}, Gender: {self.gender}, Student ID: {self.student_id}")
# 公有方法,调用私有方法打印学生信息
def display_student(self):
self.__print_info()
# 静态方法,验证学号是否符合规范
@staticmethod
def validate_student_id(student_id):
return len(student_id) == 8
# 类方法,返回学生总数
@classmethod
def get_student_count(cls):
return cls.student_count
# 属性方法,返回学生姓名和学号
@property
def student_info(self):
return f"{self.name}, {self.student_id}"
# 函数,打印单个学生信息
def print_student_info(student):
student.display_student()
# 函数,打印所有学生信息
def print_all_students_info(students_list):
for student in students_list:
student.display_student()
# 函数,添加学生到列表
def add_student_to_list(student, students_list):
students_list.append(student)
# 函数,从列表中删除学生
def remove_student_from_list(student, students_list):
students_list.remove(student)
# 递归函数,排序学生列表(按学号)
def sort_students_by_id(students_list):
if len(students_list) <= 1:
return students_list
else:
pivot = students_list[0]
lesser = sort_students_by_id([x for x in students_list[1:] if x.student_id < pivot.student_id])
greater = sort_students_by_id([x for x in students_list[1:] if x.student_id >= pivot.student_id])
return lesser + [pivot] + greater
# 匿名函数,排序学生列表(按年龄)
sort_students_by_age = lambda students_list: sorted(students_list, key=lambda x: x.age)
# 测试代码
students = []
s1 = Student("Alice", 20, "Female", "12345678")
s2 = Student("Bob", 22, "Male", "87654321")
add_student_to_list(s1, students)
add_student_to_list(s2, students)
print("All students info:")
print_all_students_info(students)
print("\nSorted by student ID:")
sorted_by_id = sort_students_by_id(students)
print_all_students_info(sorted_by_id)
print("\nSorted by age:")
sorted_by_age = sort_students_by_age(students)
print_all_students_info(sorted_by_age)
print("\nStudent count:", Student.get_student_count())
输出:
All students info:
Name: Alice, Age: 20, Gender: Female, Student ID: 12345678
Name: Bob, Age: 22, Gender: Male, Student ID: 87654321
Sorted by student ID:
Name: Alice, Age: 20, Gender: Female, Student ID: 12345678
Name: Bob, Age: 22, Gender: Male, Student ID: 87654321
Sorted by age:
Name: Alice, Age: 20, Gender: Female, Student ID: 12345678
Name: Bob, Age: 22, Gender: Male, Student ID: 87654321
Student count: 2
解析:
首先,我们定义了一个名为Student的类,这个类有四个属性:name、age、gender和student_id。这些属性在__init__方法中被初始化,__init__方法是一个特殊的方法,当我们创建一个新的Student对象时,它会被自动调用。
class Student:
def __init__(self, name, age, gender, student_id):
self.name = name
self.age = age
self.gender = gender
self.student_id = student_id
接着,我们在Student类中添加了一个私有方法__print_info,这个方法会打印学生的详细信息。私有方法的名称以两个下划线开始,这意味着它只能在类的内部被调用,不能在类的外部被直接调用。
def __print_info(self):
print(f"Name: {self.name}, Age: {self.age}, Gender: {self.gender}, Student ID: {self.student_id}")
然后,我们添加了一个公有方法display_student,这个方法会调用私有方法__print_info,打印学生的详细信息。公有方法可以在类的外部被直接调用。
def display_student(self):
self.__print_info()
接下来,我们添加了一个静态方法validate_student_id,这个方法会检查学号是否符合规范。静态方法使用@staticmethod装饰器定义,它们不需要访问类的任何属性或方法,只需要处理它们的输入参数。
@staticmethod
def validate_student_id(student_id):
return len(student_id) == 8
然后,我们添加了一个类方法get_student_count,这个方法会返回创建的学生对象的总数。类方法使用@classmethod装饰器定义,它们可以访问类的属性和方法,但不能访问实例的属性和方法。
@classmethod
def get_student_count(cls):
return cls.student_count
最后,我们添加了一个属性方法student_info,这个方法会返回学生的姓名和学号。属性方法使用@property装饰器定义,它们可以像访问属性一样被调用,而不需要加上括号。
@property
def student_info(self):
return f"{self.name}, {self.student_id}"
在定义了Student类之后,我们定义了一些函数来操作学生对象和学生列表,包括打印学生信息、添加和删除学生、以及排序学生列表。这些函数都是普通的函数,不属于任何类。
最后,我们创建了一些Student对象,并使用我们定义的函数来操作这些对象和列表。这部分代码主要用于测试我们的类和函数是否能够正常工作。
练习2
题目:设计一个简单的学生信息管理系统
要求:
-
使用面向对象的编程方式,设计一个学生类(Student),包含属性:姓名(name),年龄(age),成绩(score)。
-
在学生类中,定义一个方法get_info(),用于打印学生的信息。
-
在学生类中,定义一个方法set_score(score),用于设置学生的成绩。
-
设计一个学生管理系统类(StudentManager),包含属性:学生列表(students)。
-
在学生管理系统类中,定义一个方法add_student(),用于添加学生。
-
在学生管理系统类中,定义一个方法delete_student(name),用于根据学生姓名删除学生。
-
在学生管理系统类中,定义一个方法modify_score(name, score),用于修改学生的成绩。
-
在学生管理系统类中,定义一个方法show_all_students(),用于显示所有学生的信息。
-
在学生管理系统类中,定义一个方法show_student(name),用于显示特定学生的信息。
-
在主函数中,创建学生管理系统对象,添加几个学生,然后调用相关方法测试学生管理系统的功能。
注意:要考虑到各种可能的异常情况,比如删除不存在的学生,修改不存在的学生的成绩等。
class Student:
def __init__(self, name, age, score):
self.name = name # 学生姓名
self.age = age # 学生年龄
self.score = score # 学生成绩
def get_info(self):
# 打印学生信息
print(f"Name: {self.name}, Age: {self.age}, Score: {self.score}")
def set_score(self, score):
# 设置学生成绩
self.score = score
class StudentManager:
def __init__(self):
self.students = [] # 学生列表
def add_student(self, name, age, score):
# 添加学生
student = Student(name, age, score)
self.students.append(student)
def delete_student(self, name):
# 删除学生
for i, student in enumerate(self.students):
if student.name == name:
del self.students[i]
return
print("Student not found.") # 如果学生不存在,打印提示信息
def modify_score(self, name, score):
# 修改学生成绩
for student in self.students:
if student.name == name:
student.set_score(score)
return
print("Student not found.") # 如果学生不存在,打印提示信息
def show_all_students(self):
# 显示所有学生信息
for student in self.students:
student.get_info()
def show_student(self, name):
# 显示特定学生信息
for student in self.students:
if student.name == name:
student.get_info()
return
print("Student not found.") # 如果学生不存在,打印提示信息
def main():
# 主函数
manager = StudentManager() # 创建学生管理系统对象
manager.add_student("Alice", 20, 85) # 添加学生
manager.add_student("Bob", 21, 90) # 添加学生
manager.show_all_students() # 显示所有学生信息
manager.modify_score("Alice", 95) # 修改学生成绩
manager.show_student("Alice") # 显示特定学生信息
manager.delete_student("Bob") # 删除学生
manager.show_all_students() # 显示所有学生信息
if __name__ == "__main__":
main() # 运行主函数
第十四章 文件操作
一、文件打开与关闭
1、open()与close()
文件操作是一个非常实用的技能,它允许你的程序读取和写入文件。这就像是你要读一本书或者在日记本上写东西,首先你需要做的是打开它。
在Python中,我们使用内置的open函数来打开一个文件。这个函数需要至少一个参数:文件的路径和名称。例如,如果你想打开一个名为example.txt的文件,你可以这样写:
file = open('example.txt')
这行代码就像是告诉Python:“嘿,去找一个叫做example.txt的文件,并且打开它。”
当你打开文件时,你可以指定模式,即你想要做什么。最常见的模式有:
'r':读取模式。这是默认模式,用于读取并返回文件的内容。如果文件不存在,Python会抛出一个错误。'w':写入模式。用于写入文件。如果文件已经存在,它会被覆盖。如果文件不存在,Python会创建一个新文件。'a':追加模式。用于在文件的末尾添加新的内容。如果文件不存在,Python会创建一个新文件。'x':创建模式。用于创建一个新文件。如果文件已经存在,Python会抛出一个错误。'b':二进制模式。用于读取或写入二进制文件,如图像或音频文件。't':文本模式。用于读取或写入文本文件。这是默认模式,通常可以省略。'+':读写模式。用于同时读取和写入文件。可以与其他模式结合使用,如'r+'、'w+'、'a+'。
比如,如果你想要写入文件,你可以这样做:
file = open('example.txt', 'w')
这就像是告诉Python:“找到这个example.txt文件,如果它还不存在,就创建一个新的,然后准备写入一些东西。”
记得,当你完成文件操作后,应该总是关闭文件。这就像是读完书后要关上书本,以确保其他人也可以使用它。在Python中,你可以这样关闭文件:
file.close()
这样,你就不会遗留打开的文件,这是一个好习惯,可以避免潜在的问题,比如文件损坏或者资源泄露。
Python中
open函数的文件打开的更多模式:
-
'rb':二进制读取模式。用于读取二进制文件,如图像或音频文件。 -
'wb':二进制写入模式。用于写入二进制文件。如果文件已经存在,它会被覆盖。如果文件不存在,Python会创建一个新文件。 -
'ab':二进制追加模式。用于在二进制文件的末尾添加新的内容。如果文件不存在,Python会创建一个新文件。 -
'r+':读写模式。文件必须存在,否则会抛出一个错误。可以读取文件内容,也可以在文件任意位置写入新的内容。 -
'w+':读写模式。如果文件存在,它会被覆盖。如果文件不存在,Python会创建一个新文件。可以读取文件内容,也可以写入新的内容。 -
'a+':读写追加模式。如果文件存在,写入的内容会被添加到文件末尾。如果文件不存在,Python会创建一个新文件。可以读取文件内容,也可以写入新的内容。 -
'rb+':二进制读写模式。文件必须存在,否则会抛出一个错误。可以读取二进制文件内容,也可以在文件任意位置写入新的二进制内容。 -
'wb+':二进制读写模式。如果文件存在,它会被覆盖。如果文件不存在,Python会创建一个新文件。可以读取二进制文件内容,也可以写入新的二进制内容。 -
'ab+':二进制读写追加模式。如果文件存在,写入的二进制内容会被添加到文件末尾。如果文件不存在,Python会创建一个新文件。可以读取二进制文件内容,也可以写入新的二进制内容。
这些模式为Python提供了强大的文件操作能力,使其能够处理各种不同的文件操作需求。
二、文件读写
1、read()方法
想象一下你在用吸管喝一杯奶昔。你可以选择一次性把整杯奶昔都吸完,也可以只吸一小口,慢慢享受。在Python中读取文件的read()方法也是类似的。
当你打开一个文件后,你可以使用read()方法来读取文件的内容。这里有两种方式:
- 不带参数的
read():这就像是一次性把整杯奶昔都吸完。当你调用read()不带任何参数时,它会读取文件中的所有内容,并将其作为一个大字符串返回给你。如果文件很大,这可能会占用很多内存。
file = open('example.txt', 'r')
content = file.read() # 读取整个文件内容
print(content) # 打印文件内容
file.close() # 关闭文件
- 带参数的
read(size):这就像是每次只吸一小口奶昔。你可以指定一个size参数,它代表你想要读取的字符数量。这样,你可以一点一点地读取文件,而不是一次性读取整个文件,这对于处理大文件非常有用。
file = open('example.txt', 'r')
content = file.read(10) # 读取前10个字符
print(content) # 打印这10个字符
file.close() # 关闭文件
在使用read()方法时,文件的指针会随着读取的进度向前移动。比如,如果你第一次读取了10个字符,下一次再调用read()时,它会从第11个字符开始读取。
记得,无论何时打开文件,都要在完成操作后关闭它,就像你喝完奶昔后要把空杯子放回桌上一样。这样可以确保你的程序不会占用不必要的资源,并且文件得到妥善处理。在Python中,你可以使用with语句来自动管理文件的打开和关闭,这样你就不用每次都显式调用close()了。
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# 文件在这里已经自动关闭了
2、readlines()
想象一下你在吃一盒披萨,你可能会一片一片地吃,而不是一口气吃掉整个披萨。在Python中,readlines()方法就像是这样一片一片地读取文件。
readlines()方法是用来读取所有行(直到文件结束)并返回它们作为一个列表。每个列表元素就是文件中的一行。这就像是把披萨切成了一片一片,你可以逐片享用。
这是一个使用readlines()的例子:
with open('example.txt', 'r') as file:
lines = file.readlines()
在这个例子中,lines是一个列表,每个元素都是example.txt文件中的一行。这就像是你有一盒披萨,每片披萨都是一个独立的部分。
你可以遍历这个列表,处理每一行。这就像是你一片一片地吃披萨:
for line in lines:
print(line)
这段代码会打印出文件中的每一行。
但是,如果你的文件非常大,一次性读取所有行可能会占用很多内存。在这种情况下,你可以逐行读取文件,就像是一口一口地吃披萨:
with open('example.txt', 'r') as file:
for line in file:
print(line)
这段代码会一行一行地读取文件,每读取一行就处理一行,这样就不会占用太多内存。
readlines()方法就像是把披萨切成一片一片,让你可以逐片处理。无论你是一次性吃掉所有披萨,还是一口一口地吃,Python都提供了灵活的方法来满足你的需求。
3、readline()
当然可以。想象一下你正在吃一碗面条,你可能会一根一根地吃,而不是一口气吃掉整碗面条。在Python中,readline()方法就像是这样一根一根地读取文件。
readline()方法是用来读取文件中的一行。每次调用这个方法,它都会返回文件中的下一行。这就像是你一根一根地吃面条。
这是一个使用readline()的例子:
with open('example.txt', 'r') as file:
line = file.readline()
在这个例子中,line是一个字符串,它包含了example.txt文件中的第一行。这就像是你吃掉了第一根面条。
你可以多次调用readline(),每次都会得到下一行。这就像是你一根一根地吃面条:
with open('example.txt', 'r') as file:
line1 = file.readline()
line2 = file.readline()
line3 = file.readline()
在这个例子中,line1、line2和line3分别是文件中的第一行、第二行和第三行。
当你读到文件的末尾时,readline()会返回一个空字符串,表示没有更多的行了。这就像是你吃完了所有的面条。
3+4、readline()和readlines()的区别
readline()和readlines()都是Python中用于文件读取的方法,但它们的工作方式和用途有所不同。
如果我们把文件想象成一碗面条:
-
readline():这个方法每次调用只会读取文件中的一行,就像你用筷子夹起一根面条。每次你夹起一根面条(调用readline()),你就会得到一行内容。当你到达碗底(文件末尾)时,你的筷子(readline())会告诉你没有面条了(返回一个空字符串)。with open('example.txt', 'r') as file: while True: line = file.readline() if not line: # 如果没有读到内容,说明到了文件末尾 break print(line.strip()) # 打印每行内容,strip()用于移除行尾的换行符 -
readlines():这个方法一次性读取整个文件的所有行,并将它们作为一个列表返回,每个列表项代表一行内容。这就像你一次性把所有面条都夹到你的碗里(读取所有行到一个列表)。然后,你可以逐一查看每根面条(遍历列表中的每一行)。with open('example.txt', 'r') as file: lines = file.readlines() # 读取所有行到列表中 for line in lines: print(line.strip()) # 打印每行内容
总结一下:
readline()适合逐行读取文件,特别是当文件很大时,你不想一次性将整个文件加载到内存中。readlines()适合一次性读取整个文件的所有行,并需要将它们作为一个列表进行处理。
选择哪种方法取决于你的具体需求和文件的大小。对于大文件,readline()通常是更好的选择,因为它不会一次性占用大量内存。对于较小的文件,或者当你需要将所有行作为一个列表处理时,readlines()可能更方便。
4、write()
想象一下你正在写一封信,你会一字一字地写下你的想法和感受。在Python中,write()方法就像是这样一字一字地写入文件。
write()方法是用来将指定的字符串写入文件。这就像是你在纸上写下你的想法。这是一个使用write()的例子:
with open('example.txt', 'w') as file:
file.write("Hello, world!")
在这个例子中,"Hello, world!"这个字符串被写入到了example.txt文件中。这就像是你在纸上写下了"Hello, world!"。
你可以多次调用write(),每次都会在文件的当前位置写入指定的字符串。这就像是你一字一字地写信:
with open('example.txt', 'w') as file:
file.write("Hello, world!")
file.write("How are you?")
在这个例子中,"Hello, world!"和"How are you?"这两个字符串被连续写入到了文件中,没有任何间隔。所以,如果你想在两个字符串之间添加换行,你需要显式写入一个换行符\n:
with open('example.txt', 'w') as file:
file.write("Hello, world!\n")
file.write("How are you?\n")
这样,"Hello, world!"和"How are you?"就会被写入到文件的两行中。
三、os模块
OS模块在Python编程中是一个非常重要的模块,它提供了许多与操作系统交互的功能。你可以把它想象成一个桥梁,连接Python程序和操作系统。
-
文件操作:OS模块可以帮助你创建、删除、重命名文件等。例如,
os.remove()可以删除一个文件,os.rename()可以重命名文件。 -
目录操作:OS模块也可以帮助你处理目录(也就是文件夹)。例如,
os.mkdir()可以创建一个新的目录,os.rmdir()可以删除一个目录。 -
获取系统信息:OS模块可以获取系统的一些信息,例如,
os.name可以获取操作系统的名字,os.getcwd()可以获取当前工作目录。 -
系统命令:OS模块还可以执行系统命令。例如,
os.system()可以执行一个系统命令。 -
路径操作:OS模块还提供了一些处理文件路径的函数,例如
os.path.join()可以连接两个或更多的路径名组件,os.path.split()可以分割文件名和路径。
1、rename()
想象一下,你有一本书,但是你不喜欢它的封面标题,你想给它换一个新的标题。在现实世界中,这可能意味着你需要一个新的封面或者用笔把旧标题涂掉,然后写上新的标题。在计算机的世界里,如果这本书是一个文件,那么rename()函数就是你用来更改文件名的“笔”。
rename()函数是OS模块提供的一个工具,它可以让你更改文件或目录的名字。使用这个函数非常简单,它接受两个参数:
-
src(source的缩写):这是当前文件或目录的名字,也就是你想要更改的那个名字。 -
dst(destination的缩写):这是你想要将文件或目录更名为的新名字。
这里有一个例子,假设你有一个名为old_name.txt的文件,你想将它重命名为new_name.txt。你可以这样使用rename()函数:
import os
# 假设 'old_name.txt' 是存在的文件
os.rename('old_name.txt', 'new_name.txt')
执行这段代码后,原来的old_name.txt文件就会被重命名为new_name.txt。
但是要注意,如果目标文件名(在这个例子中是new_name.txt)已经存在,rename()函数会抛出一个错误,除非你处理这种情况,否则你的程序可能会中断。所以在使用rename()之前,最好检查一下目标文件名是否已经被占用。
2、remove()
你可以把remove()函数想象成一个虚拟的文件销毁器。在现实生活中,如果你有一张不再需要的纸,你可能会把它扔进碎纸机。在Python中,如果你有一个不再需要的文件,你可以使用remove()函数来删除它。
remove()函数只需要一个参数,就是你想要删除的文件的路径。
import os
# 假设 'unwanted_file.txt' 是存在的文件
os.remove('unwanted_file.txt')
执行这段代码后,unwanted_file.txt文件就会被删除。
但是,你需要注意的是,remove()函数只能删除文件,不能删除目录。如果你尝试使用remove()函数来删除一个目录,Python会抛出一个错误。
此外,如果你尝试删除一个不存在的文件,或者你没有权限删除一个文件,Python也会抛出一个错误。所以在使用remove()函数之前,你应该确保文件存在,而且你有删除它的权限。
3、mkdir()
想象一下,你在现实生活中需要一个新的储物箱来整理你的物品。你会去买一个储物箱,然后把它放在合适的地方,之后就可以把东西放进去了。在计算机中,如果你需要一个新的地方来存储和组织你的文件,你会创建一个新的目录(也就是文件夹)。这就是mkdir()函数的作用。
mkdir()函数用来在你的计算机上创建一个新的目录。使用这个函数也非常简单,它只需要一个参数,即你想要创建的目录的名称。
import os
# 创建一个名为 'new_directory' 的新目录
os.mkdir('new_directory')
执行这段代码后,就会在当前工作目录中创建一个名为new_directory的新目录。
但是,如果你尝试创建一个已经存在的目录,Python会抛出一个错误,因为无法创建两个同名的目录。同样,如果你没有足够的权限在某个位置创建目录,或者你提供的目录路径不合法,也会出现错误。
因此,在使用mkdir()函数之前,你可能需要检查目录是否已经存在,以及你是否有权限在所选位置创建目录。你可以使用OS模块中的os.path.exists()函数来检查目录是否存在,例如:
if not os.path.exists('new_directory'):
os.mkdir('new_directory')
这样,你就可以避免因目录已存在而导致的错误。
4、getcwd()
在现实生活中,如果你在一个大型购物中心或者公园里,你可能会看到一些指示牌或地图,上面标有“你在这里”的标记,帮助你知道自己的位置。在Python中,如果你想知道你的程序当前在哪个目录下运行,你可以使用getcwd()函数。
getcwd()是英文"get current working directory"的缩写,意思是获取当前工作目录。这个函数不需要任何参数,当你调用它时,它会返回一个字符串,表示你的程序当前的工作目录。
import os
# 获取当前工作目录
current_directory = os.getcwd()
print(current_directory)
执行这段代码,它会打印出你的程序当前的工作目录。这个目录就是你的程序运行的地方,也是默认的文件读取和写入位置。
了解当前工作目录对于文件和目录的操作非常重要,因为很多文件操作都是相对于当前工作目录进行的。例如,如果你想读取一个名为data.txt的文件,Python会在当前工作目录中查找这个文件。如果文件不在当前工作目录,你就需要提供文件的完整路径。
5、chdir()
想象一下,你在现实生活中在一个房间里工作,但是你决定你需要换一个环境,于是你收拾东西到另一个房间去工作。在计算机中,如果你的程序需要在不同的目录(文件夹)之间切换工作环境,你会使用chdir()函数。
chdir()是英文"change directory"的缩写,意思是改变当前工作目录。这个函数需要一个参数,即你想要切换到的目录的路径。
import os
# 假设我们想要切换到名为 'new_directory' 的目录
new_directory = '/path/to/new_directory'
# 改变当前工作目录
os.chdir(new_directory)
# 现在我们可以确认当前工作目录已经改变
print(os.getcwd())
执行这段代码后,你的程序的当前工作目录会变成new_directory。这意味着,如果你现在要打开或创建文件,Python会在这个新目录下进行,除非你指定了文件的完整路径。
但是,如果你提供了一个不存在的目录路径,或者你没有权限切换到那个目录,Python会抛出一个错误。因此,在使用chdir()函数之前,你应该确保目标目录存在并且可访问。
6、listdir()
想象一下,你在一个房间里,这个房间里有很多不同的物品,你想知道都有什么,所以你开始列出每一个你看到的物品。在计算机中,如果你想知道一个目录(也就是文件夹)里都有什么文件和子目录,你可以使用listdir()函数。
listdir()函数会返回一个列表,这个列表中包含了指定目录中的所有文件和子目录的名字。这个函数需要一个参数,即你想要列出内容的目录的路径。
import os
# 假设我们想要列出当前工作目录中的所有文件和子目录
directory_contents = os.listdir()
for item in directory_contents:
print(item)
执行这段代码后,它会打印出当前工作目录中的所有文件和子目录的名字。
但是,如果你提供了一个不存在的目录路径,或者你没有权限访问那个目录,Python会抛出一个错误。因此,在使用listdir()函数之前,你应该确保目标目录存在并且可访问。
7、rmdir()
想象一下,你在家里有一个空的抽屉,你决定不再需要它了,所以你清空了抽屉并拆掉它,以便腾出空间。在计算机中,如果你有一个空的目录(文件夹)并且你想删除它,你可以使用rmdir()函数。
rmdir()是英文"remove directory"的缩写,意思是删除目录。这个函数需要一个参数,即你想要删除的目录的路径。
import os
# 假设 'empty_directory' 是一个空的目录
os.rmdir('empty_directory')
执行这段代码后,名为empty_directory的目录将被删除。
但是,有几点需要注意:
-
目录必须是空的,也就是说,里面没有任何文件或子目录。如果目录不是空的,Python会抛出一个错误。
-
你必须有足够的权限来删除目录,否则也会抛出一个错误。
-
如果目录不存在,你会得到一个错误。
因此,在使用rmdir()函数之前,你应该确保目录是空的,你有权限删除它,而且它确实存在。
第十五章 异常
在Python中,异常是指程序执行过程中发生的错误。你可以把它想象成是一种信号,告诉你程序某个地方出了问题。比如,当你试图除以零时,Python会抛出一个异常,因为这在数学上是没有意义的。
异常处理是编程中非常重要的一个概念,它允许我们的程序在遇到错误时优雅地恢复,而不是直接崩溃。在Python中,我们使用try和except语句来处理异常。
这就像是一个安全网。你把可能出错的代码放在try块里,如果一切顺利,那么代码就会正常运行。但是如果出现了错误,Python会跳到except块,你可以在这里决定如何处理这个错误。
举个例子:
try:
# 尝试执行的代码
result = 10 / 0
except ZeroDivisionError:
# 如果发生了除以零的错误,就会执行这里的代码
print("哎呀,你试图除以零了!")
在这个例子中,我们尝试执行10 / 0,这会引发一个ZeroDivisionError异常。因为我们预料到了这种情况,所以我们在except块中处理了这个错误,程序就不会崩溃,而是会打印出一条友好的消息。
Python还有一个else块,它会在try块成功执行后运行,没有错误发生时执行。还有一个finally块,不管有没有异常发生,它都会执行,通常用于执行一些清理工作,比如关闭文件。
异常处理让我们的程序更加健壮,能够处理意外情况,给用户更好的体验。
一、捕获异常
1、捕获具体异常
在Python中,当程序运出行现错误时,Python会创建一个异常对象。如果这个异常对象没有被处理或捕获,那么程序就会停止运行,并显示一个错误消息。这就是我们通常说的程序崩溃。
但是,如果我们预料到可能会出现错误,我们就可以使用try和except语句来"捕获"这个异常。这就像是在可能出现问题的代码周围设置一个安全网,如果代码运行正常,那么try块中的代码就会执行。如果代码出现错误,Python就会停止执行try块中的其他代码,并执行except块中的代码。
这就是所谓的异常捕获。让我们看一个例子:
try:
# 我们尝试执行一段可能会出现错误的代码
x = 1 / 0
except ZeroDivisionError:
# 如果出现了除以零的错误,我们就会捕获这个异常,并执行这里的代码
print("不能除以零!")
在这个例子中,当我们尝试除以零时,Python会抛出一个ZeroDivisionError异常。我们使用except语句捕获了这个异常,并打印出一条错误消息。这样,即使出现了错误,我们的程序也不会崩溃,而是会优雅地处理这个问题。
你可以捕获多种类型的异常,只需要在except后面列出你想要捕获的异常类型即可。例如:
try:
# 尝试执行一段可能会出现错误的代码
x = 1 / 0
except (ZeroDivisionError, TypeError):
# 如果出现了除以零的错误或类型错误,我们就会捕获这个异常,并执行这里的代码
print("出现了一个错误!")
在这个例子中,我们捕获了两种类型的异常:ZeroDivisionError和TypeError。无论出现哪种错误,我们都会打印出一条错误消息。
2、捕获多个异常
在Python中,你可能会遇到不同类型的错误,比如:
-
ZeroDivisionError:尝试除以零时抛出。 -
TypeError:操作或函数应用于不适当类型的对象时抛出。 -
NameError:尝试访问一个未被定义的变量时抛出。 -
IndexError:在使用序列中不存在的索引时抛出,比如一个列表或元组。 -
KeyError:在使用字典中不存在的键时抛出。 -
ValueError:当传入一个函数或操作的参数是正确类型,但是不适合特定的情况时抛出。 -
AttributeError:尝试访问对象没有的属性时抛出。 -
FileNotFoundError:尝试打开一个不存在的文件时抛出。 -
IOError:在输入/输出操作失败时抛出,比如打开文件时。在Python 3中,它已经被更具体的异常如FileNotFoundError和PermissionError等取代。 -
ImportError:在导入模块或对象失败时抛出。 -
ModuleNotFoundError:在无法找到模块时抛出,是ImportError的一个子类。 -
PermissionError:尝试在没有足够权限的情况下执行操作时抛出。 -
OSError:操作系统无法执行操作时抛出,比如文件系统和进程管理相关的操作。 -
SyntaxError:解析器遇到语法错误时抛出。 -
IndentationError:代码缩进不正确时抛出,是SyntaxError的一个子类。 -
StopIteration:当迭代器没有更多元素时抛出,通常由next()函数引发。 -
KeyboardInterrupt:用户中断程序执行,通常是通过按下Ctrl+C引发。
这些只是Python中异常的一小部分。Python的异常体系非常丰富,可以处理各种不同的错误情况。了解和使用这些异常可以帮助你编写更健壮、更易于维护的代码。
如果你想在一个try/except块中捕获多个异常,你有两种方式可以做到。
第一种方式是使用多个except子句,每个子句处理一种异常。例如:
try:
# 尝试执行一段可能会出现错误的代码
x = 1 / 0
y = unknown_variable
except ZeroDivisionError:
# 如果出现了除以零的错误,我们就会捕获这个异常,并执行这里的代码
print("不能除以零!")
except NameError:
# 如果出现了名称错误,我们就会捕获这个异常,并执行这里的代码
print("变量名未定义!")
在这个例子中,我们分别处理了ZeroDivisionError和NameError两种异常。
第二种方式是在一个except子句中列出所有要捕获的异常,用逗号分隔。例如:
try:
# 尝试执行一段可能会出现错误的代码
x = 1 / 0
y = unknown_variable
except (ZeroDivisionError, NameError) as e:
# 如果出现了除以零的错误或名称错误,我们就会捕获这个异常,并执行这里的代码
print("出现了一个错误:", str(e))
在这个例子中,我们在一个except子句中捕获了ZeroDivisionError和NameError两种异常。注意,我们使用as关键字将异常对象赋值给变量e,这样我们就可以在后面的代码中使用这个异常对象了。
3、捕获所有异常
在Python中,有时候我们可能想要捕获所有可能发生的异常,不管它们是什么类型。这可以通过捕获Exception类来实现,因为在Python中几乎所有的异常都是从它派生出来的。
捕获所有异常可以使用一个通配符except子句,这样无论发生什么错误,都会被捕获处理。但是,我要强调的是,通常我们不推荐这样做,因为它会隐藏错误的具体信息,使得调试变得更加困难。不过,在某些情况下,比如当你要在程序结束前执行一些清理工作,或者当你不确定可能会遇到哪些异常时,捕获所有异常可能是有用的。
try:
# 尝试执行一段可能会出现任何错误的代码
# ...
except Exception as e:
# 捕获所有异常
print("程序出现了一个错误:", str(e))
例如:
try:
# 尝试执行一段可能会出现错误的代码
x = 1 / 0
y = unknown_variable
z = '2' + 2
except Exception as e:
# 捕获所有异常,并将异常对象赋值给变量e
print("出现了一个错误:", str(e))
在这个例子中,我们使用except Exception as e来捕获所有的异常。变量e是一个异常对象,它包含了异常的具体信息,比如错误消息等。通过打印e,我们可以得到一些关于错误的信息,这有助于我们理解发生了什么。
然而,正如我之前提到的,通常最好是尽可能具体地捕获异常。这样可以针对不同的错误情况给出更具体的错误处理,也可以避免意外捕获到那些应该传播的异常。例如,如果你的程序中有一个bug,你可能不希望通过捕获所有异常来隐藏这个bug,而是希望能够发现并修复它。
4、没有异常
在Python中,如果在执行代码时没有发生任何异常,那么try块中的代码会顺利执行完毕。但有时候,我们希望在try块成功执行后,能够执行一些操作,比如清理资源或者输出一个成功的消息。为了实现这个目的,我们可以使用else子句。
else子句紧跟在try和except块之后,只有当try块中的代码没有抛出任何异常时,else子句中的代码才会执行。这就像是说:“如果没有任何问题发生,那么请执行这部分代码。”
try:
# 尝试执行一段可能会出现错误的代码
print("尝试执行代码...")
result = 10 / 2 # 这里不会产生异常
except ZeroDivisionError:
# 如果出现了除以零的错误,会执行这里的代码
print("不能除以零!")
else:
# 如果没有异常发生,会执行这里的代码
print("一切顺利,结果是:", result)
在这个例子中,我们尝试执行了一个除法操作,这个操作是安全的,不会产生异常。因此,else子句中的代码会被执行,输出结果和一条成功的消息。
使用else子句的好处是它可以帮助我们明确区分正常执行的代码和错误处理代码。这样,我们的try块就只包含那些可能抛出异常的代码,而不是也包含处理正常逻辑的代码。这有助于提高代码的可读性和维护性。
二、异常嵌套
在Python中,我们可以在一个try/except块中嵌套另一个try/except块,这就是所谓的异常嵌套。这种结构可以让我们更精细地控制错误处理的流程。
当内部的try块抛出一个异常时,Python会首先查看内部的except子句是否可以处理这个异常。如果可以,那么就由内部的except子句处理;如果不可以,那么异常会被传递到外部的try块,由外部的except子句处理。
try:
# 外部try块
print("外部try块开始...")
try:
# 内部try块
print("内部try块开始...")
x = 1 / 0 # 这里会抛出一个ZeroDivisionError
print("内部try块结束...")
except ValueError:
# 内部except子句,只处理ValueError
print("内部except子句,捕获到ValueError!")
print("外部try块结束...")
except ZeroDivisionError:
# 外部except子句,处理ZeroDivisionError
print("外部except子句,捕获到ZeroDivisionError!")
在这个例子中,内部的try块抛出了一个ZeroDivisionError。因为内部的except子句只处理ValueError,所以它不能处理这个异常,异常被传递到了外部的try块。外部的except子句可以处理ZeroDivisionError,所以它捕获了这个异常,并打印了一条消息。
通过异常嵌套,我们可以为不同的代码段提供不同的错误处理,这有助于我们编写更健壮、更易于维护的代码。
三、抛出自定义异常
在Python中,除了内置的异常类型,我们还可以定义自己的异常类型。这可以让我们更精确地表示程序中可能出现的错误情况。自定义的异常类型是通过创建Exception类的子类来实现的。
创建自定义异常类型的基本步骤如下:
-
定义一个新的类,它继承自
Exception类或其任何一个子类。 -
如果需要,可以在新类中添加一些属性或方法来提供更多的错误信息。
自定义异常类型的例子:
# 定义一个新的异常类型,表示除数不能为零的错误
class DivisionByZeroError(Exception):
def __init__(self, message="除数不能为零"):
self.message = message
super().__init__(self.message)
在这个例子中,我们定义了一个名为DivisionByZeroError的新的异常类型。它继承自Exception类,并添加了一个message属性来存储错误消息。
一旦我们定义了自己的异常类型,就可以使用raise语句来抛出这个异常。下面是一个例子:
def divide(a, b):
if b == 0:
raise DivisionByZeroError() # 抛出我们自定义的异常
return a / b
在这个例子中,我们定义了一个divide函数,它接受两个参数a和b,并返回它们的商。如果b等于零,那么我们就抛出一个DivisionByZeroError异常。
自定义异常的用处主要体现在以下几个方面:
-
提高代码的可读性:通过自定义异常的名称和属性,可以让其他开发者更容易理解代码中可能出现的错误。例如,
ValueTooHighError或ConnectionTimeoutError这样的异常名能够直观地告诉你错误的性质。 -
精确的错误处理:自定义异常允许你为程序中的特定错误情况创建专门的异常类型。这意味着你可以编写专门处理这些特定异常的代码,而不是使用更一般的异常类型,如
Exception或ValueError。 -
维护和扩展:随着程序的发展,可能需要引入新的错误类型。自定义异常可以很容易地扩展,你可以添加新的异常类型或者在现有的异常类型中添加更多的信息。
-
错误分类:在大型应用程序中,可能需要对错误进行分类,以便不同的系统部分可以处理不同类型的错误。自定义异常可以帮助实现这种分类。
-
强制错误处理:通过自定义异常,你可以强制调用者处理某些特定的错误情况,而不是忽略错误或使用不恰当的错误处理方式。
-
统一异常接口:如果你的应用程序或库需要提供一个统一的异常处理接口,自定义异常可以帮助你实现这一点。用户只需要捕获你定义的异常,而不是多种不同的Python内置异常。
下面是一个更实际的例子,说明自定义异常如何在实际编程中使用:
class EmailNotValidError(Exception):
"""当邮箱不符合验证标准时抛出"""
pass
def validate_email(email):
if "@" not in email:
raise EmailNotValidError("邮箱地址缺少'@'符号")
try:
validate_email("example.com")
except EmailNotValidError as e:
print(e)
在这个例子中,我们定义了一个EmailNotValidError异常,用于处理电子邮件验证中的错误。当电子邮件地址不包含@符号时,我们抛出这个异常。在try/except块中,我们可以捕获这个特定的异常并给出相应的错误信息。这样,当其他开发者阅读或使用你的代码时,他们可以很容易地理解和处理电子邮件验证中可能出现的问题。
第十六章 模块与包
一、模块
在Python中,模块就像是一个工具箱。想象一下,你有一个大箱子,里面装满了各种工具,比如锤子、螺丝刀、钳子等。每当你需要做一些特定的工作时,比如挂画或者修理东西,你就会打开这个箱子,找到你需要的工具,然后使用它们。
同样地,在编程中,模块是一个包含一组功能的文件,这些功能就像工具一样,可以在你的程序中被使用。这些功能可能包括变量、函数或者类等。Python有很多内置模块,它们提供了很多有用的功能,比如数学计算、文件操作、网络连接等等。此外,还有很多第三方模块,由其他程序员编写,可以被任何人使用。
使用模块的好处是,你不需要从头开始编写所有的代码。如果你需要进行数学运算,你可以导入math模块;如果你要处理日期和时间,你可以导入datetime模块。这样,你就可以重复使用别人已经写好的代码,提高效率,减少错误。
在Python中,导入模块通常使用import语句。比如,如果你想使用math模块,你只需要在你的代码中写上import math。之后,你就可以调用math模块中的函数,比如math.sqrt(16)来计算16的平方根。
大家有没有发现,我们目前位置写的.py文件都比较单一。有些师傅可能会问道,将来写大型项目的时候,那我们这个文件也会是一个文件吗?这当然是不可能的,如果写成一个文件的话,一个文件几万行代码,基本上就被代码恶心到了,报一个错都不知道从哪开始调,调完这个另外几个又报错。所以我们一定会分文件,所以我们就可以把文件写成模块。
1、模块的写法
首先,你需要知道的是,一个Python模块其实就是一个Python文件。这个文件中可以包含定义的函数、类和变量。这些函数、类和变量可以被其他Python文件(或者说是其他Python模块)导入和使用。
假设我们要创建一个名为mymodule的模块,我们可以创建一个名为mymodule.py的Python文件。在这个文件中,我们可以定义一些函数和变量。比如:
mymodule.py
# 定义一个变量
greeting = "Hello, world!"
# 定义一个函数
def say_hello(name):
print(f"Hello, {name}!")
在这个模块中,我们定义了一个变量greeting和一个函数say_hello。现在,我们可以在其他Python文件中导入并使用这个模块。比如:
main.py
# 导入我们自定义的模块
import mymodule
# 使用模块中的变量和函数
print(mymodule.greeting)
mymodule.say_hello("Alice")
在这个main.py文件中,我们首先导入了我们自定义的mymodule模块,然后我们就可以使用mymodule.greeting来访问模块中的greeting变量,使用mymodule.say_hello("Alice")来调用模块中的say_hello函数。
这就是Python模块的基本写法。通过编写自己的模块,你可以把相关的代码组织在一起,使你的代码更加清晰和易于管理。
2、模块导入类
首先,类是Python中的一个重要概念,它允许我们创建自己的数据类型。类可以包含数据(称为属性)和可以对这些数据进行操作的函数(称为方法)。
现在,假设我们有一个模块,里面定义了一个类。我们将这个模块称为vehicle.py,里面定义了一个Car类:
vehicle.py
class Car:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def display_info(self):
print(f"This is a {self.brand} {self.model}")
在这个vehicle.py文件中,我们定义了一个Car类,它有两个属性:brand和model,以及一个方法display_info,用于打印车辆的品牌和型号。
如果我们想在另一个文件中使用这个Car类,我们需要导入它。这可以通过几种不同的方式来实现:
(1)导入整个模块,然后使用模块名作为前缀来访问类:
main.py
import vehicle
# 创建Car类的实例
my_car = vehicle.Car("Toyota", "Corolla")
# 调用Car类的方法
my_car.display_info()
(2)从模块中直接导入类:
main.py
from vehicle import Car
# 创建Car类的实例,这次不需要前缀
my_car = Car("Toyota", "Corolla")
# 调用Car类的方法
my_car.display_info()
(3)导入模块中的所有内容(不推荐,因为这样会导入模块中的所有定义,可能会导致命名冲突):
main.py
from vehicle import *
# 创建Car类的实例
my_car = Car("Toyota", "Corolla")
# 调用Car类的方法
my_car.display_info()
通过这些方法,你可以在你的程序中创建和使用Car类的实例。这样做的好处是,你可以在一个地方定义类,然后在多个地方重复使用它,这有助于代码的重用和组织。
3、模块中的__all__
在Python中,__all__是一个模块级别的变量,它控制着当从模块中使用from module import *语句时,哪些名称(比如函数、类、变量等)会被导入。这个列表是一个由字符串组成的列表,每个字符串都是模块中可导入元素的名称。
通常情况下,当你使用from module import *时,Python会导入模块中定义的所有不以下划线开头的全局名称。但是,如果模块定义了__all__,那么只有__all__列表中指定的名称会被导入。
这样做的目的是给模块作者提供一种方式来明确指出哪些是模块的公共接口,也就是说,哪些是模块打算被其他代码使用的部分。这有助于避免不想要的名称被导入,也有助于避免潜在的命名冲突。
让我们来看一个例子。假设我们有一个模块shapes.py,里面定义了几个类和函数:
shapes.py
class Circle:
# ...
class Square:
# ...
def _helper_function():
# ...
def draw_shape(shape):
# ...
__all__ = ['Circle', 'draw_shape']
在这个例子中,Circle和Square是类,_helper_function和draw_shape是函数。注意_helper_function函数的名称以单下划线开头,这是一个约定,表示这个函数是模块内部使用的,不是公共接口的一部分。
我们在shapes.py模块的末尾定义了__all__,并且只包含了'Circle'和'draw_shape'。这意味着,如果其他代码使用from shapes import *,它们只会导入Circle类和draw_shape函数,而不会导入Square类和_helper_function函数。
main.py
from shapes import *
# 这会工作,因为Circle和draw_shape在__all__列表中
c = Circle()
draw_shape(c)
# 这将不工作,因为Square和_helper_function不在__all__列表中
s = Square() # 将会抛出错误
_helper_function() # 将会抛出错误
总的来说,__all__是模块作者用来定义模块公共接口的一种方式,它告诉Python和模块的用户,当使用from module import *时,应该导入哪些名称。
4、__name__的作用
在Python中,__name__是一个特殊的内置变量,它对于每个Python文件(或者说模块)来说都是存在的。这个变量的值取决于如何使用这个文件。如果文件被直接运行,__name__变量的值会被设置为"__main__";如果文件被导入到另一个文件中,__name__的值则会被设置为模块的名字。
这个特性非常有用,因为它允许我们区分模块是被导入使用,还是作为主程序直接运行。这样,我们可以在一个文件中既定义可以被其他模块导入使用的函数和类,同时也可以包含测试这些函数和类的代码,而这些测试代码只有在文件被直接运行时才会执行。
来看一个例子:
mymodule.py
def say_hello(name):
print(f"Hello, {name}!")
if __name__ == "__main__":
# 这部分代码只有在mymodule.py被直接运行时才会执行
print("This will not run if I'm imported.")
say_hello("Alice")
如果你直接运行mymodule.py(比如通过命令行运行python mymodule.py),__name__的值将会是"__main__",因此下面的代码将会执行,你会看到输出:
This will not run if I'm imported.
Hello, Alice!
但是,如果你在另一个文件中导入mymodule:
main.py
import mymodule
mymodule.say_hello("Bob")
当你运行main.py时,mymodule.py中的__name__将会是"mymodule",因此if __name__ == "__main__":下面的代码块不会执行,你只会看到输出:
Hello, Bob!
这种方式使得mymodule.py非常灵活,它可以作为一个模块被其他文件导入,同时也可以作为一个独立的程序来运行。这在开发大型程序时非常有用,因为你可以为每个模块编写测试代码,而这些代码只会在你直接运行该模块时执行,不会影响到其他模块导入并使用它时的行为。
二、包
在Python中,包(Package)是一种用来组织相关模块的层次性目录结构。你可以把它想象成一个文件夹,这个文件夹里面不仅包含了模块(即Python文件),还可能包含其他的包(子文件夹)。这种结构让你可以把代码组织得更加清晰,也便于管理大型项目。
让我们用一个例子来说明。假设你正在写一个应用,这个应用需要处理网络请求、数据库交互和数据分析。你可以创建三个不同的包来组织你的代码:
myapplication/
__init__.py
networking/
__init__.py
http.py
ftp.py
database/
__init__.py
postgresql.py
mysql.py
analytics/
__init__.py
statistics.py
computation.py
在这个结构中,myapplication是一个包,它包含了三个子包:networking、database和analytics。每个子包里面有不同的模块,比如networking包里有http.py和ftp.py模块。
每个包目录下都会有一个特殊的文件__init__.py。这个文件的存在告诉Python这个目录应该被视为一个Python包。__init__.py可以是空的,但也可以包含包的初始化代码,或者定义__all__变量来控制from package import *时应该导入哪些模块。
当你想要使用包中的模块时,你可以使用点(.)来导航这个层次结构,比如:
from myapplication.networking import http
from myapplication.database import postgresql
from myapplication.analytics import statistics
这样,你就可以在你的代码中使用http模块、postgresql模块和statistics模块了。
包是Python中用来组织模块的一种方式,它通过文件夹的形式提供了一个层次化的命名空间,使得你可以把相关的代码模块组织在一起,从而使得代码更加模块化,易于维护和重用。
1、包中的__all__
在Python中,__all__是一个特殊的变量,它定义在一个模块或者包的顶层。当你使用from module import *或者from package import *这样的语句时,__all__就起作用了。
__all__是一个列表,它定义了当你使用from ... import *语句时,应该导入哪些模块或者包的成员。如果__all__没有定义,那么from ... import *语句会导入所有不以下划线开头的全局名称。
在包的上下文中,__all__通常用于控制哪些模块应该被导入。例如,假设你有一个名为my_package的包,它包含module1.py、module2.py和module3.py三个模块,你可以在my_package的__init__.py文件中定义__all__来控制哪些模块会被导入:
my_package/__init__.py
__all__ = ['module1', 'module2']
在这个例子中,当你使用from my_package import *时,只有module1和module2会被导入,module3不会被导入。
这样做的好处是,你可以明确地控制你的包的公共接口,也就是说,你可以控制哪些模块是你打算让其他人使用的,哪些是内部使用的。这有助于保持你的代码的清晰性和可维护性。
__all__是一个非常有用的工具,它可以帮助你更好地组织你的Python代码,特别是当你在开发大型项目时。
第十七章 三大推导式
Python中的三大推导式,它们分别是列表推导式、字典推导式和集合推导式。这些推导式都是用来创建新的数据结构的快捷方式,它们的作用和特点如下:
-
列表推导式(List Comprehensions):
- 作用:用来创建新的列表。
- 特点:可以通过对一个可迭代对象进行操作和筛选来生成列表中的元素。列表推导式比使用循环更加简洁,代码更易读。
- 例子:
[x*2 for x in range(10)]会生成一个包含0到18(每个数乘以2)的列表。
-
字典推导式(Dictionary Comprehensions):
- 作用:用来创建新的字典。
- 特点:可以通过对键值对进行操作和筛选来生成字典中的项。字典推导式可以快速将一个键值对列表转换成字典,或者根据现有字典创建新的字典。
- 例子:
{str(x): x*2 for x in range(10)}会创建一个字典,其中键是从0到9的字符串,值是相应的数字乘以2。
-
集合推导式(Set Comprehensions):
- 作用:用来创建新的集合。
- 特点:集合是一个无序的、不包含重复元素的数据结构。集合推导式可以用来快速生成集合,并自动去除重复元素。
- 例子:
{x%5 for x in range(20)}会生成一个包含0到4的集合,因为这是20以内所有数字除以5的余数。
这三种推导式都是Python中非常有用的工具,它们可以帮助我们写出更加简洁、高效的代码。使用推导式,我们可以减少代码量,提高代码的可读性,同时也可以提升编程的乐趣。
一、列表推导式
列表推导式是Python中一种非常强大且易于使用的工具,它可以帮助我们快速生成列表。你可以把它想象成一个高效的“for”循环,但是它的语法更简洁,更易于阅读。
基本的列表推导式的格式如下:
[expression for item in iterable]
这里的expression是一个表达式,它基于item进行计算。iterable是一个可迭代对象,比如列表、元组或者字符串等。
举个例子,如果我们想要生成一个包含1到10所有数字平方的列表,我们可以这样做:
squares = [x**2 for x in range(1, 11)]
print(squares)
这段代码的输出将会是:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
列表推导式还可以包含条件语句。例如,如果我们只想要那些能被2整除的数字的平方,我们可以这样写:
squares = [x**2 for x in range(1, 11) if x % 2 == 0]
print(squares)
这段代码的输出将会是:[4, 16, 36, 64, 100]
二、字典推导式
字典推导式是Python中的一种结构,它可以让我们更简洁、更高效地创建字典。它的基本形式如下:
{key_expression: value_expression for item in iterable}
在这里,key_expression和value_expression是基于item计算得出的表达式,它们分别定义了字典的键和值。iterable是一个可迭代对象,比如列表、元组或者字符串等。
举个例子,假设我们有一个列表,我们想要创建一个字典,其中的键是列表中的元素,值是这个元素的平方。我们可以这样做:
numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers}
print(squares)
这段代码的输出将会是:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
字典推导式也可以包含条件语句。例如,如果我们只想要那些能被2整除的数字及其平方,我们可以这样写:
numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers if x % 2 == 0}
print(squares)
这段代码的输出将会是:{2: 4, 4: 16}
三、集合推导式
集合推导式在Python中是一种非常方便的构建集合(Set)的方法。集合是一种不包含重复元素的数据结构,它和列表推导式或字典推导式有相似的语法结构,但是它是用来创建集合的。
集合推导式的基本语法如下:
{expression for item in iterable if condition}
这里的expression是基于item的表达式,用来生成集合中的元素。iterable是一个可迭代对象,比如列表、元组或者字符串等。condition是一个可选的条件语句,用来筛选出符合条件的元素。
例如,假设我们有一个列表,里面包含了一些数字,我们想要创建一个集合,其中包含这些数字的平方,但是不包含任何重复的值。我们可以使用集合推导式来实现这一点:
numbers = [1, 2, 2, 3, 4, 4, 4, 5]
squares_set = {x**2 for x in numbers}
print(squares_set)
这段代码的输出可能是:{1, 4, 9, 16, 25}
注意,即使在原始的numbers列表中,数字2和4出现了多次,但在squares_set集合中,每个平方值只会出现一次,因为集合自动去除了重复的元素。
集合推导式也可以包含条件语句,用来进一步控制哪些元素应该被包括在最终的集合中。例如,如果我们只想要那些原始数字能被2整除的平方值,我们可以这样写:
squares_set = {x**2 for x in numbers if x % 2 == 0}
print(squares_set)
这段代码的输出将会是:{4, 16}
第十八章 三大器
在Python编程语言中,通常所说的“三大器”指的是迭代器(Iterators)、生成器(Generators)和装饰器(Decorators)。这三个概念在Python中非常重要,它们各自有着独特的作用和用途。下面我会用通俗易懂的语言来解释它们。
- 迭代器(Iterators):
迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:__iter__()和__next__()。字符串、列表或元组对象都可用于创建迭代器。
举个例子,当你有一个列表,你可以使用迭代器按顺序访问列表中的每个元素,而不需要使用索引和循环。
- 生成器(Generators):
生成器是一种特殊类型的迭代器。简单来说,生成器是一个可以生成值的函数,但它不像普通函数那样一次返回所有值,而是每次调用时返回一个值。这意味着生成器不会一次性将所有值加载到内存中,而是按需生成,这样可以节省内存。在Python中,使用yield语句可以创建生成器。
比如说,如果你要创建一个从0数到无穷大的序列,使用生成器可以做到在每次循环时只计算下一个数,而不是一次性计算出所有的数。
- 装饰器(Decorators):
装饰器是一种设计模式,在Python中,它是一个函数,它可以让你在不修改原有函数代码的情况下,增加原有函数的新功能。装饰器通过@符号使用,放在一个函数的定义之前。
例如,如果你想要计算一个函数的执行时间,你可以写一个装饰器,它会在函数执行前后分别记录时间,然后计算出函数执行所需的时间,而不需要改变原有函数的代码。
这三大器在Python中的应用非常广泛,它们提高了代码的可读性、可维护性和效率。理解和掌握这三大器,对于编写高质量的Python代码非常有帮助。
一、迭代器
迭代器,就像一个读书的人,他会一本一本地读书,一章一章地读,一句一句地读,一次只读一句。在Python中,迭代器就是这样的一个对象,它可以记住遍历的位置,从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:__iter__()和__next__()。__iter__()方法返回迭代器本身,__next__()方法返回迭代器的下一个值。
让我们来看一个简单的例子:
my_list = [1, 2, 3, 4]
it = iter(my_list) # 创建迭代器对象
for i in it:
print(i, end=" ")
这段代码首先创建了一个列表my_list,然后使用iter()函数创建了一个迭代器对象it。然后,我们在it上进行迭代,打印出列表中的每个元素。
在这个例子中,迭代器it就像一个读书的人,它一次读一个元素,从列表的第一个元素开始,一直读到最后一个元素。
迭代器的好处是什么呢?首先,迭代器提供了一种统一的访问集合元素的方式,无论这个集合是列表、元组还是其他的数据结构。其次,迭代器只在需要时才获取下一个值,这在处理大数据时可以节省内存。
迭代器在Python中有很多灵活的使用方法。除了在for循环中使用,迭代器还可以通过next()函数直接调用来获取下一个元素,也可以与Python的其他内置函数结合使用,提供更加丰富的功能。下面是一些迭代器的其他使用方法:
- 使用
next()函数:
你可以使用next()函数来手动获取迭代器的下一个元素。这在你想要控制迭代过程或者在不同的时间点获取元素时非常有用。
my_list = [1, 2, 3]
it = iter(my_list)
print(next(it)) # 输出 1
print(next(it)) # 输出 2
print(next(it)) # 输出 3
# 如果此时再调用 next(it),因为没有更多的元素,将会抛出 StopIteration 异常
- 与
zip()函数结合使用:
zip()函数可以将多个迭代器“压缩”成一个新的迭代器,每次迭代返回一个元组,包含每个原迭代器的对应元素。
numbers = [1, 2, 3]
letters = ['a', 'b', 'c']
zipped = zip(numbers, letters)
for pair in zipped:
print(pair) # 输出 (1, 'a'), (2, 'b'), (3, 'c')
- 与
map()函数结合使用:
map()函数可以将一个函数应用于迭代器中的每个元素,返回一个新的迭代器作为结果。
numbers = [1, 2, 3]
squared = map(lambda x: x ** 2, numbers)
for num in squared:
print(num) # 输出 1, 4, 9
- 与
filter()函数结合使用:
filter()函数可以根据一个函数的返回值决定是否保留迭代器中的元素,返回一个新的迭代器。
numbers = [1, 2, 3, 4, 5]
even = filter(lambda x: % x 2 == 0, numbers)
for num in even:
print(num) # 输出 2, 4
- 使用
itertools模块:
Python的itertools模块提供了许多高级的迭代器工具,可以用来创建复杂的迭代器,比如无限迭代器、链式迭代器等。
import itertools
# 无限迭代器,会无限地重复传入的参数
for number in itertools.repeat(2):
print(number) # 将会无限打印 2,需要手动中断
迭代器是Python中一个非常强大的概念,它们的使用远远不止这些。了解和掌握迭代器可以帮助你编写出更高效、更优雅的Python代码。
想象你在餐厅吃自助餐,你的盘子就像一个迭代器。你从头到尾走一遍自助餐线,每次只能装一样菜到你的盘子里。迭代器就是这样,它一次处理集合中的一个元素,直到所有元素都被处理完。在Python中,迭代器是一个可以记住遍历位置的对象。当你使用for循环去遍历比如一个列表或者一个字符串时,你就是在使用迭代器。
遍历数据:迭代器最常见的用途是遍历数据集合,比如列表、元组、字典等。它提供了一种统一的方法来逐个访问集合中的元素,而不需要知道集合的内部结构。
节省内存:迭代器不需要一次性将所有元素加载到内存中,它们一次只处理一个元素,这对于处理大型数据集非常有用,因为它可以减少程序的内存消耗。
二、生成器
想象一下,你有一个手摇发电机,每次你转动手柄,它就会生成一点电。生成器在Python中的概念也差不多,它是一种特殊的函数,可以一次生成一个值,每次需要值的时候就“转动一次手柄”,也就是执行一次。
在普通的函数中,你用return返回一个值,然后函数就结束了。但在生成器中,你用yield返回一个值,函数会暂停并保存当前所有的状态信息,下次从它离开的地方继续执行,直到遇到下一个yield。
这样做的好处是什么呢?最大的好处就是节省内存。因为生成器不需要一次性生成所有的数据放到内存中,它是按需生成的。这在处理大数据或者无限序列的时候特别有用。
让我们来看一个简单的例子:
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
for value in gen:
print(value)
这段代码定义了一个生成器my_generator,它会生成三个值:1、2和3。当我们创建一个生成器对象gen并在它上面进行迭代时,它会依次打印出1、2和3,每次循环都会从生成器中获取一个值。
生成器的这种“按需生成”特性使得它在处理大量数据时非常高效,因为它只在需要时才生成数据,而不是一次性将所有数据加载到内存中。这就是为什么生成器在Python中非常有用,尤其是在数据科学、大数据处理等领域。
让我们来看一个更实际的例子,一个无限序列生成器。这个生成器会生成所有的自然数,从0开始。
def natural_numbers():
num = 0
while True:
yield num
num += 1
这个生成器会无限地生成自然数。你可以像这样使用它:
gen = natural_numbers()
for i in range(10):
print(next(gen))
这段代码会打印出前10个自然数,从0到9。
你可能会问,这个生成器是无限的,那么它会不会占用无限的内存呢?答案是不会。尽管这个生成器可以生成无限多的数,但是在任何给定的时间点,它只需要记住下一个数就足够了。所以,它只占用了一个整数的内存空间。这就是生成器的魔力所在。
这个例子展示了生成器的一个重要特性:它们可以按需生成值,而不需要一次性生成所有值。这使得它们在处理大数据或无限序列时非常有用。
生成器是特殊的迭代器。想象你是一个魔术师,每次摇一摇魔杖就能变出一个兔子,而不需要一开始就准备好所有的兔子。生成器就像这样,它可以在运行时生成值,而不需要一开始就创建所有的值。这样做可以节省内存,特别是当你处理大量数据时。在Python中,生成器可以通过函数来创建,这些函数使用了yield语句。
- 延迟计算:生成器允许你延迟计算结果,直到你真正需要它们。这意味着它们在生成值时不需要占用大量内存。
- 简化代码:使用生成器可以避免创建复杂的迭代器类。你只需要一个包含
yield语句的函数,就可以创建一个生成器。- 无限序列:生成器可以用来创建无限的序列,而不会在开始时就占用无限的内存。
三、装饰器
装饰器可以想象成是一个包装礼物的包装纸。当你有一个礼物(在这里指的是一个函数)时,你可能想要让它看起来更漂亮一些,所以你用包装纸把它包起来。在Python中,装饰器就是这样一个“包装纸”,它可以给函数添加一些额外的功能,而不需要修改函数本身的代码。
装饰器本质上是一个Python函数,它可以让其他函数在不改变其代码的前提下增加额外的功能。装饰器在定义时使用@符号,放在需要装饰的函数定义之前。
让我们来看一个简单的例子:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
在这个例子中,my_decorator是一个装饰器。它的参数func是一个函数,wrapper是一个内部函数,它会在func执行前后打印一些信息。我们使用@my_decorator来应用这个装饰器到say_hello函数上。
当我们调用say_hello()时,输出会是:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
这就是装饰器的魔力所在。我们没有修改say_hello函数的代码,但是通过使用@my_decorator,我们增加了在函数调用前后打印信息的功能。
装饰器的用途非常广泛,比如:
- 日志记录:自动记录函数的调用细节。
- 性能测试:自动记录函数的执行时间。
- 权限校验:检查调用者是否有权执行某个函数。
- 缓存:存储函数的返回值,以便下次调用时直接使用。
装饰器是Python高级编程中非常强大的工具,掌握它们可以帮助你写出更加模块化和可重用的代码。
装饰器就像是给房间涂上一层漆或者挂上一些装饰画,来增强房间的功能或美观,但你并没有改变房间的结构。在Python中,装饰器是一种特殊的函数,它可以给其他函数增加额外的功能,而不需要修改那些函数的代码。你可以用它来添加日志、权限检查、缓存等等。
- 代码复用:装饰器可以让你重用代码,你可以将某个功能封装在装饰器中,并在多个地方应用,而不需要重复编写相同的代码。
- 代码组织:装饰器可以帮助你更好地组织和分离逻辑,使得代码更加清晰。比如,将权限检查的代码从业务逻辑中分离出来。
- 增强功能:装饰器可以在不修改原有函数代码的情况下,给函数增加新的功能,比如添加日志、缓存、计时等。
第十九章 生成可执行文件
首先我们通过pip先下载一个模块:pyinstaller

然后创建一个.py文件,文件内容如下:
import time
a=1
while a<5:
print(a)
a+=1
time.sleep(5)
然后:
pyinstaller --onefile main.py

会生成一个名为dist的文件夹,.exe的可执行文件就在里面,可以直接打开运行。

4926

被折叠的 条评论
为什么被折叠?



