Python-完全入门笔记-全-

Python 完全入门笔记(全)

001:五分钟开启Python编程之旅 🚀

在本节课中,我们将学习如何快速搭建Python开发环境,并编写你的第一个Python程序。整个过程非常简单,即使你从未接触过编程也能轻松跟上。

下载Python解释器

首先,我们需要下载Python解释器。它的作用是将我们编写的代码转换为计算机能够理解的机器码。

  1. 访问官方网站 Python.org
  2. 点击“Downloads”菜单。
  3. 下载最新版本的Python安装程序。

对于Windows用户,在运行安装程序时,请务必勾选“Add Python.exe to PATH”选项,然后点击“Install Now”进行安装。安装成功后,Python解释器就准备就绪了。

下载集成开发环境(IDE)

接下来,我们需要一个集成开发环境(IDE),这是一个让我们编写代码的软件。对于Python开发,有两个流行的选择:PyCharm和VS Code。

如果你已经在使用VS Code,可以继续使用它,只需确保安装了Python扩展插件即可。对于编程新手,我个人认为PyCharm更友好。

如果你想使用PyCharm,请按以下步骤操作:

  1. 访问 jetbrains.com/pycharm
  2. 点击绿色的“Download”按钮。
  3. 选择“Community”(社区版),这是一个免费版本,功能对于初学者来说完全足够。
  4. 根据你的操作系统(如Windows、macOS)选择对应的安装包进行下载。
  5. 运行下载的安装程序,按照提示完成安装。你可以选择创建桌面快捷方式。

安装完成后,运行PyCharm。

创建你的第一个Python项目

打开PyCharm后,让我们开始创建第一个项目。

  1. 点击“New Project”。
  2. 你可以为项目命名,并选择保存位置。这里我们保持默认设置即可。
  3. 在“Python Interpreter”部分,确保选择了我们刚才安装的最新版Python。
  4. 点击“Create”创建项目。

项目创建好后,我们需要在项目中创建一个Python文件。

  1. 在左侧的项目文件区域右键,选择 New -> Python File
  2. 将文件命名为 main.py(或其他你喜欢的名字)。Python文件的后缀是 .py

现在,我们可以在 main.py 文件中开始编写代码了。

编写并运行代码

我们将学习一个最基本的函数:print(),它的作用是将内容输出到控制台。

main.py 文件中,输入以下代码:

print("我喜欢披萨")

要运行这段程序,点击PyCharm右上角的绿色三角形运行按钮。你会在下方的控制台窗口中看到输出结果:“我喜欢披萨”。

你可以添加更多的 print 语句来输出多行内容。例如:

print("我喜欢披萨")
print("它非常美味")

再次运行程序,控制台会依次显示这两行文字。

使用代码注释

在编程中,注释是用来为自己或他人解释代码的笔记,Python解释器会忽略它们,不会输出。

在Python中,使用井号 # 来添加单行注释。

# 这是我的第一个Python程序
print("我喜欢披萨")
print("它非常美味")

当你运行包含注释的代码时,注释部分不会显示在输出中。

总结

本节课我们一起学习了如何搭建Python开发环境。我们完成了两个关键软件的下载与安装:Python解释器PyCharm IDE。接着,我们创建了第一个Python项目,并编写了简单的 print() 语句来向控制台输出信息。最后,我们还了解了如何使用 # 符号添加代码注释。

恭喜你!你已经成功迈出了Python编程的第一步。在下一个主题中,我们将探讨一个更核心的概念:变量

002:变量

在本节课中,我们将要学习Python编程中的核心概念之一:变量。我们将了解什么是变量,以及四种基本的数据类型:字符串、整数、浮点数和布尔值。

什么是变量?🤔

变量是一个用于存储值的容器。变量的行为就像它包含的那个值一样。每个变量都应该有一个唯一的名称。

字符串(String)📝

字符串是由一系列文本字符组成的数据类型。它可以用双引号或单引号表示。

以下是创建和使用字符串变量的步骤:

  1. 赋值:使用等号(=)为变量赋值。
    first_name = "Bro"
    
  2. 打印变量:在print语句中直接使用变量名(不加引号)来输出其值。
    print(first_name)  # 输出:Bro
    
  3. 使用F-String格式化:F-String(格式化字符串)是一种便捷的将变量插入文本的方法。在字符串前加上字母f,然后在字符串内部用花括号{}包裹变量名。
    print(f"Hello, {first_name}")  # 输出:Hello, Bro
    

让我们创建更多字符串变量来巩固理解:

food = "pizza"
email = "bro123@fake.com"
print(f"You like {food}")  # 输出:You like pizza
print(f"Your email is {email}")  # 输出:Your email is bro123@fake.com

整数(Integer)🔢

整数是不带小数部分的数字。它们可以用于算术运算。

上一节我们介绍了文本数据,本节中我们来看看数字数据。以下是创建整数变量的例子:

age = 25
quantity = 3
num_of_students = 30
print(f"You are {age} years old.")  # 输出:You are 25 years old.
print(f"You are buying {quantity} items.")  # 输出:You are buying 3 items.
print(f"Your class has {num_of_students} students.")  # 输出:Your class has 30 students.

重要提示:定义整数时,不要加引号,否则它会被当作字符串处理。

浮点数(Float)⚖️

浮点数是包含小数部分的数字。

了解了整数之后,我们来看看更精确的数字表示方式。以下是浮点数变量的例子:

price = 10.99
gpa = 3.2
distance = 5.5
print(f"The price is ${price}")  # 输出:The price is $10.99
print(f"Your GPA is {gpa}")  # 输出:Your GPA is 3.2
print(f"You ran {distance} km")  # 输出:You ran 5.5 km

布尔值(Boolean)✅❌

布尔值是一种逻辑数据类型,只有两个可能的值:True(真)或False(假)。注意首字母必须大写。

布尔值通常不直接输出,而是用于程序内部的逻辑判断,比如if语句。

以下是布尔值变量的例子:

is_student = True
for_sale = True
is_online = True

现在,我们来看看如何将布尔值与if语句结合使用:

if is_student:
    print("You are a student.")
else:
    print("You are not a student.")
# 输出:You are a student.

if for_sale:
    print("That item is for sale.")
else:
    print("That item is not available.")
# 输出:That item is for sale.

if is_online:
    print("You are online.")
else:
    print("You are offline.")
# 输出:You are online.

总结📚

本节课中我们一起学习了Python中的变量。我们了解到:

  • 变量是一个可重复使用的、用于存储值的容器。
  • 我们探讨了四种基本数据类型:
    • 字符串(String):一系列文本字符。
    • 整数(Integer):不带小数部分的数字。
    • 浮点数(Float):包含小数部分的数字。
    • 布尔值(Boolean):逻辑值,只能是TrueFalse

课后练习:在评论区尝试创建并分享四个变量,分别是一个字符串、一个整数、一个浮点数和一个布尔值,并尽量想出独特的例子。

003:Python中的类型转换

在本节课中,我们将要学习Python中的类型转换。类型转换是将一个数据类型的值转换为另一个数据类型的过程。理解这个概念对于处理用户输入、进行数学运算以及在不同数据类型间传递数据至关重要。

概述:什么是类型转换?🤔

类型转换,也称为类型铸造,是编程中一个基础且重要的概念。它允许我们将一种数据类型的值转换为另一种数据类型。例如,我们可以将整数转换为浮点数,将字符串转换为布尔值,或者将浮点数转换为字符串。

上一节我们介绍了Python的基本数据类型,本节中我们来看看如何在这些类型之间进行转换。

为什么需要类型转换?💡

在实际编程中,类型转换非常有用。主要有以下几个原因:

  1. 处理用户输入:当使用 input() 函数获取用户输入时,无论用户输入什么,程序都会将其视为字符串。如果我们需要对输入的数字进行数学计算,就必须先将字符串转换为整数或浮点数。
  2. 数据格式化与处理:例如,我们有一个浮点数,但只需要它的整数部分。一种方法就是将其转换为整数类型,这会自动截断小数部分。
  3. 逻辑判断:有时我们需要根据一个值是否存在(比如用户是否输入了名字)来进行判断,这时可以将字符串转换为布尔值。

显式类型转换 ✍️

显式类型转换需要我们手动使用Python内置的函数来指定转换的目标类型。这是最常用和最清晰的方式。

以下是Python中用于显式类型转换的四个核心函数:

  • int():将值转换为整数。
  • float():将值转换为浮点数。
  • str():将值转换为字符串。
  • bool():将值转换为布尔值。

为了演示,我们先创建几个不同数据类型的变量:

name = “小明”    # 字符串
age = 21         # 整数
gpa = 1.9        # 浮点数
student = True   # 布尔值

我们可以使用 type() 函数来查看任何变量的数据类型:

print(type(name))   # 输出:<class ‘str’>
print(type(age))    # 输出:<class ‘int’>
print(type(gpa))    # 输出:<class ‘float’>
print(type(student))# 输出:<class ‘bool’>

现在,让我们开始进行显式转换的练习。

1. 转换为浮点数
我们可以将整数 age 转换为浮点数:

age = float(age)
print(age)         # 输出:21.0
print(type(age))   # 输出:<class ‘float’>

2. 转换为整数
我们可以将浮点数 gpa 转换为整数。注意,转换会直接截断小数部分,而不是四舍五入:

gpa = int(gpa)
print(gpa)         # 输出:1
print(type(gpa))   # 输出:<class ‘int’>

3. 转换为字符串
我们可以将布尔值 student 转换为字符串:

student = str(student)
print(student)      # 输出:True
print(type(student))# 输出:<class ‘str’>

4. 转换为布尔值
将其他类型转换为布尔值遵循一些特定规则:

  • 数字:0 转换为 False任何非零值(正数、负数)都转换为 True
  • 字符串:空字符串 “” 转换为 False任何非空字符串(包括只包含空格的字符串)都转换为 True
# 转换数字
age = 21
age = bool(age)
print(age) # 输出:True

age = 0
age = bool(age)
print(age) # 输出:False

# 转换字符串
name = “小明”
name = bool(name)
print(name) # 输出:True

name = “”
name = bool(name)
print(name) # 输出:False

布尔转换在检查用户是否输入了内容时非常有用。

隐式类型转换 🤖

隐式类型转换由Python解释器自动完成,通常发生在不同数据类型的值进行混合运算时。

最常见的例子是整数和浮点数之间的运算:

x = 2    # 整数
y = 2.0  # 浮点数

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/18a7e380e5ef1c7651fed7a7299c3114_5.png)

# 整数除以浮点数,结果会自动提升为精度更高的浮点数类型
x = x / y
print(x)         # 输出:1.0
print(type(x))   # 输出:<class ‘float’>

在这个例子中,Python自动将整数 2 隐式转换为浮点数 2.0 以执行除法,最终结果也是浮点数。

总结 📚

本节课中我们一起学习了Python中的类型转换。

  • 类型转换是将一种数据类型的值转换为另一种数据类型的过程。
  • 显式转换使用内置函数(int(), float(), str(), bool())手动进行,代码意图清晰。
  • 隐式转换由Python在执行表达式(如整数与浮点数运算)时自动完成。
  • 掌握类型转换对于处理用户输入、数据计算和程序逻辑控制至关重要。

记住核心的转换函数,并在需要明确控制数据类型时主动使用它们,这将帮助你写出更健壮、更少错误的代码。

Python入门教程:P04:用户输入与练习

在本节课中,我们将学习如何在Python中接收用户输入。我们将通过input()函数从控制台获取信息,并学习如何将输入的数据转换为合适的类型(如整数或浮点数)以便进行计算。课程包含三个实践练习:创建一个“疯狂填词”游戏、计算矩形面积以及制作一个简易购物车程序。


用户输入基础

上一节我们介绍了变量的概念,本节中我们来看看如何从用户那里获取信息并存储到变量中。

在Python中,我们使用input()函数来接收用户输入。该函数会暂停程序运行,等待用户在控制台输入内容并按回车键。输入的内容会以字符串的形式返回。

以下是一个基本示例,询问用户姓名:

name = input("Enter your name: ")
print(f"Hello, {name}!")

运行此程序时,控制台会显示提示“Enter your name:”,等待你输入姓名。输入后,程序会打印问候语。


输入数据的类型转换

当我们接受用户输入时,输入的数据总是字符串类型。如果我们需要将其用于数学运算,就必须进行类型转换。

例如,如果我们询问年龄并想将其加1:

age = input("Enter your age: ")
# 直接加1会导致错误,因为 age 是字符串
# age = age + 1  # 错误!

为了进行运算,我们需要使用int()float()函数将字符串转换为数字:

age = int(input("Enter your age: "))
age = age + 1
print(f"Next year you will be {age} years old.")

我们也可以在一行代码中完成输入和类型转换,这更为简洁。


练习一:创建“疯狂填词”游戏 🎭

现在,让我们应用所学知识来创建一个“疯狂填词”游戏。在这个游戏中,用户需要提供一些词语(如名词、动词、形容词),程序会将这些词语填入一个预设的故事模板中,生成一个有趣的故事。

以下是创建游戏的步骤:

  1. 设计一个故事模板,其中留出空白让用户填写。
  2. 使用input()函数提示用户输入不同类型的词语。
  3. 将用户输入存储到变量中。
  4. 使用f-string将变量填入故事模板并打印结果。

示例代码:

# 请求用户输入
adjective1 = input("Enter an adjective: ")
noun = input("Enter a noun: ")
adjective2 = input("Enter an adjective: ")
verb = input("Enter a verb: ")
adjective3 = input("Enter an adjective: ")

# 使用f-string填充故事
story = f"Today I went to a {adjective1} zoo. In an exhibit, I saw a {noun}. The {noun} was {adjective2} and {verb}ing. I was {adjective3}!"
print(story)

运行程序,根据提示输入词语,看看会生成什么有趣的故事吧!你可以在评论区分享你的故事。


练习二:计算矩形面积 📐

上一节我们创建了一个文字游戏,本节中我们来看看一个更实用的例子:计算几何图形的面积。

在这个练习中,我们将编写一个程序,接收用户输入的矩形长度和宽度,然后计算并输出其面积。如果需要,我们还可以扩展程序来计算三维长方体的体积。

以下是实现步骤:

  1. 使用input()获取长度和宽度,并立即用float()转换为浮点数。
  2. 使用公式 面积 = 长度 × 宽度 进行计算。
  3. 将结果打印给用户。

示例代码:

# 获取用户输入并转换为数字
length = float(input("Enter the length of the rectangle (cm): "))
width = float(input("Enter the width of the rectangle (cm): "))

# 计算面积
area = length * width
print(f"The area is {area} cm².")

# 扩展:计算长方体体积
height = float(input("Enter the height of the rectangle (cm): "))
volume = length * width * height
print(f"The volume is {volume} cm³.")

尝试运行程序并输入不同的数值,验证计算结果。


练习三:制作购物车程序 🛒

最后,我们来制作一个简易的购物车程序。这个程序将演示如何处理多个输入并进行简单的财务计算。

程序需要完成以下功能:

  1. 询问用户想购买的商品名称、单价和数量。
  2. 根据公式 总价 = 单价 × 数量 计算总金额。
  3. 使用round()函数将总价格式化为两位小数。
  4. 向用户显示清晰的订单摘要。

以下是实现代码:

# 获取商品信息
item = input("What item would you like to buy? ")
price = float(input("What is the price of the item? $"))
quantity = int(input("How many would you like? "))

# 计算总价
total = price * quantity

# 格式化输出,保留两位小数
print(f"You have bought {quantity} {item}(s).")
print(f"Your total is: ${round(total, 2)}")

运行程序,模拟购买几件商品,检查总价计算是否正确。


总结

本节课中我们一起学习了Python中用户输入的核心知识。我们掌握了使用input()函数获取用户输入,理解了输入数据默认为字符串类型,并学会了使用int()float()进行类型转换以用于计算。通过三个循序渐进的练习——“疯狂填词”游戏、矩形面积计算器和购物车程序——我们巩固了这些概念,并看到了它们在实际小程序中的应用。记住,接收和处理用户输入是与程序交互的基础,是构建更复杂应用的第一步。

005:Python中的数学运算与练习 🧮

在本节课中,我们将学习Python中所有必要的数学运算知识。内容涵盖基础算术运算符、内置数学函数、math模块中的函数,并通过几个练习来巩固所学。课程内容较多,我们将分节进行讲解。

基础算术运算符

上一节我们介绍了课程概述,本节中我们来看看最基础的算术运算符。假设我们有一个变量 friends,初始值为0。

friends = 0

以下是Python中常用的算术运算符及其用法:

  • 加法运算符 (+): 用于将变量增加1。

    friends = friends + 1
    

    可以使用增强赋值运算符 += 来简化:

    friends += 1
    

    打印 friends 将得到结果 1

  • 减法运算符 (-): 用于减少变量的值。

    friends = friends - 2
    

    其增强赋值运算符版本为:

    friends -= 2
    
  • 乘法运算符 (*): 用于将变量乘以一个数。

    friends = friends * 3
    

    其增强赋值运算符版本为:

    friends *= 3
    
  • 除法运算符 (/): 用于将变量除以一个数。

    friends = friends / 2
    

    其增强赋值运算符版本为:

    friends /= 2
    
  • 指数运算符 (**): 用于计算一个数的幂。

    friends = friends ** 2
    

    其增强赋值运算符版本为:

    friends **= 2
    
  • 取模运算符 (%): 用于获取除法运算的余数。例如,10 % 3 的结果是 1,因为10除以3余1。这个运算符常用来判断一个数是奇数还是偶数(偶数除以2的余数为0,奇数则为1)。

    remainder = friends % 3
    

内置数学函数

了解了基础运算符后,我们来看看Python内置的一些实用数学函数。假设我们有三个变量:

x = 3.14
y = -4
z = 5

以下是几个常用的内置函数:

  • round() 函数: 用于对数字进行四舍五入。

    result = round(x)  # 结果为 3
    
  • abs() 函数: 用于获取数字的绝对值(即该数到0的距离)。

    result = abs(y)    # 结果为 4
    
  • pow() 函数: 用于计算一个数的幂。pow(4, 3) 表示4的3次方。

    result = pow(4, 3) # 结果为 64
    
  • max() 函数: 用于找出多个值中的最大值。

    result = max(x, y, z) # 结果为 5
    
  • min() 函数: 用于找出多个值中的最小值。

    result = min(x, y, z) # 结果为 -4
    

math模块中的函数与常量

对于更高级的数学运算,我们需要导入Python的 math 模块。首先在代码开头添加:

import math

导入模块后,就可以使用其中的函数和常量了:

  • 常量 math.pi: 提供圆周率π的近似值。

    print(math.pi) # 输出 3.141592653589793
    
  • 常量 math.e: 提供自然常数e的近似值。

    print(math.e)  # 输出 2.718281828459045
    
  • math.sqrt() 函数: 用于计算一个数的平方根。

    x = 9
    result = math.sqrt(x) # 结果为 3.0
    
  • math.ceil() 函数: 用于将一个浮点数向上取整(向正无穷方向)。

    x = 9.1
    result = math.ceil(x) # 结果为 10
    
  • math.floor() 函数: 用于将一个浮点数向下取整(向负无穷方向)。

    x = 9.9
    result = math.floor(x) # 结果为 9
    

练习:运用数学知识解决问题

理论需要结合实践,下面我们通过几个练习来运用刚才学到的知识。

练习1:计算圆的周长 🥧

圆的周长公式为:C = 2 * π * r。我们将编写程序让用户输入半径,然后计算并输出周长。

import math

radius = float(input("请输入圆的半径:"))
circumference = 2 * math.pi * radius
print(f"圆的周长为:{round(circumference, 2)} cm")

练习2:计算圆的面积 🔵

圆的面积公式为:A = π * r²。这个练习与上一个类似。

import math

radius = float(input("请输入圆的半径:"))
area = math.pi * pow(radius, 2)
print(f"圆的面积为:{round(area, 2)} cm²")

练习3:计算直角三角形斜边 📐

根据勾股定理,直角三角形斜边c的长度公式为:c = √(a² + b²)

import math

a = float(input("请输入直角边A的长度:"))
b = float(input("请输入直角边B的长度:"))
c = math.sqrt(pow(a, 2) + pow(b, 2))
print(f"斜边C的长度为:{c}")

总结

本节课中我们一起学习了Python中的核心数学运算。我们从最基础的加减乘除、指数和取模运算符开始,然后探索了Python内置的 round(), abs(), pow(), max(), min() 等函数。接着,我们引入了 math 模块,学习了如何使用 math.pi, math.sqrt(), math.ceil(), math.floor() 等工具来处理更复杂的数学计算。最后,我们通过计算圆的周长与面积、求解直角三角形斜边三个练习,巩固了所有这些知识。掌握这些数学工具,将为后续的编程学习打下坚实的基础。在下一节课中,我们将学习字符串格式化的相关内容。

006:Python中的条件语句(if, elif, else) 🐍

在本节课中,我们将要学习Python中的条件语句。条件语句是编程中进行决策的基础工具,它允许程序根据特定条件的真假来执行不同的代码块。

概述

条件语句的核心是if语句。它的基本逻辑是:如果某个条件为真,那么执行一段代码;否则,可以执行另一段代码。我们还将学习如何使用elif来检查多个条件,以及如何利用布尔值来简化条件判断。


基本if语句

上一节我们介绍了条件语句的概念,本节中我们来看看最基本的if语句结构。

首先,我们通过一个示例来获取用户的年龄:

age = int(input("Enter your age: "))

假设用户想申请信用卡,但规定年龄必须大于或等于18岁。我们可以使用if语句来检查这个条件:

if age >= 18:
    print("You are now signed up.")

代码解释

  • if age >= 18::这是一个条件检查。>=是比较运算符,表示“大于或等于”。如果age变量的值满足这个条件,则执行下方缩进的代码。
  • 注意:和代码缩进。if语句后的冒号:是必须的,而所有属于该if块的代码都必须缩进(通常是4个空格或一个Tab键)。

运行代码,如果输入21,条件为真,程序会打印“You are now signed up.”。如果输入13,条件为假,则if块内的代码会被跳过,程序没有任何输出。


使用else处理其他情况

如果条件不满足时,我们想执行另一段代码,该怎么办呢?这时就需要else语句。

以下是else语句的用法:

if age >= 18:
    print("You are now signed up.")
else:
    print("You must be 18 plus to sign up.")

现在,如果用户年龄小于18岁,程序会打印“You must be 18 plus to sign up.”。else就像一个“备用方案”,当所有if条件都不满足时执行。


使用elif检查多个条件

有时我们需要检查一系列条件,而不仅仅是“是”或“否”。这时可以使用elifelse if的缩写)。

以下是添加多个elif语句的示例:

if age >= 100:
    print("You are too old to sign up.")
elif age >= 18:
    print("You are now signed up.")
elif age < 0:
    print("You haven‘t been born yet.")
else:
    print("You must be 18 plus to sign up.")

重要提示:条件检查是从上到下依次进行的。一旦某个条件为真,就会执行对应的代码块,并跳过后面所有的elifelse。因此,条件的顺序非常重要。在上面的例子中,我们首先检查年龄是否过大,然后检查是否成年,最后检查是否为无效的负数。


更多条件语句示例

为了加深理解,我们来看几个不同的应用场景。

示例1:是/否回答

以下是询问用户是否需要食物的示例:

response = input("Would you like food? (Y/N): ")

if response == "Y":
    print("Have some food.")
else:
    print("No food for you.")

核心概念:注意这里使用了双等号==。在Python中,=是赋值运算符,用于给变量赋值。而==比较运算符,用于检查两个值是否相等。在条件判断中,必须使用==

示例2:检查输入是否为空

以下是检查用户是否输入了名字的示例:

name = input("Enter your name: ")

if name == "":
    print("You did not type in your name.")
else:
    print(f"Hello {name}")

这里,name == ““检查name变量是否等于空字符串。如果用户直接按回车键,条件为真,程序会提示用户未输入名字。


在if语句中使用布尔变量

布尔值(TrueFalse)可以直接用在if语句中,因为条件表达式本身的结果就是布尔值。

以下是使用布尔变量的示例:

for_sale = True

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/26574ae142fbb06f2cf03bdeb4f00980_51.png)

if for_sale:
    print("This item is for sale.")
else:
    print("This item is not for sale.")

因为for_sale本身就是True,所以if for_sale:等价于if True:,条件为真,执行第一条打印语句。如果将for_sale改为False,则会执行else块。

另一个例子:

online = False

if online:
    print("The user is online.")
else:
    print("The user is offline.")


总结

本节课中我们一起学习了Python条件语句的核心用法:

  1. 基本结构:使用ifelifelse来构建决策分支。
  2. 语法要点:条件后要加冒号:,执行代码块必须正确缩进。
  3. 比较运算符:使用==来判断相等,使用>=<等进行比较。
  4. 执行逻辑:程序从上到下检查条件,第一个为真的条件块会被执行,后续块被跳过。
  5. 布尔值应用:可以直接将布尔变量作为条件进行判断。

条件语句是控制程序流程的基石,熟练掌握它将为你后续学习循环、函数等更复杂的编程概念打下坚实基础。

007:编写一个简易计算器 🧮

在本节课中,我们将学习如何使用Python的基础知识,特别是if语句,来编写一个简单的命令行计算器程序。我们将从用户那里获取输入,执行基本的算术运算,并处理一些常见的错误。


概述

我们将创建一个程序,允许用户选择一种算术运算符(加、减、乘、除),然后输入两个数字。程序会根据用户选择的运算符计算结果并输出。我们还将学习如何处理无效的输入。


获取用户输入

首先,我们需要从用户那里获取三个信息:运算符、第一个数字和第二个数字。

以下是获取用户输入的代码:

operator = input("Enter an operator (+, -, *, /): ")
num1 = float(input("Enter the first number: "))
num2 = float(input("Enter the second number: "))

注意:我们使用float()函数将输入的字符串转换为浮点数。这是因为input()函数返回的是字符串类型,直接进行数学运算会导致字符串拼接,而不是数值计算。例如,"10" + "11"的结果是"1011",而不是21


使用条件语句判断运算

上一节我们介绍了如何获取用户输入。本节中,我们来看看如何使用if-elif-else语句来判断用户选择了哪种运算符,并执行相应的计算。

以下是判断和计算的逻辑结构:

if operator == "+":
    result = num1 + num2
elif operator == "-":
    result = num1 - num2
elif operator == "*":
    result = num1 * num2
elif operator == "/":
    result = num1 / num2
else:
    print(f"{operator} is not a valid operator.")

代码解释

  • ifelif语句检查operator变量的值。
  • 如果匹配到有效的运算符(+, -, *, /),则进行相应的计算并将结果存储在result变量中。
  • 如果operator的值不匹配任何有效选项,则执行else块中的代码,向用户提示输入无效。


输出结果与格式化

在计算出结果后,我们需要将其输出给用户。为了使结果更易读,我们可以使用round()函数对结果进行四舍五入。

以下是输出结果的代码:

if operator == "+":
    result = num1 + num2
    print(round(result, 3))
elif operator == "-":
    result = num1 - num2
    print(round(result, 3))
elif operator == "*":
    result = num1 * num2
    print(round(result, 3))
elif operator == "/":
    result = num1 / num2
    print(round(result, 3))
else:
    print(f"{operator} is not a valid operator.")

公式/函数

  • round(number, ndigits):将number四舍五入到ndigits位小数。如果省略ndigits,则四舍五入到最接近的整数。
  • 例如,round(5.6789, 2) 返回 5.68


完整代码示例

将以上所有部分组合起来,就得到了我们简易计算器的完整代码。

# 获取用户输入
operator = input("Enter an operator (+, -, *, /): ")
num1 = float(input("Enter the first number: "))
num2 = float(input("Enter the second number: "))

# 根据运算符进行计算并输出结果
if operator == "+":
    result = num1 + num2
    print(round(result, 3))
elif operator == "-":
    result = num1 - num2
    print(round(result, 3))
elif operator == "*":
    result = num1 * num2
    print(round(result, 3))
elif operator == "/":
    result = num1 / num2
    print(round(result, 3))
else:
    print(f"{operator} is not a valid operator.")


总结

本节课中我们一起学习了如何构建一个基础的Python计算器。我们实践了以下几个核心概念:

  1. 使用input()函数获取用户输入。
  2. 使用float()进行类型转换,确保进行数值运算。
  3. 使用if-elif-else条件语句来控制程序流程,根据不同的输入执行不同的代码块。
  4. 使用round()函数格式化输出结果。
  5. 通过else分支处理无效的用户输入,使程序更加健壮。

这个程序是理解Python基础控制流和用户交互的一个绝佳起点。你可以尝试在此基础上增加更多功能,例如支持更多运算符(如求余%、幂运算**),或者加入循环让用户可以连续计算。

008:体重转换器练习 🏋️‍♂️

在本节课中,我们将要学习如何使用Python的if语句来创建一个体重单位转换器程序。该程序能够根据用户的选择,将体重在磅(pounds)和公斤(kilograms)之间进行转换。

概述

我们将创建一个交互式程序,其核心逻辑是:

  1. 获取用户输入的体重数值。
  2. 获取用户输入的体重单位(公斤K或磅L)。
  3. 使用if语句判断单位,并进行相应的换算。
  4. 输出转换后的结果。

上一节我们介绍了if语句的基本用法,本节中我们来看看如何将其应用到一个实际的编程练习中。

程序构建步骤

以下是构建体重转换器程序的具体步骤。

第一步:获取用户输入

首先,我们需要获取用户输入的体重数值和单位。

weight = float(input("请输入您的体重:"))
unit = input("请输入单位(K 代表公斤, L 代表磅):")
  • input()函数用于获取用户输入。
  • float()函数将输入的字符串转换为浮点数,以便进行数学计算。

第二步:使用if语句进行判断和转换

接下来,我们使用if-elif-else语句来判断用户输入的单位,并执行相应的换算。

if unit == "K":
    # 将公斤转换为磅
    weight = weight * 2.205
    unit = "LBS"
elif unit == "L":
    # 将磅转换为公斤
    weight = weight / 2.205
    unit = "KGS"
else:
    # 处理无效输入
    print(f"您输入的单位 ‘{unit}’ 无效。")
  • 核心公式
    • 公斤转磅:weight_in_pounds = weight_in_kilograms * 2.205
    • 磅转公斤:weight_in_kilograms = weight_in_pounds / 2.205
  • 在转换的同时,我们也更新unit变量的值,以便在输出时显示正确的单位。

第三步:格式化并输出结果

最后,我们需要输出转换后的结果。为了提升可读性,我们使用round()函数对结果进行四舍五入,并保留一位小数。

if unit == "LBS" or unit == "KGS":
    print(f"您的体重是 {round(weight, 1)} {unit}。")
  • round(weight, 1):将weight变量的值四舍五入到小数点后一位。
  • 注意,输出语句被放在了if条件内,只有当用户输入了有效的单位(KL)时才会执行,避免了在输入无效时也打印结果。

完整程序代码

将以上所有步骤组合起来,就得到了完整的体重转换器程序。

# 获取用户输入
weight = float(input("请输入您的体重:"))
unit = input("请输入单位(K 代表公斤, L 代表磅):")

# 判断单位并进行转换
if unit == "K":
    weight = weight * 2.205
    unit = "LBS"
elif unit == "L":
    weight = weight / 2.205
    unit = "KGS"
else:
    print(f"您输入的单位 ‘{unit}’ 无效。")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/7fe7139509e0d8ba0d869a8d2ac5a86f_13.png)

# 输出转换结果
if unit == "LBS" or unit == "KGS":
    print(f"您的体重是 {round(weight, 1)} {unit}。")

程序运行示例

让我们看看程序运行时的效果。

示例 1:将公斤转换为磅

请输入您的体重:81
请输入单位(K 代表公斤, L 代表磅):K
您的体重是 178.6 LBS。

示例 2:将磅转换为公斤

请输入您的体重:180
请输入单位(K 代表公斤, L 代表磅):L
您的体重是 81.6 KGS。

示例 3:输入无效单位

请输入您的体重:150
请输入单位(K 代表公斤, L 代表磅):Pizza
您输入的单位 ‘Pizza’ 无效。

总结

本节课中我们一起学习了如何综合运用input()float()类型转换、if-elif-else条件判断以及round()函数,构建了一个实用的体重单位转换器。这个练习巩固了if语句的用法,并展示了如何将基本语法组合起来解决实际问题。你可以尝试修改这个程序,例如增加更多单位(如斤、盎司)的转换功能。

Python超全入门教程:P09:温度转换程序

在本节课中,我们将创建一个温度转换程序作为练习。我们将学习如何接收用户输入,使用条件判断语句,以及应用数学公式进行单位转换。


程序结构与用户输入

首先,我们需要询问用户当前的温度单位。我们将使用 input() 函数来获取输入,并将其存储在一个变量中。

以下是程序初始化的步骤:

  1. 询问温度单位。
  2. 询问温度数值。
  3. 将温度数值转换为浮点数以便计算。
unit = input("当前温度单位是摄氏度还是华氏度? (C/F): ")
temp = float(input("请输入温度值: "))

使用条件判断进行逻辑分支

上一节我们获取了用户输入,本节中我们来看看如何根据不同的单位执行不同的转换逻辑。我们将使用 if-elif-else 语句来处理三种情况:摄氏度转华氏度、华氏度转摄氏度以及无效的单位输入。

以下是核心的条件判断结构:

if unit == "C" or unit == "c":
    # 摄氏度转华氏度的代码
    pass
elif unit == "F" or unit == "f":
    # 华氏度转摄氏度的代码
    pass
else:
    # 处理无效输入
    print(f"‘{unit}’ 不是一个有效的温度单位。")


摄氏度转华氏度

如果用户输入的单位是摄氏度(‘C‘),我们需要将其转换为华氏度。转换公式如下:

公式: 华氏度 = (摄氏度 × 9/5) + 32

在代码中,我们使用这个公式进行计算,并使用 round() 函数将结果四舍五入到一位小数,使输出更整洁。

if unit == "C" or unit == "c":
    converted_temp = round((temp * 9 / 5) + 32, 1)
    print(f"温度为 {converted_temp} 华氏度。")


华氏度转摄氏度

如果用户输入的单位是华氏度(‘F‘),我们则执行相反的转换。转换公式如下:

公式: 摄氏度 = (华氏度 - 32) × 5/9

同样地,我们应用公式并格式化输出。

elif unit == "F" or unit == "f":
    converted_temp = round((temp - 32) * 5 / 9, 1)
    print(f"温度为 {converted_temp} 摄氏度。")


总结

本节课中我们一起学习了如何构建一个完整的温度转换程序。我们实践了从用户获取输入、使用 if-elif-else 进行条件分支、应用数学公式进行计算,以及格式化输出结果。这个程序很好地结合了输入、处理和输出这三个基本编程步骤。在下一节课中,我们将尝试创建一个功能更丰富的计算器程序。

010:逻辑运算符 🧠

在本节课中,我们将要学习Python中的逻辑运算符。逻辑运算符用于条件语句(如if语句)中,帮助我们组合和判断多个条件。我们将介绍三种逻辑运算符:andornot,并通过简单的例子理解它们的行为。


逻辑运算符简介

逻辑运算符用于处理条件语句。它们可以检查两个或多个条件是否成立。以下是三种主要的逻辑运算符:

  • and:检查所有条件是否都为真。
  • or:检查至少一个条件是否为真。
  • not:反转条件的真假值。


使用 and 运算符

and 运算符要求其连接的所有条件都为真,整个表达式才为真。如果其中任何一个条件为假,整个表达式就为假。

以下是一个使用 and 运算符检查温度范围的例子:

temp = 25

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/d8147ec1d2bf4a124432b69e1a01005a_23.png)

if temp > 0 and temp < 30:
    print("温度适宜。")
else:
    print("温度不佳。")

在这个例子中,只有当温度大于0 并且 小于30时,程序才会打印“温度适宜。”。因为当前温度是25,所以输出结果为“温度适宜。”。

如果温度是-10或40,由于不满足两个条件同时为真,程序将执行else分支,打印“温度不佳。”。


使用 or 运算符

上一节我们介绍了需要所有条件都为真的 and 运算符。本节中我们来看看 or 运算符,它只要求至少一个条件为真。

我们可以用 or 运算符改写上面的温度检查程序,逻辑变为:如果温度在适宜范围内,则视为不佳。

temp = 40

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/d8147ec1d2bf4a124432b69e1a01005a_38.png)

if temp <= 0 or temp >= 30:
    print("温度不佳。")
else:
    print("温度适宜。")

在这个例子中,如果温度小于等于0 或者 大于等于30,条件就为真,打印“温度不佳。”。当前温度是40,满足第二个条件,所以输出“温度不佳。”。

如果温度是20,两个条件都为假,则执行else分支,打印“温度适宜。”。


使用 not 运算符

not 运算符与前两者不同,它不连接多个条件,而是对一个条件的结果进行取反。

假设我们有一个布尔变量表示天气是否晴朗:

sunny = False

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/d8147ec1d2bf4a124432b69e1a01005a_54.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/d8147ec1d2bf4a124432b69e1a01005a_56.png)

if not sunny:
    print("今天是阴天。")
else:
    print("今天是晴天。")

not sunny 会将 sunny 的值反转。因为 sunnyFalse,所以 not sunny 就是 True,程序会打印“今天是阴天。”。

如果将 sunny 设为 True,那么 not sunny 就是 False,程序会执行else分支,打印“今天是晴天。”。


总结

本节课中我们一起学习了Python中的三种逻辑运算符:

  1. and:连接多个条件,要求所有条件都为真,结果才为真。
  2. or:连接多个条件,要求至少一个条件为真,结果就为真。
  3. not:对一个布尔条件取反,真变假,假变真。

这些运算符是构建复杂条件判断的基础,在未来的编程实践中会经常用到。

Python超全入门教程:P11:条件表达式 🎯

在本节课中,我们将要学习Python中的条件表达式。这是一种简洁的语法,可以在一行代码中实现简单的条件判断,是if-else语句的快捷方式。


什么是条件表达式? 🤔

条件表达式是一种单行的快捷方式,用于替代if-else语句。如果你熟悉其他编程语言,它也被称为三元运算符。其核心行为是根据一个条件,在两个值中选择一个进行打印或赋值。

其基本公式如下:

X if condition else Y

如果条件为真,则返回X;如果条件为假,则返回Y


基础示例:判断数字正负 🔢

让我们从一个简单的例子开始。我们将创建一个变量num,并判断它是正数还是负数。

num = 5
print("positive" if num > 0 else "negative")

在这个例子中,条件num > 0为真,因此会打印"positive"。如果num-5,条件为假,则会打印"negative"


判断数字奇偶性 🔄

上一节我们介绍了如何判断正负,本节中我们来看看如何判断一个数字是奇数还是偶数。这次,我们将把结果赋值给一个变量。

num = 6
result = "even" if num % 2 == 0 else "odd"
print(result)

这里,num % 2 == 0检查num是否能被2整除。如果能,则result被赋值为"even",否则为"odd"。当num为6时,打印"even";如果num为5,则会打印"odd"


比较两个数字的大小 ⚖️

接下来,我们使用条件表达式来找出两个数字中的最大值和最小值。

a = 6
b = 7

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/e58cbf877cdc2bb205e37d41a6c36e36_31.png)

max_num = a if a > b else b
print(max_num)  # 输出 7

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/e58cbf877cdc2bb205e37d41a6c36e36_33.png)

min_num = a if a < b else b
print(min_num)  # 输出 6

第一个表达式判断a是否大于b,是则返回a,否则返回b,从而得到最大值。第二个表达式则用于获取最小值。


根据年龄判断状态 👶👨

条件表达式也可以用于处理更复杂的逻辑,例如根据年龄判断一个人是成年人还是儿童。

age = 25
status = "adult" if age >= 18 else "child"
print(status)  # 输出 adult

如果age是13,条件age >= 18为假,status将被赋值为"child"


根据温度描述天气 🌡️

让我们看一个根据温度描述天气的例子。

temperature = 30
weather = "hot" if temperature > 20 else "cold"
print(weather)  # 输出 hot

如果temperature是20,条件temperature > 20为假,weather将被赋值为"cold"


根据用户角色设置访问权限 🔐

最后一个例子,我们将根据用户的角色来决定其访问权限级别。

user_role = "admin"
access_level = "full access" if user_role == "admin" else "limited access"
print(access_level)  # 输出 full access

如果user_role"guest",条件user_role == "admin"为假,access_level将被赋值为"limited access"


总结 📝

本节课中我们一起学习了Python中的条件表达式。它是一种单行的快捷方式,用于替代if-else语句,类似于其他编程语言中的三元运算符。通过条件表达式,我们可以根据一个条件,在两个值中选择一个进行打印或赋值。其核心公式是:

X if condition else Y

我们通过判断数字正负、奇偶性、比较大小、判断年龄状态、描述天气以及设置用户权限等多个例子,实践了条件表达式的用法。掌握这个语法能让你的代码更加简洁明了。

Python超全入门教程:P12:Python字符串方法详解 🧵

在本节课中,我们将学习Python中一些实用的字符串方法。字符串本质上是一系列字符的集合。掌握这些方法能帮助我们更高效地处理和验证文本数据。课程最后,我们将通过一个验证用户输入的小练习来巩固所学知识。


获取字符串长度

首先,我们来看如何获取字符串的长度。这可以通过内置的 len() 函数实现,它会返回字符串包含的字符总数(包括空格)。

以下是具体步骤:

  1. 使用 input() 函数获取用户输入的全名。
  2. 使用 len() 函数计算该字符串的长度。
  3. 将结果存储在一个变量中并打印出来。
name = input("Enter your full name: ")
result = len(name)
print(result)

例如,输入 “Bro Code”,len() 函数将返回 8


查找字符位置

上一节我们介绍了如何获取字符串长度,本节中我们来看看如何查找特定字符在字符串中的位置。

我们可以使用 .find() 方法来定位某个字符首次出现的位置。索引从 0 开始计数。

以下是具体步骤:

  1. 在字符串变量后使用 .find() 方法,并在括号内指定要查找的字符。
  2. 该方法返回一个整数,代表字符的索引位置。
  3. 如果未找到字符,则返回 -1
name = "Bro Code"
result = name.find(" ")
print(result)  # 输出: 3
result = name.find("B")
print(result)  # 输出: 0
result = name.find("q")
print(result)  # 输出: -1

若要查找字符最后一次出现的位置,可以使用 .rfind() 方法。

result = name.rfind("o")
print(result)  # 输出: 5

修改字符串大小写

字符串提供了几种方法来改变其字符的大小写。

以下是具体方法:

  • .capitalize(): 将字符串的第一个字符转换为大写,其余字符转换为小写。
  • .upper(): 将字符串中的所有字符转换为大写。
  • .lower(): 将字符串中的所有字符转换为小写。

这些方法会返回一个新的字符串,不会修改原始字符串。

name = "bro code"
name = name.capitalize()
print(name)  # 输出: Bro code

name = name.upper()
print(name)  # 输出: BRO CODE

name = name.lower()
print(name)  # 输出: bro code

检查字符串内容

有时我们需要检查字符串是否只包含特定类型的字符。

以下是两个常用的检查方法:

  • .isdigit(): 如果字符串只包含数字,则返回 True,否则返回 False
  • .isalpha(): 如果字符串只包含字母(不包含空格、数字等),则返回 True,否则返回 False
name = "Bro123"
print(name.isdigit())  # 输出: False
print(name.isalpha())  # 输出: False

num = "123"
print(num.isdigit())   # 输出: True

letters = "BroCode"
print(letters.isalpha()) # 输出: True

统计与替换字符

接下来,我们学习如何统计特定字符的出现次数,以及如何替换字符串中的部分内容。

以下是具体方法:

  • .count(): 统计某个字符或子字符串在字符串中出现的次数。
  • .replace(): 将字符串中所有的指定字符替换为另一个字符。这是最实用的字符串方法之一。

phone_number = "123-456-7890"
# 统计短横线 '-' 的数量
dash_count = phone_number.count("-")
print(dash_count)  # 输出: 2

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/ba79e9203646a5ab158f7040e17a4ea7_14.png)

# 将短横线替换为空格
new_number = phone_number.replace("-", " ")
print(new_number)  # 输出: 123 456 7890

# 移除所有短横线(替换为空字符串)
new_number = phone_number.replace("-", "")
print(new_number)  # 输出: 1234567890

探索更多字符串方法

Python 提供了丰富的字符串方法。如果你想查看完整列表,可以使用 help() 函数。

print(help(str))

这将打印出所有可用的字符串方法及其简要说明,例如 capitalize, casefold, center, count, encode, endswith 等。


实践练习:验证用户名输入

现在,让我们运用所学的字符串方法来完成一个练习:验证用户输入的用户名是否符合规则。

规则如下:

  1. 用户名长度不能超过12个字符。
  2. 用户名不能包含空格。
  3. 用户名不能包含数字。

我们将分步实现这些验证。

username = input("Enter a username: ")

# 规则1:检查长度
if len(username) > 12:
    print("Your username can't be more than 12 characters.")
# 规则2:检查是否包含空格
elif username.find(" ") != -1:  # 如果找到空格,find() 返回值不是 -1
    print("Your username can't contain spaces.")
# 规则3:检查是否只包含字母
elif not username.isalpha():    # 如果不是纯字母
    print("Your username can't contain numbers.")
else:
    print(f"Welcome {username}!")

总结

本节课中我们一起学习了Python中多个核心的字符串方法。我们掌握了如何获取字符串长度(len())、查找字符位置(.find(), .rfind())、修改大小写(.capitalize(), .upper(), .lower())、检查内容(.isdigit(), .isalpha())以及统计和替换字符(.count(), .replace())。最后,我们通过一个验证用户名的综合练习,将这些方法应用到了实际场景中。熟练掌握这些方法将极大地提升你处理文本数据的能力。

013:字符串索引 📍

在本节课中,我们将要学习Python中的字符串索引。索引允许我们使用方括号(索引操作符)来访问字符串中的特定字符或子串。我们将从基础概念开始,逐步学习如何指定起始位置、结束位置和步长,并通过信用卡号的实例来掌握这些技巧。

索引操作符基础

索引操作符是一对方括号 [],跟在字符串变量名之后。通过它,我们可以访问字符串序列中的元素。计算机的计数从0开始,因此字符串的第一个字符索引是0。

例如,我们定义一个信用卡号字符串:

credit_number = "1234-5678-9012-3456"

要访问第一个字符,我们可以这样做:

print(credit_number[0])  # 输出: 1

将索引改为1,则访问第二个字符:

print(credit_number[1])  # 输出: 2

使用起始和结束索引切片

上一节我们介绍了如何访问单个字符,本节中我们来看看如何获取字符串的一部分,即“切片”。

切片语法是在方括号内使用冒号分隔起始和结束索引:[start:end]。起始索引是包含的,而结束索引是排除的。

例如,要获取前四个数字:

print(credit_number[0:4])  # 输出: 1234
# 或者省略起始索引0,效果相同
print(credit_number[:4])   # 输出: 1234

要获取第二组数字“5678”,我们需要找到正确的索引位置:

print(credit_number[5:9])  # 输出: 5678

如果需要从某个位置开始直到字符串末尾的所有字符,可以省略结束索引:

print(credit_number[5:])   # 输出: 5678-9012-3456

使用负索引

除了从左边开始计数,Python还支持负索引,从字符串末尾开始计数,-1代表最后一个字符。

以下是负索引的用法示例:

print(credit_number[-1])   # 输出: 6 (最后一个字符)
print(credit_number[-2])   # 输出: 5 (倒数第二个字符)

理解步长参数

到目前为止,我们学习了如何获取连续的子串。现在,让我们看看如何使用步长来间隔地获取字符。

步长是索引操作符中的第三个参数,语法为 [start:end:step]。如果省略起始和结束,只指定步长,则需要两个冒号 [::step]

例如,获取字符串中每隔一个的字符:

print(credit_number[::2])  # 输出: 13-68 91 35

将步长改为3,则每隔两个字符取一个:

print(credit_number[::3])  # 输出: 14-7-26

实践应用示例

让我们将所学知识应用到两个实际场景中。

场景一:获取信用卡号的后四位

这在隐藏敏感信息时很常用。我们可以使用负索引轻松实现:

last_digits = credit_number[-4:]
print(f"XXXX-XXXX-XXXX-{last_digits}")
# 输出: XXXX-XXXX-XXXX-3456

场景二:反转字符串

通过将步长设置为-1,我们可以轻松地反转整个字符串:

reversed_number = credit_number[::-1]
print(reversed_number)
# 输出: 6543-2109-8765-4321

总结

本节课中我们一起学习了Python字符串索引的核心知识。我们了解到:

  1. 索引操作符 [] 用于访问字符串中的元素。
  2. 基本索引从0开始,字符串[索引] 获取单个字符。
  3. 切片语法 [起始:结束] 用于获取子串,起始包含,结束排除。
  4. 负索引 [-1] 用于从字符串末尾开始访问。
  5. 步长参数 [::步长] 用于以特定间隔获取字符,设置步长为-1可以反转字符串。

通过理解和练习这些概念,你将能够灵活地操作和处理Python中的任何字符串数据。

014:电子邮件切片器练习 📧

在本节课中,我们将创建一个电子邮件切片器程序。这个程序将演示如何利用字符串索引和切片功能,将一个完整的电子邮件地址拆分为用户名和域名两部分。这是一个非常实用的练习,能帮助你巩固对字符串操作的理解。

概述

我们将编写一个Python程序,接收用户输入的电子邮件地址,然后自动提取出其中的用户名和域名。核心步骤包括:获取用户输入、定位“@”符号的位置、使用字符串切片功能进行分割。

程序构建步骤

1. 获取用户输入

首先,我们需要获取用户输入的电子邮件地址。我们将使用 input() 函数来实现。

email = input("Enter your email: ")

运行这行代码时,程序会显示提示信息“Enter your email:”,并等待用户输入。用户输入的电子邮件地址将被存储在名为 email 的变量中。

2. 定位“@”符号

为了分割电子邮件,我们需要知道“@”符号在字符串中的位置。字符串对象有一个内置的 index() 方法,可以返回指定字符第一次出现的索引位置。

at_index = email.index('@')

这里,email.index('@') 会返回“@”符号在 email 字符串中的索引号,并将其赋值给变量 at_index

3. 分割用户名和域名

有了“@”符号的位置,我们就可以使用字符串切片来提取用户名和域名了。

  • 提取用户名:用户名是从字符串开头到“@”符号之前的部分。
    username = email[:at_index]
    
    切片 [:at_index] 表示从索引0开始(可省略),到 at_index 结束(不包含该索引的字符)。

  • 提取域名:域名是从“@”符号之后到字符串结尾的部分。为了不包含“@”符号本身,起始索引需要是 at_index + 1
    domain = email[at_index + 1:]
    
    切片 [at_index + 1:] 表示从 at_index 的下一个字符开始,一直到字符串末尾。

4. 输出结果

最后,我们使用f-string格式化字符串来清晰地输出结果。

print(f"Your username is {username} and domain is {domain}.")

代码优化

上面的代码分步清晰,易于理解。我们也可以将步骤合并,写出更简洁(但可能稍难阅读)的代码:

email = input("Enter your email: ")
username = email[:email.index('@')]
domain = email[email.index('@') + 1:]
print(f"Your username is {username} and domain is {domain}.")

这种方法减少了变量 at_index 的定义,直接将 email.index('@') 的调用嵌入到切片操作中。两种方式在功能上是完全等效的,你可以根据对代码可读性的偏好进行选择。

程序运行示例

假设我们运行程序并输入电子邮件 bro123@gmail.com

  1. 程序提示:Enter your email:
  2. 用户输入:bro123@gmail.com
  3. 程序计算:
    • email.index('@') 返回 6(“@”在字符串中的位置)。
    • username = email[:6] 得到 "bro123"
    • domain = email[7:] 得到 "gmail.com"
  4. 程序输出:Your username is bro123 and domain is gmail.com.

总结

本节课我们一起学习了如何构建一个电子邮件切片器。通过这个练习,我们实践了以下几个核心Python概念:

  • 使用 input() 函数获取用户输入。
  • 使用字符串的 index() 方法查找特定字符的位置。
  • 使用字符串切片操作 [start:end] 来提取子字符串。
  • 使用f-string进行格式化的输出。

这个程序虽然简单,但很好地串联了字符串处理的基础知识,是巩固学习成果的绝佳练习。

Python超全入门教程:P15:格式化说明符详解 🎯

在本节课中,我们将要学习Python中的格式化说明符。格式化说明符用于f-string中,允许我们根据插入的特定标志来格式化数值。通过掌握它们,你可以精确控制数字的显示方式,例如小数精度、对齐方式和千位分隔符等。


什么是格式化说明符?

上一节我们介绍了f-string的基本用法,本节中我们来看看如何通过格式化说明符来增强其功能。

格式化说明符是放置在f-string占位符内、冒号之后的一系列标志。其基本语法如下:

f"{value:flags}"

其中,value是要格式化的变量或表达式,flags是控制格式化的各种标志。


控制小数精度

以下是控制浮点数显示精度的常用方法。

在冒号后使用 . 加上数字和 f 可以指定小数点后显示的位数。例如,.2f 表示保留两位小数。

price1 = 3.14159
price2 = -987.65
price3 = 12.34

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/430574d3a031a6a2ebda2fe02f026c51_15.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/430574d3a031a6a2ebda2fe02f026c51_16.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/430574d3a031a6a2ebda2fe02f026c51_17.png)

print(f"价格1是:${price1:.2f}")
print(f"价格2是:${price2:.2f}")
print(f"价格3是:${price3:.2f}")

输出结果:

价格1是:$3.14
价格2是:$-987.65
价格3是:$12.34

你可以通过改变数字来调整精度,例如 .1f 显示一位小数,.3f 显示三位小数。如果原始数字小数位数不足,系统会自动补零。


控制宽度与对齐

以下是控制输出字段宽度和对齐方式的方法。

在冒号后直接添加一个数字可以指定该值占用的最小字符宽度。例如,:10 表示分配10个字符的空间。

print(f"价格1是:${price1:10.2f}")
print(f"价格2是:${price2:10.2f}")
print(f"价格3是:${price3:10.2f}")

默认情况下,数字会在分配的宽度内右对齐。要改变对齐方式,可以使用以下符号:

  • <:左对齐
  • >:右对齐(默认)
  • ^:居中对齐

# 左对齐
print(f"价格1是:${price1:<10.2f}")
# 居中对齐
print(f"价格2是:${price2:^10.2f}")


显示数值符号

以下是控制数值正负号显示的方法。

使用 + 标志可以在正数前显示加号,负数前则正常显示减号。使用空格 标志可以在正数前保留一个空格,使正负数能对齐显示。

print(f"价格1是:${price1:+10.2f}")  # 显示 +3.14
print(f"价格2是:${price2:+10.2f}")  # 显示 -987.65
print(f"价格3是:${price3: 10.2f}")  # 正数前有空格,与负数对齐

添加千位分隔符

以下是让大数字更易读的方法。

使用逗号 , 标志可以为数字添加千位分隔符。

price1 = 3000.14159
price2 = -987654.65
price3 = 1200.34

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/430574d3a031a6a2ebda2fe02f026c51_45.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/430574d3a031a6a2ebda2fe02f026c51_47.png)

print(f"价格1是:${price1:,.2f}")
print(f"价格2是:${price2:,.2f}")
print(f"价格3是:${price3:,.2f}")

输出结果:

价格1是:$3,000.14
价格2是:$-987,654.65
价格3是:$1,200.34


组合使用标志

你可以将多个标志组合在一起使用,以实现复杂的格式化需求。标志的顺序通常是:对齐、符号、宽度、逗号、精度。

例如,组合使用千位分隔符、两位小数精度并为正数添加加号:

print(f"价格1是:${price1:+,.2f}")
print(f"价格2是:${price2:+,.2f}")
print(f"价格3是:${price3:+,.2f}")


总结

本节课中我们一起学习了Python f-string中的格式化说明符。我们了解了如何通过 :.2f 这样的标志来控制小数精度,使用 :10:<10 来控制宽度和对齐,用 + 或空格来显示符号,以及用 , 来添加千位分隔符。这些工具能帮助你以清晰、专业的方式格式化输出数据。记住,你可以在一个占位符中组合多种标志来满足特定的显示要求。

016:While循环详解 🔄

在本节课中,我们将要学习Python中的while循环。while循环是一种控制流语句,它允许我们重复执行一段代码,只要指定的条件保持为真。这对于处理需要持续验证或重复的任务非常有用,例如验证用户输入。


什么是While循环? 🤔

while循环会在某个条件保持为真时,重复执行其内部的代码块。它的基本结构如下:

while condition:
    # 要执行的代码

condition(条件)评估为True时,循环体内的代码就会执行。执行完毕后,程序会再次检查条件。如果条件仍为True,则再次执行循环体;如果条件变为False,则退出循环,继续执行循环之后的代码。


示例一:验证用户姓名输入

上一节我们介绍了while循环的基本概念,本节中我们来看看一个具体的应用:确保用户输入了有效的姓名。

我们将创建一个程序,持续要求用户输入姓名,直到他们输入了非空的内容为止。

name = input("Enter your name: ")

while name == "":
    print("You did not enter your name.")
    name = input("Enter your name: ")

print(f"Hello {name}")

代码解释:

  1. 首先,我们使用input()函数获取用户的姓名。
  2. 然后,while循环检查name变量是否等于空字符串""
  3. 如果条件为真(即用户没有输入任何内容),则执行循环体:打印一条消息,并再次提示用户输入。
  4. 这个过程会一直重复,直到用户输入了内容(使name != ""),此时循环条件变为假,程序退出循环。
  5. 最后,打印问候语。

运行示例:

Enter your name: (用户直接按回车)
You did not enter your name.
Enter your name: (用户再次直接按回车)
You did not enter your name.
Enter your name: Alex
Hello Alex

这个例子展示了while循环的核心优势:它允许我们持续执行代码,直到满足某个退出条件。


示例二:验证年龄为正数

理解了基本循环后,我们来看另一个常见场景:确保用户输入的数字是有效的,例如年龄必须为正数。

以下是实现此功能的步骤:

  1. 获取用户输入的年龄,并将其转换为整数。
  2. 使用while循环检查年龄是否小于0。
  3. 如果条件为真(年龄为负),则提示用户并重新获取输入。
  4. 当用户输入有效的年龄(>=0)后,退出循环并显示结果。
age = int(input("Enter your age: "))

while age < 0:
    print("Age can't be negative.")
    age = int(input("Enter your age: "))

print(f"You are {age} years old.")

运行示例:

Enter your age: -5
Age can't be negative.
Enter your age: -1
Age can't be negative.
Enter your age: 25
You are 25 years old.

避免无限循环 ⚠️

在使用while循环时,必须确保循环最终有办法停止,否则会陷入无限循环。例如,下面的代码就会导致无限循环,因为循环体内没有改变name变量的值,条件name == ""永远为真。

name = ""
while name == "":
    print("You did not enter your name.")
# 缺少重新为 name 赋值的代码,循环无法退出!

因此,在循环体内提供一种改变条件状态的“退出策略”至关重要,通常是通过重新获取用户输入或更新某个变量来实现。


示例三:使用逻辑运算符 not

while循环的条件可以结合逻辑运算符来构建更复杂的逻辑。例如,我们可以让程序持续运行,直到用户输入特定的退出指令(如字母‘Q’)。

以下是实现一个简单“食物喜好收集器”的步骤:

  1. 提示用户输入一种喜欢的食物。
  2. 使用while not (food == "q")作为条件,意思是“当食物不是‘q’时继续循环”。
  3. 在循环内,打印用户喜欢的食物,并提示输入下一种。
  4. 当用户输入‘q’时,条件变为假,退出循环。
food = input("Enter a food you like (q to quit): ")

while not food == "q":
    print(f"You like {food}")
    food = input("Enter another food you like (q to quit): ")

print("Bye")

运行示例:

Enter a food you like (q to quit): pizza
You like pizza
Enter another food you like (q to quit): sushi
You like sushi
Enter another food you like (q to quit): ramen
You like ramen
Enter another food you like (q to quit): q
Bye

示例四:使用逻辑运算符 or

我们还可以使用or运算符来检查多个条件。只要其中一个条件为真,整个循环条件就为真。

让我们创建一个程序,要求用户输入一个1到10之间的数字。

以下是实现逻辑的步骤:

  1. 获取用户输入并转换为整数。
  2. 使用while循环,条件是number < 1 number > 10。这意味着输入的数字只要不在1-10范围内,循环就会继续。
  3. 在循环内,提示输入无效并重新获取数字。
  4. 输入有效后,退出循环并显示数字。
number = int(input("Enter a number between 1 through 10: "))

while number < 1 or number > 10:
    print(f"{number} is not valid.")
    number = int(input("Enter a number between 1 through 10: "))

print(f"Your number is {number}")

运行示例:

Enter a number between 1 through 10: 0
0 is not valid.
Enter a number between 1 through 10: 15
15 is not valid.
Enter a number between 1 through 10: 5
Your number is 5

总结 📝

本节课中我们一起学习了Python的while循环。

  • 核心概念while循环用于在指定条件保持为真时重复执行代码块。其基本结构为:
    while condition:
        # 循环体
    
  • 主要用途:非常适合用于验证用户输入,确保输入符合要求,也适用于许多需要重复执行直到满足特定条件的场景。
  • 关键要点
    1. 必须在循环体内提供改变循环条件的逻辑,否则可能导致无限循环
    2. 可以配合逻辑运算符(如notorand)来构建更复杂的循环条件。
    3. 当条件变为False时,程序会退出循环,继续执行后续代码。

通过以上示例,你应该已经掌握了while循环的基本用法和常见模式。在实践中多加练习,你会更熟练地运用它来控制程序的流程。

Python入门教程:P17:复利计算器 💰

在本节课中,我们将学习如何使用Python创建一个复利计算器。我们将从用户那里获取初始本金、利率和时间,然后计算并显示最终的账户余额。过程中,我们会运用while循环来确保用户输入有效的数值。


概述

复利是金融中的一个核心概念,指利息不仅基于初始本金计算,还基于之前累积的利息计算。本节课,我们将编写一个程序,根据用户输入的本金、年利率和投资年限,计算复利增长后的总金额。


获取用户输入

首先,我们需要从用户那里获取三个关键数据:本金、利率和时间。我们将使用while循环来确保用户输入的值是有效的(例如,不能为负数)。

以下是获取本金、利率和时间的步骤:

  1. 获取本金:提示用户输入初始投资金额,并确保该值大于0。
  2. 获取利率:提示用户输入年利率,并确保该值大于0。
  3. 获取时间:提示用户输入投资年限,并确保该值大于0。

我们将使用while循环持续提示用户,直到输入有效的数值为止。

# 获取本金
principle = float(input("请输入本金金额:"))
while principle <= 0:
    print("本金不能小于或等于0。")
    principle = float(input("请输入本金金额:"))

# 获取利率
rate = float(input("请输入年利率(百分比):"))
while rate <= 0:
    print("利率不能小于或等于0。")
    rate = float(input("请输入年利率(百分比):"))

# 获取时间
time = int(input("请输入投资年限(整数):"))
while time <= 0:
    print("时间不能小于或等于0。")
    time = int(input("请输入投资年限(整数):"))

计算复利

在获取了所有必要的输入后,下一步是计算复利。复利的计算公式如下:

总金额 = 本金 × (1 + 利率/100) ^ 时间

在Python中,我们可以使用幂运算符 ** 来实现这个计算。

# 计算复利
total = principle * (1 + rate / 100) ** time

显示结果

计算完成后,我们需要将结果以清晰、易读的格式展示给用户。我们将使用格式化字符串来确保金额显示为两位小数。

# 显示结果
print(f"经过 {time} 年后的余额为:${total:.2f}")

完整代码示例

将以上所有步骤组合起来,就得到了完整的复利计算器程序。

# 获取本金
principle = float(input("请输入本金金额:"))
while principle <= 0:
    print("本金不能小于或等于0。")
    principle = float(input("请输入本金金额:"))

# 获取利率
rate = float(input("请输入年利率(百分比):"))
while rate <= 0:
    print("利率不能小于或等于0。")
    rate = float(input("请输入年利率(百分比):"))

# 获取时间
time = int(input("请输入投资年限(整数):"))
while time <= 0:
    print("时间不能小于或等于0。")
    time = int(input("请输入投资年限(整数):"))

# 计算复利
total = principle * (1 + rate / 100) ** time

# 显示结果
print(f"经过 {time} 年后的余额为:${total:.2f}")

另一种循环实现方式

除了使用带条件的while循环,我们还可以使用while True循环配合break语句来实现相同的输入验证逻辑。这种方式允许用户输入0值(如果业务逻辑允许的话)。

以下是使用while True循环的代码片段:

# 获取本金(允许0值)
while True:
    principle = float(input("请输入本金金额:"))
    if principle < 0:
        print("本金不能小于0。")
    else:
        break

# 获取利率(允许0值)
while True:
    rate = float(input("请输入年利率(百分比):"))
    if rate < 0:
        print("利率不能小于0。")
    else:
        break

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c78aeb72a18674e25209852c2d86c3c0_6.png)

# 获取时间(允许0值)
while True:
    time = int(input("请输入投资年限(整数):"))
    if time < 0:
        print("时间不能小于0。")
    else:
        break

总结

在本节课中,我们一起学习了如何创建一个Python复利计算器。我们掌握了以下关键点:

  1. 使用while循环进行输入验证,确保用户提供有效的本金、利率和时间。
  2. 应用复利公式 总金额 = 本金 × (1 + 利率/100) ^ 时间 进行计算。
  3. 使用格式化字符串来美观地输出结果。
  4. 了解了while True循环与break语句结合使用的另一种输入验证模式。

通过这个项目,你不仅练习了基础数学运算在编程中的应用,还加强了对循环控制流的理解。你可以尝试修改程序,例如增加计算每月复利或显示每年余额明细的功能。

018:Python中的For循环

在本节课中,我们将要学习Python中的for循环。for循环用于执行固定次数的代码块,它可以遍历一个范围、一个字符串、一个序列或任何可迭代的对象。与while循环相比,for循环更适合在需要执行固定次数操作的情况下使用。

基础语法与遍历数字范围

上一节我们介绍了for循环的基本概念,本节中我们来看看它的基础语法。for循环的基本结构是:for 变量 in 可迭代对象:,然后缩进编写需要重复执行的代码块。

以下是使用for循环和range()函数计数的基本示例:

for x in range(1, 11):
    print(x)
  • for x in range(1, 11)::这行代码创建了一个循环。x是循环变量,在每次迭代中,它会依次被赋值为range(1, 11)生成的数字。range(1, 11)生成从1开始到10结束(不包含11)的数字序列。
  • print(x):这是循环体,会为序列中的每个数字执行一次,打印出当前的x值。

运行这段代码,会依次打印出数字1到10。

反向遍历与步长控制

了解了正向计数后,我们来看看如何反向计数以及控制计数的步长。

要反向计数,可以将range()函数嵌套在reversed()函数中:

for i in reversed(range(1, 11)):
    print(i)
print("Happy New Year!")

这段代码会从10倒数到1,然后打印“Happy New Year!”。

range()函数还可以接受第三个参数,用于指定步长。以下是按步长为2进行计数的示例:

for x in range(1, 11, 2):
    print(x)
  • range(1, 11, 2):生成从1开始、小于11、步长为2的序列,即 [1, 3, 5, 7, 9]
    如果将步长改为3,则会按3计数:[1, 4, 7, 10]

遍历字符串

for循环不仅可以遍历数字范围,还可以遍历字符串等任何可迭代对象。

以下是一个遍历信用卡号字符串的示例:

credit_card = "1234-5678-9012-3456"
for x in credit_card:
    print(x)

在这个例子中,循环变量x会依次代表字符串中的每一个字符,包括数字和短横线“-”,并逐个打印出来。

循环控制:continue与break

在循环中,有两个重要的关键字可以控制流程:continuebreak。它们同样适用于while循环。

continue语句用于跳过当前循环的剩余语句,直接进入下一次迭代。例如,我们希望跳过不吉利的数字13:

for x in range(1, 21):
    if x == 13:
        continue
    print(x)

x等于13时,continue语句被执行,print(x)被跳过,循环直接进入下一次迭代(x=14),因此输出中不会出现13。

break语句用于立即终止整个循环。例如,我们希望在遇到13时停止计数:

for x in range(1, 21):
    if x == 13:
        break
    print(x)

x等于13时,break语句被执行,整个for循环被立即终止,因此输出只到12为止。

总结

本节课中我们一起学习了Python中的for循环。我们了解到:

  • for循环用于对可迭代对象进行遍历,执行固定次数的操作。
  • 可以使用range()函数生成数字序列进行遍历,并可以通过参数控制起始值、结束值和步长。
  • for循环可以遍历字符串,逐个处理其中的字符。
  • 使用continue关键字可以跳过当前迭代,使用break关键字可以提前终止整个循环。
  • 在需要执行不确定次数(如等待用户输入)时,while循环更合适;而在需要遍历已知序列或执行固定次数操作时,for循环通常是更好的选择。

Python入门教程:P19:嵌套循环详解 🔄

在本节课中,我们将要学习Python中的嵌套循环。嵌套循环是指一个循环结构位于另一个循环结构的代码块内部。通过掌握嵌套循环,你可以处理更复杂的数据遍历和模式生成任务。


嵌套循环的基本概念

上一节我们介绍了单层循环,本节中我们来看看嵌套循环。嵌套循环可以理解为“循环中的循环”。外部的循环称为外层循环,内部的循环称为内层循环

以下是嵌套循环的几种常见组合形式:

  • while 循环内部嵌套另一个 while 循环。
  • for 循环内部嵌套另一个 for 循环。
  • for 循环内部嵌套 while 循环。
  • while 循环内部嵌套 for 循环。

具体使用哪种组合取决于实际需求。


第一个示例:重复打印数字序列

让我们从一个简单的例子开始:打印数字1到9。我们将使用一个 for 循环来实现。

for x in range(1, 10):
    print(x, end=' ')

代码解释range(1, 10) 生成从1到9的数字序列。print(x, end=' ') 中的 end=' ' 参数让每次打印后以空格结尾,而不是默认的换行,从而使所有数字打印在同一行。

运行上述代码,输出结果为:

1 2 3 4 5 6 7 8 9

现在,如果我们想将这个打印1到9的过程重复三次,该怎么办呢?我们可以创建另一个循环来包裹住现有的循环。

for x in range(3):
    for y in range(1, 10):
        print(y, end=' ')
    print()

代码解释

  1. 外层循环 for x in range(3): 会执行3次。
  2. 内层循环 for y in range(1, 10): 在每次外层循环执行时,都会完整地打印数字1到9。
  3. 内层循环结束后,print() 会输出一个换行,这样每次打印的1-9序列就会单独成行。

运行这段代码,输出结果为:

1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9

这就是一个典型的嵌套循环结构。外层循环控制重复的次数,内层循环执行具体的重复任务。


实战项目:打印自定义矩形

理解了基本原理后,我们来创建一个更有趣的项目:根据用户输入的行数、列数和符号,打印一个矩形图案。

我们将复用之前的代码结构,但使其动态化。

# 获取用户输入
rows = int(input("请输入行数:"))
columns = int(input("请输入列数:"))
symbol = input("请输入要使用的符号:")

# 使用嵌套循环打印矩形
for x in range(rows):
    for y in range(columns):
        print(symbol, end='')
    print()

代码解释

  1. 使用 input() 获取用户输入,并用 int() 将行数和列数转换为整数。
  2. 外层循环 for x in range(rows): 控制矩形的行数。
  3. 内层循环 for y in range(columns): 控制每一行中符号的个数(列数)。
  4. print(symbol, end='') 打印用户指定的符号,并且不换行。
  5. 内层循环结束后,print() 输出换行,切换到下一行。

运行示例1

请输入行数:4
请输入列数:10
请输入要使用的符号:$
$$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$

运行示例2

请输入行数:3
请输入列数:5
请输入要使用的符号:*
*****
*****
*****

总结

本节课中我们一起学习了Python的嵌套循环。

  • 核心概念:嵌套循环是一个循环结构内包含另一个循环结构。外层循环每执行一次,内层循环会完整地执行其所有迭代。
  • 关键点:循环的类型(forwhile)可以任意组合,内层循环的代码必须正确缩进。
  • 应用:嵌套循环非常适合处理需要多重遍历的任务,例如生成二维图案(如矩形、三角形)、遍历二维列表(矩阵)或进行组合计算。

通过理解和练习嵌套循环,你将能够解决更多复杂的编程问题。

020:用Python创建倒计时器

在本节课中,我们将学习如何使用Python创建一个倒计时器程序。我们将运用之前学过的循环、输入处理和time模块等知识,最终实现一个可以显示小时、分钟和秒的数字时钟式倒计时器。

导入时间模块

首先,我们需要导入Python的time模块。这个模块包含一个非常有用的函数。

以下是time模块中sleep函数的使用方法:

time.sleep()

在括号内指定秒数,程序就会“休眠”相应的时间。例如,time.sleep(3)会让程序暂停3秒,然后继续执行后续代码。

获取用户输入

接下来,我们需要询问用户希望设置多长的倒计时时间。

我们将创建一个变量my_time来存储用户输入的秒数。通过input函数获取输入,并使用int()进行类型转换,确保它是一个整数。

my_time = int(input("请输入倒计时时间(秒):"))

构建倒计时循环

为了创建倒计时,我们需要一个循环。虽然while循环和for循环都可以实现,但这里我们选择使用for循环。

我们将使用range()函数生成一个数字序列。为了让倒计时从大到小进行,我们需要让rangemy_time开始,到0结束,并且步长为-1。

for x in range(my_time, 0, -1):
    time.sleep(1)
    print(x)
print("时间到!")

这段代码会每秒打印一个递减的数字,直到0时打印“时间到!”。

格式化为数字时钟

上一节我们实现了一个基本的倒计时。本节中,我们来看看如何将其格式化为更易读的“时:分:秒”数字时钟格式。

我们需要从总秒数x中分别计算出小时、分钟和秒。

以下是计算各时间单位的公式:

  • seconds = x % 60
  • minutes = int(x / 60) % 60
  • hours = int(x / 3600)

我们使用模运算%来确保分钟和秒数不会超过60。计算完成后,使用f-string和格式说明符02d来确保每个单位都显示为两位数字(例如“05”秒)。

整合后的循环部分代码如下:

for x in range(my_time, 0, -1):
    seconds = x % 60
    minutes = int(x / 60) % 60
    hours = int(x / 3600)
    print(f"{hours:02d}:{minutes:02d}:{seconds:02d}")
    time.sleep(1)
print("时间到!")

完整代码示例

以下是倒计时器程序的完整代码:

import time

my_time = int(input("请输入倒计时时间(秒):"))

for x in range(my_time, 0, -1):
    seconds = x % 60
    minutes = int(x / 60) % 60
    hours = int(x / 3600)
    print(f"{hours:02d}:{minutes:02d}:{seconds:02d}")
    time.sleep(1)

print("时间到!")

总结

本节课中我们一起学习了如何用Python创建一个倒计时器。我们回顾并应用了多个核心概念:使用time.sleep()函数控制程序暂停,通过input()获取用户输入,利用for循环和range()函数实现递减计数,并运用模运算%和格式化字符串将总秒数转换为标准的“时:分:秒”格式。这个练习很好地融合了循环、数学运算和输入/输出处理,是巩固基础知识的优秀实践。

Python入门教程:P21:Python列表、集合与元组详解 🍎

在本节课中,我们将要学习Python中三种核心的集合类型:列表、集合和元组。集合可以被视为一个用于存储多个值的“单一变量”。我们将逐一探讨它们的特点、创建方法以及基本操作。


列表:有序且可变

列表是Python中最常用的集合类型。它使用方括号 [] 来创建,其中的元素是有序的,并且可以被修改。

创建列表的公式如下:

fruits = ["apple", "orange", "banana", "coconut"]

列表是有序的,这意味着元素的位置是固定的。我们可以通过索引来访问列表中的元素,索引从0开始。

访问列表元素的代码示例:

print(fruits[0])  # 输出: apple
print(fruits[1])  # 输出: orange

如果尝试访问不存在的索引,Python会抛出 IndexError 错误。

我们还可以使用切片操作来获取列表的一部分。

切片操作的代码示例:

print(fruits[0:3])   # 输出: ['apple', 'orange', 'banana']
print(fruits[::2])   # 输出: ['apple', 'banana']
print(fruits[::-1])  # 输出: ['coconut', 'banana', 'orange', 'apple']

列表是可迭代的,这意味着我们可以使用 for 循环来遍历其中的每一个元素。

遍历列表的代码示例:

for fruit in fruits:
    print(fruit)

以下是列表常用方法的简介:

  • append(): 在列表末尾添加一个元素。
    fruits.append("pineapple")
    
  • remove(): 移除列表中第一个匹配的元素。
    fruits.remove("apple")
    
  • insert(): 在指定索引位置插入一个元素。
    fruits.insert(0, "pineapple")
    
  • sort(): 对列表进行升序排序。
    fruits.sort()
    
  • reverse(): 反转列表中元素的顺序。
    fruits.reverse()
    
  • clear(): 清空列表中的所有元素。
    fruits.clear()
    
  • index(): 返回指定元素第一次出现的索引。
    print(fruits.index("apple"))
    
  • count(): 统计指定元素在列表中出现的次数。
    print(fruits.count("banana"))
    

要查看列表的所有可用方法,可以使用 dir() 函数。要了解某个方法的具体功能,可以使用 help() 函数。

查看列表方法的代码示例:

print(dir(fruits))
print(help(fruits))

此外,我们可以使用 len() 函数获取列表的长度,使用 in 操作符检查某个值是否存在于列表中。

获取长度和成员检查的代码示例:

print(len(fruits))          # 输出列表长度
print("apple" in fruits)    # 输出: True
print("pineapple" in fruits) # 输出: False

上一节我们介绍了有序且可变的列表,本节中我们来看看另一种集合类型:集合。

集合:无序且元素唯一

集合使用花括号 {} 来创建。它的主要特点是元素无序不允许重复

创建集合的公式如下:

fruits = {"apple", "orange", "banana", "coconut"}

集合是无序的,这意味着每次打印时元素的顺序可能不同,并且不能通过索引来访问元素。尝试使用索引会导致 TypeError

虽然集合中的元素本身不可变(不能通过索引修改),但我们可以向集合中添加或移除元素。

以下是集合的一些常用方法:

  • add(): 向集合中添加一个元素。
    fruits.add("pineapple")
    
  • remove(): 从集合中移除一个指定元素。如果元素不存在会报错。
    fruits.remove("apple")
    
  • pop(): 随机移除并返回集合中的一个元素。
    removed_fruit = fruits.pop()
    
  • clear(): 清空集合中的所有元素。
    fruits.clear()
    

与列表类似,我们可以使用 len() 函数获取集合的大小,使用 in 操作符检查成员关系。

集合操作的代码示例:

print(len(fruits))
print("apple" in fruits)

由于集合自动去重,它非常适合用于检查成员资格或消除重复项。


了解了列表和集合之后,我们来看最后一种基础集合类型:元组。

元组:有序且不可变

元组使用圆括号 () 来创建。它与列表类似,都是有序的,但关键区别在于元组是不可变的,创建后不能修改其中的元素。

创建元组的公式如下:

fruits = ("apple", "orange", "banana", "coconut", "coconut")

元组的不可变性带来了一个优点:执行速度比列表更快。因此,如果你需要存储一组不需要修改的数据,使用元组是更高效的选择。

由于不可变,元组的方法比列表少得多,主要有两个:

  • index(): 返回指定元素第一次出现的索引。
    print(fruits.index("apple"))
    
  • count(): 统计指定元素在元组中出现的次数。
    print(fruits.count("coconut")) # 输出: 2
    

同样,我们可以使用 len()in 操作符和 for 循环来处理元组。

元组基本操作的代码示例:

print(len(fruits))
print("pineapple" in fruits)
for fruit in fruits:
    print(fruit)

总结 🎯

本节课中我们一起学习了Python中三种基础的集合类型:

  1. 列表 []: 有序可变,允许重复元素。功能最丰富,是最通用的集合。
  2. 集合 {}: 无序且元素唯一,不允许重复。主要用于成员测试和去重。
  3. 元组 (): 有序不可变,允许重复元素。因为不可变,所以处理速度比列表快,适用于存储不应更改的数据。

记住,集合是用于存储多个值的单一变量。根据你的需求——是否需要顺序、是否允许修改、是否允许重复——来选择最合适的类型。在接下来的课程中,我们将探讨另一种强大的集合:字典。

022:购物车程序 🛒

在本节课中,我们将创建一个购物车程序。这是一个练习项目,旨在巩固上一节关于列表、集合和元组的知识。通过实践操作这些集合类型,我们能更好地掌握它们的用法。因此,在继续深入学习之前,我们通过这个练习来熟悉它们。

我们将使用两个列表来分别存储商品和价格,并计算总价。

程序结构概述

我们将创建两个空列表:一个用于存储商品名称,另一个用于存储对应的价格。同时,我们还需要一个变量来累计总价。程序的核心是一个while循环,它会持续询问用户想要购买的商品,直到用户选择退出。最后,程序会显示购物车内的所有商品及其总价。

初始化变量

首先,我们声明两个空列表和一个总价变量。

foods = []
prices = []
total = 0

我们选择使用列表而非元组,是因为元组不可变,而我们需要向购物车中添加商品。我们也没有使用集合,因为集合是无序的,而我们需要按顺序显示购物车内容。

构建主循环

我们将使用一个while True循环来持续接收用户输入。循环内部需要包含一个退出机制。

while True:

在循环中,我们首先询问用户想要购买什么商品。

    food = input(“请输入要购买的商品(输入‘q’退出):”)

为了允许用户输入大写或小写的‘Q’来退出,我们使用.lower()方法将输入转换为小写后再进行比较。

    if food.lower() == “q”:
        break

如果用户输入的不是退出指令,我们就将商品添加到foods列表中,并询问该商品的价格。

    else:
        foods.append(food)
        price = float(input(f”请输入 {food} 的价格:”))
        prices.append(price)

注意,我们使用float()函数将价格输入转换为浮点数,以便后续进行数学计算。

显示购物车内容

退出循环后,我们需要显示用户购物车中的所有商品。

print(“-” * 5 + “您的购物车” + “-” * 5)
for food in foods:
    print(food, end=“ ”)
print()

这里,print(food, end=“ ”)中的end=“ ”参数将默认的换行符替换为空格,使得所有商品名称在同一行水平显示。如果你希望垂直显示,可以移除end参数。

计算并显示总价

接下来,我们遍历prices列表,将所有价格相加得到总价。

for price in prices:
    total += price

最后,我们打印出总金额。

print()
print(f“总价为:${total:.2f}”)

完整代码与运行示例

将以上所有部分组合起来,就得到了完整的购物车程序。

foods = []
prices = []
total = 0

while True:
    food = input(“请输入要购买的商品(输入‘q’退出):”)
    if food.lower() == “q”:
        break
    else:
        foods.append(food)
        price = float(input(f”请输入 {food} 的价格:”))
        prices.append(price)

print(“-” * 5 + “您的购物车” + “-” * 5)
for food in foods:
    print(food, end=“ ”)
print()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/134375871f7f27dcf798d1d2fd297c54_11.png)

for price in prices:
    total += price

print()
print(f“总价为:${total:.2f}”)

运行示例:

请输入要购买的商品(输入‘q’退出):披萨
请输入 披萨 的价格:5.99
请输入要购买的商品(输入‘q’退出):汉堡
请输入 汉堡 的价格:3.50
请输入要购买的商品(输入‘q’退出):热狗
请输入 热狗 的价格:1.75
请输入要购买的商品(输入‘q’退出):q
-----您的购物车-----
披萨 汉堡 热狗

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/134375871f7f27dcf798d1d2fd297c54_17.png)

总价为:$11.24

总结

本节课中,我们一起学习了如何创建一个简单的购物车程序。我们实践了使用列表来存储动态数据,利用while循环处理持续的用户输入,并通过条件判断(检查用户是否输入‘q’)来控制循环的退出。这个练习巩固了对列表、循环和条件语句的理解,是迈向更复杂Python编程的坚实一步。

023:二维集合详解 📚

在本节课中,我们将要学习Python中的二维集合。二维集合,即由集合(如列表、元组)构成的集合,非常适合用来表示网格或矩阵形式的数据,类似于Excel电子表格。

什么是二维集合? 🤔

一个二维列表本质上就是一个由列表组成的列表。它非常灵活,可以用来创建数据网格。除了列表,你也可以创建二维元组或由其他集合构成的二维集合。

创建二维列表 🛠️

首先,我们创建三个一维列表作为示例:

fruits = ["苹果", "橙子", "香蕉", "椰子"]
vegetables = ["芹菜", "胡萝卜", "土豆"]
meats = ["鸡肉", "鱼肉", "火鸡肉"]

接下来,我们将这三个列表组合成一个二维列表:

groceries = [fruits, vegetables, meats]

或者,你也可以不预先命名内部列表,直接创建二维列表:

groceries = [
    ["苹果", "橙子", "香蕉", "椰子"],
    ["芹菜", "胡萝卜", "土豆"],
    ["鸡肉", "鱼肉", "火鸡肉"]
]

访问二维列表元素 🔍

访问二维列表的元素与访问一维列表略有不同。如果只使用一个索引,你将得到整个“行”(即一个内部列表)。

print(groceries[0])  # 输出:['苹果', '橙子', '香蕉', '椰子']

要访问具体的元素,你需要使用两个索引,类似于坐标系统:第一个索引代表行,第二个索引代表列。

print(groceries[0][0])  # 输出:苹果 (第0行,第0列)
print(groceries[1][1])  # 输出:胡萝卜 (第1行,第1列)

以下是访问所有元素的示例:

  • groceries[0][0] = 苹果
  • groceries[0][1] = 橙子
  • groceries[0][2] = 香蕉
  • groceries[1][0] = 芹菜
  • groceries[1][1] = 胡萝卜
  • groceries[1][2] = 土豆
  • groceries[2][0] = 鸡肉
  • groceries[2][1] = 鱼肉
  • groceries[2][2] = 火鸡肉

遍历二维列表 🔄

要遍历二维列表中的所有元素,可以使用嵌套循环。

使用单层循环只能遍历每一行:

for collection in groceries:
    print(collection)

使用嵌套循环则可以遍历每一个具体的元素:

for row in groceries:
    for food in row:
        print(food, end=" ")  # 使用空格代替换行
    print()  # 每行结束后换行

其他类型的二维集合 📦

二维集合不限于列表。你可以根据需求选择不同的数据结构。

  • 列表的元组:外部是元组,内部是列表。元组不可变,但内部的列表可变。
    mixed_collection = (["a", "b"], ["c", "d"])
    
  • 元组的元组:外部和内部都是元组。完全不可变,访问速度快。
    tuple_2d = (("a", "b"), ("c", "d"))
    
  • 集合的元组:外部是元组,内部是集合。注意,集合是无序的。
    set_tuple = ({"a", "b"}, {"c", "d"})
    

实践练习:创建电话键盘 ⌨️

现在,让我们通过一个练习来巩固所学知识:创建一个模拟电话键盘的二维集合。

由于键盘数字顺序固定且不需要修改,我们选择使用二维元组,因为它有序且不可变,性能更好。

numpad = (
    ("1", "2", "3"),
    ("4", "5", "6"),
    ("7", "8", "9"),
    ("*", "0", "#")
)

使用嵌套循环来打印这个键盘布局:

for row in numpad:
    for num in row:
        print(num, end=" ")
    print()

输出结果将是一个整齐的网格:

1 2 3
4 5 6
7 8 9
* 0 #

总结 📝

本节课中我们一起学习了Python中的二维集合。我们了解到:

  1. 二维集合是由集合(列表、元组等)构成的集合,用于表示网格数据。
  2. 访问元素需要使用两个索引:[行][列]
  3. 遍历二维集合通常需要嵌套循环。
  4. 可以根据数据的特性(是否需要有序、是否可变)选择列表、元组或集合来构建二维结构。
  5. 通过创建电话键盘的练习,我们实践了如何应用二维元组来解决实际问题。

当你需要处理表格、矩阵或任何网格状数据时,二维集合是一个非常实用的工具。

024:使用Python创建问答游戏 🎮

在本节课中,我们将学习如何使用Python创建一个简单的问答游戏。我们将通过定义问题、选项、答案,并处理用户输入和计分,来构建一个完整的交互式程序。


定义数据与变量 📝

首先,我们需要声明游戏所需的所有集合和变量。我们将使用元组存储问题和选项,使用列表存储用户的猜测,并使用变量来跟踪分数和当前问题编号。

以下是游戏所需的核心数据结构:

questions = (
    "How many elements are in the periodic table?",
    "Which animal lays the largest eggs?",
    "What is the most abundant gas in Earth's atmosphere?",
    "How many bones are in the human body?",
    "Which planet in the solar system is the hottest?"
)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b94cb2384287477683c64cf51c55ff26_1.png)

options = (
    ("A. 116", "B. 117", "C. 118", "D. 119"),
    ("A. Whale", "B. Crocodile", "C. Elephant", "D. Ostrich"),
    ("A. Oxygen", "B. Nitrogen", "C. Carbon Dioxide", "D. Hydrogen"),
    ("A. 206", "B. 207", "C. 208", "D. 209"),
    ("A. Mercury", "B. Venus", "C. Earth", "D. Mars")
)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b94cb2384287477683c64cf51c55ff26_3.png)

answers = ("C", "D", "A", "A", "B")
guesses = []
score = 0
question_num = 0

显示问题与选项 💬

上一节我们定义了游戏的数据。本节中,我们来看看如何将这些数据显示给用户。我们将遍历问题列表,并同时显示对应的问题和选项。

以下是显示逻辑的核心代码:

for question in questions:
    print("----------------------")
    print(question)
    for option in options[question_num]:
        print(option)
    # ... 后续将在此处添加用户输入和判断逻辑
    question_num += 1


处理用户输入与判断答案 ✅

在显示了问题和选项之后,程序需要接收用户的答案,并判断其是否正确。我们将使用 input() 函数获取输入,并将其与正确答案进行比较。

以下是处理用户输入和计分的步骤:

  1. 获取用户输入并将其转换为大写。
  2. 将用户的猜测添加到 guesses 列表中。
  3. 判断猜测是否正确,并更新分数。
    guess = input("Enter (A, B, C, D): ").upper()
    guesses.append(guess)

    if guess == answers[question_num]:
        score += 1
        print("CORRECT!")
    else:
        print("INCORRECT!")
        print(f"{answers[question_num]} is the correct answer.")

显示最终结果 📊

当所有问题都回答完毕后,我们需要向用户展示最终结果,包括正确答案、用户的答案以及最终得分百分比。

以下是显示结果的代码:

print("----------------------")
print("       RESULTS        ")
print("----------------------")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b94cb2384287477683c64cf51c55ff26_27.png)

print("answers: ", end="")
for answer in answers:
    print(answer, end=" ")
print()

print("guesses: ", end="")
for guess in guesses:
    print(guess, end=" ")
print()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b94cb2384287477683c64cf51c55ff26_29.png)

score = int(score / len(questions) * 100)
print(f"Your score is: {score}%")

总结 🎉

本节课中我们一起学习了如何使用Python创建一个基础的问答游戏。我们掌握了如何组织数据(使用元组和列表)、如何通过循环遍历和显示内容、如何处理用户输入并进行逻辑判断,以及如何计算和展示最终结果。你可以尝试修改问题、选项和答案来创建属于自己的个性化问答游戏。

025:Python字典详解 📚

在本节课中,我们将要学习Python中一个非常重要的数据结构——字典。字典是Python四种基本集合类型之一,它通过键值对的形式存储数据,具有有序、可变的特性,并且不允许键重复。

概述

字典由一系列键值对组成,每个键都映射到一个值。常见的例子包括ID与姓名、商品与价格等。本节课我们将以国家和首都为例,学习如何创建、访问和操作字典。


创建字典 🛠️

要创建一个字典,需要使用一对花括号 {},并在其中定义键值对。键和值之间用冒号 : 分隔,不同的键值对之间用逗号 , 分隔。

以下是创建一个名为 capitals 的国家-首都字典的代码:

capitals = {
    "USA": "Washington, D.C.",
    "India": "New Delhi",
    "China": "Beijing",
    "Russia": "Moscow"
}

查看字典的属性和方法 🔍

在深入学习之前,我们可以使用 dir() 函数来查看字典对象的所有属性和方法。

print(dir(capitals))

如果想获得更详细的说明,可以使用 help() 函数。

help(capitals)

访问字典的值 🔑

上一节我们介绍了如何创建字典,本节中我们来看看如何访问其中的值。要获取字典中某个键对应的值,可以使用 get() 方法。

print(capitals.get("USA"))  # 输出: Washington, D.C.
print(capitals.get("India")) # 输出: New Delhi

如果查找的键不存在,get() 方法会返回 None。我们可以利用这一点进行条件判断。

if capitals.get("Japan"):
    print("That capital exists.")
else:
    print("That capital doesn‘t exist.")


更新字典 ✏️

字典是可变的,这意味着我们可以添加新的键值对,或者修改已有的值。

以下是更新字典的几种方法:

  • 添加或更新键值对:使用 update() 方法。
    capitals.update({"Germany": "Berlin"})
    capitals.update({"USA": "Detroit"})  # 更新已有键的值
    print(capitals)
    

  • 删除键值对
    • 使用 pop() 方法删除指定键。
      capitals.pop("China")
      
    • 使用 popitem() 方法删除最后插入的键值对。
      capitals.popitem()
      

  • 清空字典:使用 clear() 方法。
    capitals.clear()
    


遍历字典 🔄

字典提供了几种方法来获取其内容的视图,便于我们进行遍历操作。

以下是遍历字典的几种方式:

  • 获取所有键:使用 keys() 方法。
    for key in capitals.keys():
        print(key)
    

  • 获取所有值:使用 values() 方法。
    for value in capitals.values():
        print(value)
    

  • 同时获取键和值:使用 items() 方法。这是最常用的遍历方式,它会返回一个包含键值对元组的可迭代对象。
    for key, value in capitals.items():
        print(f"{key}: {value}")
    


总结

本节课中我们一起学习了Python字典的核心知识。我们了解到字典是一种由键值对组成的、有序可变的集合,其键不允许重复

我们掌握了以下关键操作:

  1. 使用花括号 {} 创建字典。
  2. 使用 get() 方法安全地访问值。
  3. 使用 update()pop()popitem()clear() 方法来修改字典。
  4. 使用 keys()values()items() 方法来遍历字典的不同部分。

字典在未来的编程项目中会非常有用,例如在游戏开发中存储角色属性或游戏状态。希望本教程能帮助你打下坚实的基础!

Python入门教程:P26:创建小卖部程序 🍿

在本节课中,我们将创建一个模拟电影院小卖部功能的程序。我们将使用字典来管理菜单项及其价格,并通过这个程序来熟悉字典的基本操作。


创建菜单字典

首先,我们需要创建一个名为 menu 的字典,用于存储商品名称(键)和对应的价格(值)。

menu = {
    "pizza": 3.00,
    "nachos": 4.50,
    "popcorn": 6.00,
    "fries": 2.50,
    "chips": 1.00,
    "soft pretzel": 2.00,
    "soda": 1.50,
    "lemonade": 1.25
}

初始化购物车和总价

为了记录用户选择的商品和计算总价,我们需要初始化一个空的购物车列表和一个总价变量。

cart = []
total = 0.0

向用户展示菜单

为了让用户看到所有选项,我们需要将字典的内容格式化输出。我们可以使用字典的 .items() 方法来遍历所有键值对。

以下是展示菜单的代码:

print("MENU")
for key, value in menu.items():
    print(f"{key:10} : ${value:.2f}")
print("-" * 20)

这段代码会整齐地列出所有商品及其价格。


获取用户输入

接下来,我们需要一个循环来持续询问用户想要购买什么商品,直到用户选择退出。

while True:
    food = input("Select an item (Q to quit): ").lower()
    if food == "q":
        break
    elif menu.get(food) is not None:
        cart.append(food)
    else:
        print(f"Item '{food}' is not on the menu.")

在这段代码中:

  • 我们使用 .lower() 方法确保输入统一为小写,方便比较。
  • 使用字典的 .get() 方法检查输入的商品是否在菜单中。如果不在,则返回 None
  • 如果商品有效,就将其添加到购物车 cart 列表中。

计算总价并输出结果

当用户完成选择后,我们需要遍历购物车,根据菜单字典查找每个商品的价格,并累加计算出总价。

以下是计算和输出总价的代码:

print("YOUR CART")
for food in cart:
    print(food, end=" ")
    total += menu.get(food)

print()
print("-" * 20)
print(f"Total: ${total:.2f}")

这段代码会先列出购物车中的所有商品,然后显示最终的总金额。


程序完整代码

将以上所有部分组合起来,就得到了完整的小卖部程序:

menu = {
    "pizza": 3.00,
    "nachos": 4.50,
    "popcorn": 6.00,
    "fries": 2.50,
    "chips": 1.00,
    "soft pretzel": 2.00,
    "soda": 1.50,
    "lemonade": 1.25
}

cart = []
total = 0.0

print("MENU")
for key, value in menu.items():
    print(f"{key:10} : ${value:.2f}")
print("-" * 20)

while True:
    food = input("Select an item (Q to quit): ").lower()
    if food == "q":
        break
    elif menu.get(food) is not None:
        cart.append(food)
    else:
        print(f"Item '{food}' is not on the menu.")

print("YOUR CART")
for food in cart:
    print(food, end=" ")
    total += menu.get(food)

print()
print("-" * 20)
print(f"Total: ${total:.2f}")

总结

本节课中,我们一起学习了如何创建一个模拟小卖部功能的Python程序。我们主要运用了字典这一数据结构来存储和管理商品与价格,并通过循环和条件判断实现了用户交互、商品选择及总价计算。这个程序的核心目的是帮助初学者熟悉字典的基本操作,包括创建、遍历、使用 .get() 方法安全地访问值等。

027:在Python中生成随机数

在本节课中,我们将学习如何在Python中生成随机数。我们将介绍random模块的几个核心方法,并通过创建一个数字猜谜游戏来巩固所学知识。

导入模块

首先,我们需要导入Python内置的random模块。这个模块提供了许多与随机数相关的有用方法。

import random

要查看random模块提供的所有方法,可以使用help()函数。

print(help(random))

生成随机整数

上一节我们介绍了如何导入模块,本节中我们来看看如何生成随机整数。randint()方法可以生成指定范围内的随机整数。

以下是使用randint()生成随机整数的步骤:

  1. 调用random.randint(a, b)方法。
  2. 参数ab定义了随机数的范围,包含两端。
  3. 将结果赋值给一个变量。

例如,模拟掷一个六面骰子:

dice_roll = random.randint(1, 6)
print(f"骰子点数是:{dice_roll}")

模拟掷一个二十面骰子:

d20_roll = random.randint(1, 20)
print(f"二十面骰子点数是:{d20_roll}")

你也可以使用变量来定义范围:

low = 1
high = 100
random_number = random.randint(low, high)
print(f"1到100之间的随机数是:{random_number}")

生成随机浮点数

除了整数,我们还可以生成随机浮点数。random()方法会返回一个范围在[0.0, 1.0)之间的随机浮点数。

float_number = random.random()
print(f"0到1之间的随机浮点数是:{float_number}")

从序列中随机选择

在未来的课程中,我们会创建石头剪刀布游戏,这时就需要从一组选项中随机选择一个。choice()方法可以做到这一点。

以下是使用choice()从列表中随机选取一个元素的步骤:

  1. 准备一个包含多个选项的序列(如列表)。
  2. 调用random.choice(sequence)方法。
  3. 该方法会返回序列中的一个随机元素。
options = ["石头", "剪刀", "布"]
computer_choice = random.choice(options)
print(f"电脑选择了:{computer_choice}")

打乱序列顺序

如果你需要打乱一个序列(如洗牌),可以使用shuffle()方法。它会直接修改原序列的顺序。

cards = ["红桃A", "方块2", "黑桃10", "梅花K", "大王"]
random.shuffle(cards)
print(f"洗牌后的顺序是:{cards}")

实践:数字猜谜游戏

现在,让我们运用所学的知识来创建一个数字猜谜游戏。这个游戏会生成一个随机数,然后让用户来猜,直到猜对为止。

import random

# 设置数字范围
low = 1
high = 100

# 初始化猜测次数
guesses = 0

# 生成目标随机数
number = random.randint(low, high)

print(f"游戏开始!数字范围是 {low} 到 {high}。")

while True:
    # 获取用户输入
    guess = int(input(f"请输入一个 {low} 到 {high} 之间的数字:"))
    guesses += 1  # 猜测次数加1

    # 判断猜测结果
    if guess < number:
        print("猜小了!")
    elif guess > number:
        print("猜大了!")
    else:
        print(f"恭喜你,猜对了!数字就是 {number}。")
        break  # 猜对后退出循环

print(f"本轮游戏你总共猜了 {guesses} 次。")

游戏运行示例:

游戏开始!数字范围是 1 到 100。
请输入一个 1 到 100 之间的数字:50
猜大了!
请输入一个 1 到 100 之间的数字:25
猜小了!
请输入一个 1 到 100 之间的数字:37
猜对了!数字就是 37。
本轮游戏你总共猜了 3 次。

总结

本节课中我们一起学习了Python中random模块的核心用法。我们掌握了如何使用randint()生成随机整数,使用random()生成随机浮点数,使用choice()从序列中随机选取元素,以及使用shuffle()打乱序列顺序。最后,我们通过创建一个完整的数字猜谜游戏,将这些知识点融会贯通。在未来的课程中,我们会在更多游戏项目中运用随机数来增加趣味性和不确定性。

028:编写一个数字猜谜游戏 🎮

在本节课中,我们将使用Python创建一个数字猜谜游戏。这是一个面向初学者的项目,通过完成它,可以帮助我们巩固对之前所学主题的理解。

概述与准备

我们将从导入必要的模块和定义游戏变量开始。

首先,我们需要导入 random 模块,因为它将帮助我们生成一个随机数字。

import random

接下来,定义游戏数字的范围。我们将使用两个变量来存储最小值和最大值。

lowest_number = 1
highest_number = 100

你可以根据需要自由选择不同的范围。我们将范围设置为1到100。一个随机数将在这个范围内被选中,并存储在名为 answer 的变量中。

answer = random.randint(lowest_number, highest_number)

为了测试,我们可以先打印出答案。

print(answer)

现在,我们需要一些额外的变量来跟踪游戏状态。guesses 变量用于记录猜测次数,is_running 是一个布尔变量,用于控制游戏主循环。

guesses = 0
is_running = True

游戏主循环与用户交互

上一节我们设置了游戏的基础变量,本节中我们来看看如何构建游戏的主循环并与用户交互。

首先,打印一个欢迎信息。

print(f"Python数字猜谜游戏!\n请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")

现在,我们需要一个 while 循环来持续运行游戏的每一轮。

while is_running:

在循环内部,我们将提示用户输入他们的猜测。

    guess = input("请输入你的猜测:")

输入验证

获取用户输入后,我们需要验证其有效性。以下是需要检查的情况:

  • 输入是否为数字:用户可能输入非数字内容,如“pizza”。
  • 数字是否在有效范围内:用户可能输入一个超出定义范围的数字。

首先,检查输入是否为数字。

    if guess.isdigit():
        # 暂时用pass占位,稍后填充逻辑
        pass
    else:
        print("无效猜测。")
        print(f"请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")

如果输入是数字,我们需要将其转换为整数类型,并增加猜测次数计数器。

    if guess.isdigit():
        guess = int(guess)
        guesses += 1

然后,检查转换后的数字是否在有效范围内。

        if guess < lowest_number or guess > highest_number:
            print("该数字超出范围。")
            print(f"请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")

游戏逻辑判断

处理完输入验证后,接下来是实现游戏的核心逻辑:判断猜测的数字与正确答案的关系。

如果猜测的数字小于正确答案,我们提示“太低”。

        elif guess < answer:
            print("太低了,再试一次。")

如果猜测的数字大于正确答案,我们提示“太高”。

        elif guess > answer:
            print("太高了,再试一次。")

如果猜测的数字既不小于也不大于正确答案,那么它就是正确答案。

        else:
            print(f"正确!答案是 {answer}。")
            print(f"猜测次数:{guesses}。")

当用户猜中答案后,我们需要将 is_running 变量设置为 False 以退出 while 循环,结束游戏。

            is_running = False

完整代码与总结

将以上所有部分组合起来,就得到了完整的数字猜谜游戏代码。

import random

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c4b89b28761031c14dd4b1973d1bd265_53.png)

lowest_number = 1
highest_number = 100
answer = random.randint(lowest_number, highest_number)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c4b89b28761031c14dd4b1973d1bd265_55.png)

guesses = 0
is_running = True

print(f"Python数字猜谜游戏!\n请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c4b89b28761031c14dd4b1973d1bd265_57.png)

while is_running:
    guess = input("请输入你的猜测:")

    if guess.isdigit():
        guess = int(guess)
        guesses += 1

        if guess < lowest_number or guess > highest_number:
            print("该数字超出范围。")
            print(f"请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")
        elif guess < answer:
            print("太低了,再试一次。")
        elif guess > answer:
            print("太高了,再试一次。")
        else:
            print(f"正确!答案是 {answer}。")
            print(f"猜测次数:{guesses}。")
            is_running = False
    else:
        print("无效猜测。")
        print(f"请选择一个介于 {lowest_number} 和 {highest_number} 之间的数字。")

本节课中我们一起学习了如何构建一个完整的Python数字猜谜游戏。我们实践了模块导入、变量定义、循环控制、条件判断和用户输入处理等多个基础概念。你可以将此作为一个小型项目,通过修改数字范围或添加更多功能(如难度选择、猜测历史记录等)来进一步练习和巩固你的Python技能。

029:用Python实现石头剪刀布游戏 🎮

在本节课中,我们将学习如何使用Python创建一个经典的“石头剪刀布”游戏。我们将运用之前学过的random模块、循环和条件判断等知识,构建一个可以反复游玩的完整游戏程序。


导入模块与定义选项

首先,我们需要导入random模块,以便让计算机能够随机选择。

import random

接下来,我们定义游戏的可选动作。由于这些选项在游戏过程中不会改变,我们使用元组来存储它们。

options = ("rock", "paper", "scissors")

获取玩家与电脑的选择

我们将创建两个变量来分别存储玩家和电脑的选择。初始时,玩家的选择设为None

player = None
computer = random.choice(options)

为了获取玩家的选择,我们使用input()函数。但玩家可能会输入无效选项,因此我们需要一个循环来确保输入有效。

以下是获取有效玩家输入的代码:

while player not in options:
    player = input("Enter a choice (rock, paper, scissors): ").lower()

这段代码会持续循环,直到玩家输入的内容在options元组中为止。


显示选择与判断胜负

在获取双方选择后,我们先将其显示出来。

print(f"Player: {player}")
print(f"Computer: {computer}")

现在进入游戏的核心逻辑——判断胜负。胜负规则如下:

  • 平局:玩家与电脑选择相同。
  • 玩家获胜:(player == "rock" and computer == "scissors")(player == "paper" and computer == "rock")(player == "scissors" and computer == "paper")
  • 其他情况均为玩家失败。

以下是实现此逻辑的代码:

if player == computer:
    print("It's a tie!")
elif (player == "rock" and computer == "scissors") or \
     (player == "paper" and computer == "rock") or \
     (player == "scissors" and computer == "paper"):
    print("You win!")
else:
    print("You lose!")


实现游戏循环与重玩功能

为了让玩家可以多次游戏,我们需要将上述所有代码放入一个主循环中。我们使用一个布尔变量playing来控制循环。

playing = True

while playing:
    # 重置玩家和电脑的选择
    player = None
    computer = random.choice(options)

    # 获取玩家有效输入
    while player not in options:
        player = input("Enter a choice (rock, paper, scissors): ").lower()

    # 显示选择
    print(f"Player: {player}")
    print(f"Computer: {computer}")

    # 判断胜负
    if player == computer:
        print("It's a tie!")
    elif (player == "rock" and computer == "scissors") or \
         (player == "paper" and computer == "rock") or \
         (player == "scissors" and computer == "paper"):
        print("You win!")
    else:
        print("You lose!")

    # 询问是否再玩一次
    if not input("Play again? (y/n): ").lower() == "y":
        playing = False

print("Thanks for playing!")

在每一轮游戏结束后,程序会询问玩家是否继续。如果输入的不是“y”,则将playing变量设为False,从而退出主循环,游戏结束。


总结

本节课中,我们一起学习了如何用Python构建一个“石头剪刀布”游戏。我们综合运用了以下知识点:

  1. 使用random.choice()让电脑随机选择。
  2. 使用while循环确保玩家输入有效。
  3. 使用if-elif-else语句实现游戏的胜负逻辑。
  4. 使用一个由布尔变量控制的while循环来实现游戏的重复进行。

通过这个项目,你将更深入地理解条件判断、循环和基本输入/输出在具体程序中的应用。

Python入门教程:P30:Python骰子模拟器程序

在本节课中,我们将学习如何使用Python创建一个骰子模拟器程序。我们将使用ASCII艺术来图形化地展示骰子,并学习如何随机生成数字、处理用户输入以及格式化输出。


导入模块与准备字符

首先,我们需要导入random模块,因为程序需要随机生成1到6之间的数字来模拟掷骰子。

import random

为了构建骰子的图形,我们需要使用一些Unicode字符。以下是构建骰子所需的字符代码,你可以直接复制使用。

┌─────────┐
│         │
│    ●    │
│         │
└─────────┘

这些字符将组合成骰子的五个行,构成一个完整的骰子面。


构建骰子图形字典

上一节我们准备了基础字符,本节中我们来看看如何将它们组织起来。我们将创建一个字典,其中键是数字1到6,值是一个由字符串组成的元组,每个字符串代表骰子图形的一行。

以下是构建该字典的代码:

dice_art = {
    1: ("┌─────────┐",
        "│         │",
        "│    ●    │",
        "│         │",
        "└─────────┘"),
    2: ("┌─────────┐",
        "│  ●      │",
        "│         │",
        "│      ●  │",
        "└─────────┘"),
    3: ("┌─────────┐",
        "│  ●      │",
        "│    ●    │",
        "│      ●  │",
        "└─────────┘"),
    4: ("┌─────────┐",
        "│  ●   ●  │",
        "│         │",
        "│  ●   ●  │",
        "└─────────┘"),
    5: ("┌─────────┐",
        "│  ●   ●  │",
        "│    ●    │",
        "│  ●   ●  │",
        "└─────────┘"),
    6: ("┌─────────┐",
        "│  ●   ●  │",
        "│  ●   ●  │",
        "│  ●   ●  │",
        "└─────────┘")
}

这样,我们就有了一个将数字映射到对应ASCII图形的数据结构。


获取用户输入与生成随机数

现在,我们需要让用户决定掷多少个骰子,并根据输入生成相应数量的随机数。

以下是实现此功能的步骤:

  1. 创建一个空列表dice来存储每次掷骰的结果。
  2. 初始化一个变量total为0,用于计算总和。
  3. 使用input()函数获取用户输入的骰子数量,并将其转换为整数。
  4. 使用for循环和random.randint(1, 6)生成指定数量的随机数,并添加到dice列表中。
  5. 遍历dice列表,累加所有值以计算总和。
dice = []
total = 0

num_of_dice = int(input("How many dice? "))

for die in range(num_of_dice):
    dice.append(random.randint(1, 6))

for die in dice:
    total += die

print(f"Total: {total}")

垂直显示骰子图形

有了随机数列表和图形字典,我们现在可以将骰子图形垂直打印出来。这可以通过嵌套循环实现:外层循环遍历每个骰子,内层循环打印该骰子图形的每一行。

for die in range(num_of_dice):
    for line in dice_art.get(dice[die]):
        print(line)

这种方法会将每个骰子完整地打印在下一个骰子的上方。


水平显示骰子图形(进阶)

如果你希望所有骰子在同一行水平排列,逻辑会稍微复杂一些。我们需要先打印所有骰子的第一行,然后是所有骰子的第二行,依此类推。

以下是实现水平显示的方法:

  1. 外层循环迭代5次(对应骰子图形的5行)。
  2. 内层循环遍历dice列表中的每个骰子数字。
  3. 对于每个骰子,通过dice_art.get(die)[line]获取当前行的字符串并打印,同时设置end=""以避免换行。
  4. 内层循环结束后,打印一个空行以实现换行,开始下一行的打印。
for line in range(5):
    for die in dice:
        print(dice_art.get(die)[line], end="")
    print()

这种方法将所有骰子图形并排显示,视觉效果更紧凑。


课程总结

本节课中我们一起学习了如何创建一个完整的Python骰子模拟器程序。我们涵盖了以下核心知识点:导入random模块生成随机数,使用字典数据结构映射数字与多行ASCII图形,通过嵌套循环控制图形的垂直与水平打印格式,以及处理用户输入并计算总和。这个项目综合运用了Python的基础语法,是巩固所学知识的良好练习。

031:Python中的替换密码加密程序

在本节课中,我们将要学习如何编写一个替换密码加密程序。我们将通过随机替换字符的方式来加密信息,并使用相同的密钥进行解密。这是一个适合初学者的网络安全基础练习。

概述

替换密码是一种基础的加密技术。其核心思想是建立一个字符映射表,将明文中的每个字符替换为另一个字符,从而生成密文。解密时,使用相同的映射表进行反向替换即可恢复原文。

准备字符集

首先,我们需要导入必要的模块并定义用于加密的字符集。

import random
import string

# 定义字符集,包括标点符号、数字和字母
chars = string.punctuation + string.digits + string.ascii_letters + " "

我们使用 string 模块的常量来方便地获取标点符号、数字和字母。为了避免换行符等特殊空白字符,我们手动添加了一个空格字符。

接下来,我们将这个字符串转换为列表,以便后续操作。

chars = list(chars)

生成随机密钥

密钥是字符集的一个随机排列。我们将创建原始字符集的一个副本,然后将其打乱顺序。

key = chars.copy()
random.shuffle(key)

这样,我们就得到了两个列表:chars 是原始顺序,key 是随机顺序。它们将构成我们的加密映射关系。

加密过程

现在,我们来实现加密功能。程序会请求用户输入明文,然后根据映射关系将其转换为密文。

plain_text = input("请输入要加密的消息:")
cipher_text = ""

for letter in plain_text:
    index = chars.index(letter)
    cipher_text += key[index]

print(f"原始消息:{plain_text}")
print(f"加密消息:{cipher_text}")

以下是加密过程的步骤说明:

  1. 遍历明文:使用 for 循环逐个处理明文中的字符。
  2. 查找索引:对于每个字符,在 chars 列表中查找其对应的索引位置。
  3. 替换字符:根据找到的索引,从 key 列表中获取对应的替换字符,并将其添加到密文字符串中。

解密过程

解密是加密的逆过程。我们需要使用相同的 keychars 列表来还原信息。

cipher_text = input("请输入要解密的消息:")
plain_text = ""

for letter in cipher_text:
    index = key.index(letter)
    plain_text += chars[index]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/39b2be91ac6f5a41e24dd865e738b55e_15.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/39b2be91ac6f5a41e24dd865e738b55e_17.png)

print(f"加密消息:{cipher_text}")
print(f"解密消息:{plain_text}")

以下是解密过程的步骤说明:

  1. 遍历密文:使用 for 循环逐个处理密文中的字符。
  2. 查找索引:对于每个字符,在 key 列表中查找其对应的索引位置。
  3. 还原字符:根据找到的索引,从 chars 列表中获取原始字符,并将其添加到明文字符串中。

总结

本节课中我们一起学习了如何用Python实现一个简单的替换密码程序。我们掌握了以下核心概念:

  • 字符映射:通过 charskey 两个列表建立加密与解密的对应关系。
  • 加密算法cipher_text += key[chars.index(letter)]
  • 解密算法plain_text += chars[key.index(letter)]

这个程序每次运行时都会生成一个新的随机密钥,因此相同的明文每次加密的结果都不同。要成功解密,必须使用加密时生成的同一套 charskey。这是一个理解古典密码学和基础编程逻辑的优秀实践。

032:Python函数其实很简单 🐍

在本节课中,我们将要学习Python中一个非常核心的概念:函数。函数可以被看作是一段可重复使用的代码块,它能帮助我们避免编写重复的代码,让程序结构更清晰、更易于维护。

什么是函数?🤔

函数是一个可重复调用的代码块。你可以将函数想象成一个预先打包好的工具,每当需要执行特定任务时,只需“调用”这个工具即可,而无需每次都重写一遍工具内部的运作步骤。

要调用一个函数,你需要在函数名后面加上一对圆括号 ()

为什么需要函数?🎯

让我们通过一个场景来理解。假设我需要唱三遍“生日快乐歌”。如果不使用函数,我可能需要重复写三遍相同的代码,或者使用循环。

以下是重复代码的示例:

print("Happy birthday to you")
print("Happy birthday to you")
print("Happy birthday to you")

虽然这样可以完成任务,但代码显得冗长且重复。更好的方法是:将这段代码写一次,然后在需要时重复使用它。这正是函数的作用。

如何定义和调用函数?🔧

要定义一个函数,你需要使用 def 关键字,后跟一个唯一的函数名、一对圆括号和一个冒号 :。属于该函数的代码需要缩进在下方。

以下是定义一个名为 happy_birthday 的函数的示例:

def happy_birthday():
    print("Happy birthday to you")

要调用这个函数,只需写下函数名并加上圆括号:

happy_birthday()

这样,函数内的代码就会被执行一次。如果需要执行三次,只需调用三次即可:

happy_birthday()
happy_birthday()
happy_birthday()

向函数传递数据:参数与实参 📤

函数的一个强大之处在于,你可以向它传递数据,这些数据被称为实参。函数内部接收这些数据的临时变量则被称为形参

你可以在调用函数时,将数据放在圆括号内传递进去。例如,我们修改 happy_birthday 函数,让它接收一个名字:

def happy_birthday(name):
    print(f"Happy birthday to {name}")

现在,我们可以向不同的人唱生日歌:

happy_birthday("Bro")
happy_birthday("Steve")
happy_birthday("Joe")

传递多个参数 📦

函数可以接收多个参数。你需要确保调用时传递的实参数量与函数定义时的形参数量匹配,并且顺序一致。

让我们扩展生日歌函数,同时接收名字和年龄:

def happy_birthday(name, age):
    print(f"Happy birthday to {name}")
    print(f"You are {age} years old")

调用时传递两个参数:

happy_birthday("Bro", 20)
happy_birthday("Steve", 30)
happy_birthday("Joe", 40)

请注意:形参和实参的顺序非常重要。如果顺序错乱,结果也会出错。

一个更实用的例子:发票函数 💰

让我们创建一个更实用的函数来生成发票。这个函数接收用户名、金额和到期日三个参数。

def display_invoice(username, amount, due_date):
    print(f"Hello {username},")
    print(f"Your bill of ${amount:.2f} is due on {due_date}")

调用示例:

display_invoice("BroCode", 42.50, "January 1")
display_invoice("Joe Schmo", 100.01, "January 2")

从函数返回数据:return语句 🔄

return 语句用于结束函数的执行,并将一个结果“发送回”给函数的调用者。这使得函数可以计算一个值,并将这个值用于程序的其他部分。

例如,我们创建一些基本的数学运算函数:

def add(x, y):
    z = x + y
    return z

def subtract(x, y):
    z = x - y
    return z

def multiply(x, y):
    z = x * y
    return z

def divide(x, y):
    z = x / y
    return z

调用这些函数并将返回值赋给变量:

result = add(1, 2)
print(result) # 输出:3

result = subtract(1, 2)
print(result) # 输出:-1

result = multiply(1, 2)
print(result) # 输出:2

result = divide(1, 2)
print(result) # 输出:0.5

你可以将函数调用想象成:函数执行完毕后,它本身“变成”了 return 语句后面的值。

综合示例:创建全名函数 📝

让我们创建一个函数,它接收名和姓,将其首字母大写,然后组合成完整的姓名并返回。

def create_name(first, last):
    first = first.capitalize()
    last = last.capitalize()
    return first + " " + last

调用这个函数:

full_name = create_name("bro", "code")
print(full_name) # 输出:Bro Code

full_name = create_name("spongebob", "squarepants")
print(full_name) # 输出:Spongebob Squarepants

总结 📚

本节课中我们一起学习了Python函数的核心知识:

  1. 函数定义:使用 def 关键字定义函数,函数体需要缩进。
  2. 函数调用:通过函数名加圆括号 () 来执行函数内的代码。
  3. 参数与实参:调用函数时可以传递数据(实参),函数通过形参来接收这些数据。数量和顺序必须匹配。
  4. 返回值:使用 return 语句可以将函数计算的结果发送回调用处,这个值可以被赋值给变量或用于其他操作。

函数是构建模块化、可读性强且高效代码的基石。掌握了函数,你就掌握了让代码变得整洁和强大的关键工具。在后续的学习中,我们还会经常使用并深化对函数的理解。

033:默认参数详解 🎯

在本节课中,我们将要学习Python函数中一个非常实用的特性——默认参数。默认参数可以为函数参数提供预设值,从而简化函数调用,使代码更加灵活和简洁。

上一节我们介绍了位置参数,本节中我们来看看如何为参数设置默认值。

什么是默认参数? 🤔

默认参数是为函数参数指定的一个默认值。当调用函数时,如果省略了该参数,则会自动使用这个默认值。

一个计算净价的例子 💰

让我们通过一个计算商品净价的函数来理解默认参数。

首先,我们定义一个函数 net_price,它接收三个参数:商品原价 list_price、折扣 discount 和销售税 tax。其计算公式为:

净价 = 原价 × (1 - 折扣) × (1 + 税率)

用Python代码表示如下:

def net_price(list_price, discount, tax):
    return list_price * (1 - discount) * (1 + tax)

假设我购买一台标价500美元的PlayStation 5,没有折扣,税率为5%(0.05),调用方式如下:

print(net_price(500, 0, 0.05))

输出结果为 525.0

引入默认参数 ✨

现在,假设在大多数情况下(比如90%),折扣都为0,税率也固定为5%。每次都传入这两个相同的值显得冗余。这时,我们可以使用默认参数来优化函数。

以下是优化后的函数定义,为 discounttax 参数设置了默认值:

def net_price(list_price, discount=0, tax=0.05):
    return list_price * (1 - discount) * (1 + tax)

现在,在通常没有折扣和固定税率的情况下,我们只需要传入原价即可:

print(net_price(500))  # 输出 525.0

默认参数的灵活性 🔧

使用默认参数的好处在于,当情况有变时,我们仍然可以传入新的值来覆盖默认值。

例如,如果顾客有10%的折扣券:

print(net_price(500, 0.1))  # 输出 472.5

如果该商品免税:

print(net_price(500, 0.1, 0))  # 输出 450.0

这使得函数既能处理常见情况,又能灵活应对特例。

实践练习:创建一个倒计时器 ⏱️

接下来,我们通过创建一个倒计时函数来巩固对默认参数的理解。

我们将使用 time 模块。这个函数将从 start 数到 end,每秒打印一个数字。

import time

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4c1bb0f301ca3e307a1ee0fc96b39fd0_54.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4c1bb0f301ca3e307a1ee0fc96b39fd0_55.png)

def count(end, start=0):
    for x in range(start, end + 1):
        print(x)
        time.sleep(1)
    print("Done!")

注意:在定义函数时,所有默认参数必须放在非默认参数(位置参数)的后面。这是Python的语法规则。

现在调用函数:

  • 大多数时候我们从0开始计数,所以只需传入结束值:

count(10)  # 从0数到10
  • 如果想从15开始数到30:

count(30, 15)  # 从15数到30

总结 📝

本节课中我们一起学习了Python的默认参数。

  • 定义:默认参数是为函数参数预设的值,在调用时若省略该参数则自动使用。
  • 作用:它们能使函数调用更简洁,减少需要传递的参数数量,尤其是在某些参数值在大多数情况下都保持一致时。
  • 语法规则:在函数定义中,默认参数必须位于所有非默认参数(位置参数)之后。

默认参数是编写清晰、灵活且易于使用的函数的重要工具。下一节,我们将探讨另一种参数类型——关键字参数。

Python超全入门教程:P34:关键字参数详解 🧠

在本节课中,我们将要学习Python中的关键字参数。关键字参数是一种在调用函数时,通过指定参数名来传递参数的方式。我们将了解它的定义、优势、使用方法,并通过具体示例来加深理解。


什么是关键字参数?🔍

上一节我们介绍了函数参数的基本概念。本节中,我们来看看一种特殊的参数传递方式——关键字参数。

一个关键字参数是一个前面带有标识符(即参数名)的参数。它的核心语法是 参数名=值

关键字参数的优势 ✨

以下是使用关键字参数的两个主要好处:

  1. 提高代码可读性:通过参数名,可以清晰地知道每个值对应的含义。
  2. 参数顺序无关紧要:只要使用了关键字参数,传递参数的顺序可以与函数定义时参数的顺序不同。

从位置参数到关键字参数 🔄

假设我们有一个用于显示问候信息的函数 hello

def hello(greeting, title, first, last):
    print(f"{greeting} {title}. {first} {last}")

目前,我们使用位置参数调用它:

hello("Hello", "Mr.", "Spongebob", "Squarepants")
# 输出:Hello Mr. Spongebob Squarepants

位置参数的顺序至关重要。如果调换参数顺序,输出结果就会混乱。

现在,我们可以将其转换为关键字参数调用:

hello(greeting="Hello", title="Mr.", first="Spongebob", last="Squarepants")

使用关键字参数后,参数的顺序就不再重要了。例如,以下调用方式依然正确:

hello(title="Mr.", last="Squarepants", first="Spongebob", greeting="Hello")

混合使用位置参数与关键字参数 ⚠️

在混合使用时,必须确保所有位置参数在所有关键字参数之前

  • 正确示例hello("Hello", title="Mr.", first="Spongebob", last="Squarepants")
  • 错误示例hello(title="Mr.", "Hello", first="Spongebob", last="Squarepants") (会导致语法错误)

关键字参数的实际应用示例 💡

示例1:澄清参数含义

当参数值本身含义模糊时,关键字参数能有效避免混淆。

# 不使用关键字参数,容易混淆哪个是名,哪个是姓
hello("Hello", "Mr.", "John", "James")

# 使用关键字参数,含义一目了然
hello(greeting="Hello", title="Mr.", first="James", last="John")

示例2:使用内置函数的关键字参数

Python 许多内置函数都提供了有用的关键字参数。例如 print() 函数:

  • end 参数:指定打印结束后的字符,默认为换行符 \n

    for x in range(1, 11):
        print(x, end=" ")  # 用空格代替换行
    # 输出:1 2 3 4 5 6 7 8 9 10
    

  • sep 参数:指定多个打印项之间的分隔符,默认为空格。

    print("1", "2", "3", "4", "5", sep="-")
    # 输出:1-2-3-4-5
    

综合练习:生成电话号码 📞

让我们创建一个生成电话号码格式字符串的函数,并练习使用关键字参数。

def get_phone(country_code, area_code, first_digits, last_digits):
    return f"{country_code}-{area_code}-{first_digits}-{last_digits}"

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/a333a25cc3dca4431fecef82dea2556e_81.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/a333a25cc3dca4431fecef82dea2556e_83.png)

# 使用关键字参数调用,顺序可以任意
phone_number = get_phone(country_code="1", area_code="123", first_digits="456", last_digits="7890")
print(phone_number)  # 输出:1-123-456-7890

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/a333a25cc3dca4431fecef82dea2556e_85.png)

# 也可以保持与定义一致的顺序,但使用关键字形式
phone_number = get_phone(country_code="1", area_code="123", first_digits="456", last_digits="7890")


总结 📝

本节课中我们一起学习了Python的关键字参数。

  • 定义:关键字参数是通过 参数名=值 形式传递的参数。
  • 优势提高代码可读性,并且调用时参数的顺序可以任意
  • 规则:当混合使用位置参数和关键字参数时,所有位置参数必须位于关键字参数之前。
  • 应用:广泛用于内置函数(如 print()endsep)和自定义函数中,使代码意图更清晰。

关键字参数是编写清晰、易维护Python代码的重要工具之一。

Python超全入门教程:P36:可迭代对象详解 🐍

在本节课中,我们将要学习Python中一个核心概念——可迭代对象。我们将了解什么是可迭代对象,以及如何在不同类型的数据结构(如列表、元组、集合、字符串和字典)中使用循环来遍历它们。


什么是可迭代对象?

任何能够一次返回一个元素的对象或集合,都被视为可迭代对象。如果一个对象或集合被认为是可迭代的,那么它就可以在循环中被遍历。


列表的迭代

列表是典型的可迭代对象。我们可以使用for循环来遍历列表中的每个元素。

for循环的上下文中,我们会逐个获得列表中的元素。对于正在处理的每个元素,我们可以给它一个临时的、描述性的名称。

以下是创建和遍历一个数字列表的示例:

numbers = [1, 2, 3, 4, 5]
for number in numbers:
    print(number)

这段代码将输出数字1到5,每个数字占一行。

为当前元素命名时,应使用能清晰描述其内容的名称。例如,使用item也是一个不错的选择:

for item in numbers:
    print(item)


反向迭代

你可以使用reversed()函数来反向遍历一个可迭代对象。

for number in reversed(numbers):
    print(number)

这将输出数字5、4、3、2、1。


控制打印输出

如果你不希望每个元素都打印在新的一行,可以修改print函数的end参数。

默认情况下,print函数以换行符结束。我们可以将其替换为空格或其他字符。

例如,用空格分隔每个元素:

for number in numbers:
    print(number, end=' ')

或者,用“ - ”来分隔:

for number in numbers:
    print(number, end=' - ')


元组的迭代

元组也是可迭代对象。我们可以像遍历列表一样遍历元组。

numbers_tuple = (1, 2, 3, 4, 5)
for number in numbers_tuple:
    print(number)


集合的迭代

集合同样是可迭代的。但需要注意的是,集合是无序的,并且不能使用reversed()函数进行反向迭代,尝试这样做会引发TypeError

以下是遍历集合的示例:

fruits = {"apple", "orange", "banana", "coconut"}
for fruit in fruits:
    print(fruit)

字符串的迭代

字符串也是可迭代对象,遍历字符串会逐个返回其中的字符。

name = "YourChannelName"
for character in name:
    print(character, end=' ')


字典的迭代

字典的迭代稍微复杂一些。默认情况下,直接迭代字典会返回所有的,而不是值。

my_dict = {'a': 1, 'b': 2, 'c': 3}
for key in my_dict:
    print(key)  # 输出:a, b, c

获取字典的值:
要获取值,需要使用字典的.values()方法。

for value in my_dict.values():
    print(value)  # 输出:1, 2, 3

同时获取键和值:
要同时获取键和值,需要使用字典的.items()方法,它会返回一个包含键值对的可迭代对象。

for key, value in my_dict.items():
    print(f"{key} = {value}")

输出结果为:a = 1, b = 2, c = 3


总结

本节课中,我们一起学习了Python中的可迭代对象。可迭代对象是指能够一次返回一个元素的对象或集合,这意味着它们可以在for循环中被遍历。我们实践了如何遍历列表、元组、集合、字符串和字典,并学习了如何控制输出格式以及处理字典的键和值。掌握可迭代对象是理解Python循环和数据处理的基础。

037:成员运算符

在本节课中,我们将要学习Python中的成员运算符。成员运算符用于检查一个值或变量是否存在于某个序列(如字符串、列表、元组、集合或字典)中。它们是编写条件判断时非常有用的工具。

成员运算符简介

成员运算符包括 innot in。它们会返回一个布尔值(TrueFalse),表示检查的结果。

  • in:如果指定的值在序列中找到,则返回 True,否则返回 False
  • not in:如果指定的值不在序列中找到,则返回 True,否则返回 False。它是 in 的反向操作。

在字符串中使用成员运算符

让我们从一个简单的猜字母游戏开始,看看如何在字符串中使用成员运算符。

首先,我们设定一个秘密单词,然后让用户猜一个字母。程序需要判断用户猜的字母是否存在于这个单词中。

以下是实现这个逻辑的代码:

secret_word = "APPLE"
guess = input("Guess a letter in the secret word: ")

if guess in secret_word:
    print(f"There is a {guess}.")
else:
    print(f"{guess} was not found.")

在这段代码中,guess in secret_word 就是一个成员运算。如果 guesssecret_word 中的一个字符,表达式结果为 True,程序会打印“There is a...”;否则结果为 False,程序会打印“… was not found”。

我们也可以使用 not in 来实现相同的逻辑,只是需要调换 ifelse 后面的语句块。

if guess not in secret_word:
    print(f"{guess} was not found.")
else:
    print(f"There is a {guess}.")

在列表、元组和集合中使用成员运算符

上一节我们介绍了如何在字符串中使用成员运算符。本节中我们来看看如何在其他序列类型(如列表、元组和集合)中使用它。它们的用法与字符串类似。

以下是一个在学生集合中查找姓名的例子:

students = {"Spongebob", "Patrick", "Sandy"}
search_name = input("Enter the name of a student: ")

if search_name in students:
    print(f"{search_name} is a student.")
else:
    print(f"{search_name} was not found.")

在这个例子中,students 是一个集合。search_name in students 会检查输入的名字是否是集合中的一个元素。

在字典中使用成员运算符

在字典中使用成员运算符时,默认检查的是键(Key),而不是值(Value)。

让我们创建一个学生成绩字典,然后根据学生姓名(键)来查找其成绩(值)。

grades = {
    "Sandy": "A",
    "Squidward": "B",
    "Spongebob": "C",
    "Patrick": "D"
}

student = input("Enter the name of a student: ")

if student in grades:
    # 如果找到该学生(键),则通过键获取对应的值(成绩)
    student_grade = grades[student]
    print(f"{student}'s grade is {student_grade}.")
else:
    print(f"{student} was not found.")

代码 student in grades 检查输入的姓名是否是字典 grades 中的一个键。如果是,我们使用 grades[student] 来获取该键对应的值,即学生的成绩。

综合应用示例:验证邮箱地址

最后,我们来看一个稍微复杂一点的例子,它结合了多个成员运算和逻辑运算符。我们将编写一个简单的程序来验证一个邮箱地址是否有效(这里仅做简单演示,检查是否包含“@”和“.”)。

以下是验证逻辑:

email = input("Enter your email: ")

if "@" in email and "." in email:
    print("Valid email.")
else:
    print("Invalid email.")

在这段代码中,我们使用了 and 逻辑运算符来连接两个条件:"@" in email"." in email。只有当邮箱中同时包含“@”符号和“.”时,整个条件才为 True,程序才会判定为有效邮箱。

总结

本节课中我们一起学习了Python的成员运算符 innot in

  • 它们用于判断一个值或变量是否存在于指定的序列中。
  • 可以应用的序列类型包括字符串、列表、元组、集合和字典(检查键)。
  • 它们返回布尔值 TrueFalse,非常适合在 if 条件语句中使用。
  • 通过组合逻辑运算符(如 and),可以构建更复杂的检查条件。

掌握成员运算符能帮助你更高效地处理数据集合和进行条件判断。

Python超全入门教程:P38:列表推导式

在本节课中,我们将要学习Python中的列表推导式。这是一种创建列表的简洁方法,比传统的循环更紧凑、更易读。

列表推导式遵循一个基本公式:[expression for item in iterable if condition]。其中,expression是对每个元素进行的操作,item是迭代变量,iterable是可迭代对象(如列表、元组、字符串或range对象),if condition是可选的筛选条件。

上一节我们介绍了列表推导式的基本概念,本节中我们来看看如何通过具体示例来掌握它。

从传统循环到列表推导式

首先,我们通过一个传统循环的例子来理解列表推导式的优势。假设我们要创建一个列表,包含数字1到10的2倍。

以下是使用传统for循环的实现方式:

doubles = []
for x in range(1, 11):
    doubles.append(x * 2)
print(doubles)  # 输出: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

这段代码虽然功能正确,但略显冗长。现在,我们使用列表推导式来实现相同的功能:

doubles = [x * 2 for x in range(1, 11)]
print(doubles)  # 输出: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

可以看到,列表推导式将多行代码压缩为一行,逻辑更清晰。

基础练习

接下来,我们通过几个基础练习来熟悉列表推导式的语法。

练习1:创建数字的三倍列表

triples = [y * 3 for y in range(1, 11)]
print(triples)  # 输出: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

练习2:创建数字的平方列表

squares = [z * z for z in range(1, 11)]
print(squares)  # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

处理字符串列表

列表推导式同样适用于处理字符串列表。

练习3:将水果名称转换为大写

fruits = ['apple', 'orange', 'banana', 'coconut']
uppercase_fruits = [fruit.upper() for fruit in fruits]
print(uppercase_fruits)  # 输出: ['APPLE', 'ORANGE', 'BANANA', 'COCONUT']

练习4:提取每个水果名称的首字母

fruit_chars = [fruit[0] for fruit in fruits]
print(fruit_chars)  # 输出: ['a', 'o', 'b', 'c']

使用条件筛选

列表推导式最强大的功能之一是能够结合条件进行筛选。我们可以在for循环后添加if语句。

练习5:从混合列表中筛选正数

numbers = [1, -2, 3, -4, 5, -6]
positive_nums = [num for num in numbers if num >= 0]
print(positive_nums)  # 输出: [1, 3, 5]

练习6:从混合列表中筛选负数

negative_nums = [num for num in numbers if num < 0]
print(negative_nums)  # 输出: [-2, -4, -6]

练习7:筛选偶数

numbers = [1, -2, 3, -4, 5, -6, 8, -7]
even_nums = [num for num in numbers if num % 2 == 0]
print(even_nums)  # 输出: [-2, -4, -6, 8]

练习8:筛选奇数

odd_nums = [num for num in numbers if num % 2 == 1]
print(odd_nums)  # 输出: [1, 3, 5, -7]

练习9:筛选及格分数

grades = [85, 42, 79, 90, 56, 61, 30]
passing_grades = [grade for grade in grades if grade >= 60]
print(passing_grades)  # 输出: [85, 79, 90, 61]

总结

本节课中我们一起学习了Python的列表推导式。它是一种非常高效和优雅的创建列表的方法,其核心公式为 [expression for item in iterable if condition]。通过将循环、条件判断和表达式组合在一行代码中,列表推导式使得代码更加简洁、易读。从处理数字列表到字符串列表,再到结合条件进行筛选,列表推导式都是一个强大且实用的工具。

Python入门教程:P39:Match Case语句详解

在本节课中,我们将要学习Python中的match case语句。这是一种用于替代多重if-elif语句的结构,可以使代码更加清晰和易读。

match case语句在其他编程语言中也被称为switch语句。其核心逻辑是:当某个值匹配到特定的case时,就执行对应的代码块。使用match case的主要好处在于语法更简洁,代码结构更清晰。

第一个示例:将数字转换为星期几

首先,我们来看一个使用传统if-elif-else语句的函数。这个函数接收一个1到7的数字,返回对应的星期几字符串。

def get_day_of_week(day):
    if day == 1:
        return "Sunday"
    elif day == 2:
        return "Monday"
    elif day == 3:
        return "Tuesday"
    elif day == 4:
        return "Wednesday"
    elif day == 5:
        return "Thursday"
    elif day == 6:
        return "Friday"
    elif day == 7:
        return "Saturday"
    else:
        return "Not a valid day"

上面的代码功能正常,但当条件分支很多时,会显得冗长。接下来,我们看看如何使用match case语句重写它。

def get_day_of_week(day):
    match day:
        case 1:
            return "Sunday"
        case 2:
            return "Monday"
        case 3:
            return "Tuesday"
        case 4:
            return "Wednesday"
        case 5:
            return "Thursday"
        case 6:
            return "Friday"
        case 7:
            return "Saturday"
        case _:
            return "Not a valid day"

match case语句中:

  • match后面跟要检查的变量(这里是day)。
  • 每个case后面跟一个可能匹配的值。
  • case _是一个通配符,匹配所有未被前面case捕获的情况,相当于else子句。

测试这个函数:

  • get_day_of_week(1) 返回 "Sunday"
  • get_day_of_week(2) 返回 "Monday"
  • get_day_of_week(7) 返回 "Saturday"
  • get_day_of_week("pizza") 返回 "Not a valid day"

可以看到,match case版本在逻辑上与if-elif-else版本完全一致,但结构上更加规整。

第二个示例:判断是否为周末

上一节我们介绍了match case的基本语法,本节中我们来看看一个更实用的例子,并学习如何合并多个case

假设我们要创建一个函数,根据输入的星期几字符串,判断是否为周末(周六或周日)。

初始版本可能如下:

def is_weekend(day):
    match day:
        case "Sunday":
            return True
        case "Monday":
            return False
        case "Tuesday":
            return False
        case "Wednesday":
            return False
        case "Thursday":
            return False
        case "Friday":
            return False
        case "Saturday":
            return True
        case _:
            return False

这个版本虽然正确,但MondayFriday都返回False,代码有重复。我们可以使用|(或)运算符来合并具有相同操作的case

以下是优化后的版本:

def is_weekend(day):
    match day:
        case "Saturday" | "Sunday":
            return True
        case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
            return False
        case _:
            return False

在这个版本中:

  • case "Saturday" | "Sunday": 表示如果day"Saturday""Sunday",则匹配成功,返回True
  • 同理,工作日被合并到一个case中。
  • 通配符case _处理所有其他无效输入。

测试这个函数:

  • is_weekend("Saturday") 返回 True
  • is_weekend("Monday") 返回 False
  • is_weekend("Sunday") 返回 True
  • is_weekend("Friday") 返回 False
  • is_weekend("pizza") 返回 False

通过合并case,代码变得更加简洁,逻辑也更清晰。

总结

本节课中我们一起学习了Python中的match case语句。

  • match case是多重if-elif-else语句的替代方案,用于根据一个变量的不同值执行不同的代码块。
  • 其基本语法是 match variable: 后跟一系列 case value: 分支。
  • 使用 case _: 作为通配符,处理所有未匹配的情况。
  • 可以使用 | (或)运算符将多个值合并到一个case中,使代码更简洁。
  • 与传统的if-elif链相比,match case语句的语法更清晰,可读性更强,尤其是在处理多个离散值时。

希望本教程能帮助你理解并使用match case语句来编写更优雅的Python代码。

040:什么是Python模块?📦

在本节课中,我们将要学习Python模块。模块是Python编程中组织代码的重要方式,它允许你将代码分割到不同的文件中,以便于管理和重用。

概述

模块本质上就是一个包含Python代码的文件,你可以将其包含到你的程序中。使用模块有助于将大型程序拆分为可重用的独立文件。Python提供了丰富的内置模块,同时也允许你创建自己的模块。

查看可用模块

要查看Python标准库中所有可用的模块,你可以使用help()函数。具体操作是:在函数中传入字符串"modules",然后打印结果。

以下是部分可用的内置模块示例:

  • math:提供数学运算函数。
  • string:提供字符串操作工具。
  • time:提供时间相关的函数。
  • pickle:用于序列化和反序列化Python对象(注意:它和“泡菜”无关)。

探索模块内容

要查看一个模块内部包含的所有变量和函数,你可以将该模块的名称作为参数传递给help()函数。

例如,对于math模块,使用help(math)可以列出其包含的变量(如pie)和函数(如sqrtsin)。

导入模块

要使用一个模块,你需要先导入它。Python提供了几种导入方式。

基本导入

使用import关键字,后跟模块名。导入后,你可以通过模块名.变量名模块名.函数名()的格式访问其内容。

import math
print(math.pi)  # 输出圆周率π的值

使用别名导入

使用import ... as ...语法可以为模块设置一个别名。这在模块名较长时非常有用,可以减少代码输入量。

import math as m
print(m.pi)  # 使用别名`m`来访问

导入特定内容

使用from ... import ...语法可以从模块中导入特定的变量或函数。导入后,可以直接使用其名称,无需前缀。

from math import pi
print(pi)  # 直接使用`pi`,无需`math.`前缀

注意:这种方式可能导致命名冲突。如果你在程序中定义了一个同名的变量,它会覆盖从模块导入的内容,可能导致难以察觉的错误。

上一节我们介绍了导入模块的几种方法,本节中我们来看看一个因导入方式不当导致命名冲突的例子。

from math import e  # 导入数学常数e (约等于2.718)
a, b, c, d = 1, 2, 3, 4
print(e ** a, e ** b, e ** c, e ** d)  # 输出e的1,2,3,4次方

e = 5  # 意外地重新定义了变量e,覆盖了从math导入的e
print(e ** e)  # 现在计算的是5的5次方,而非e的5次方,结果完全错误

为了避免这种问题,建议使用import module_name的方式,并在使用时显式地加上模块名前缀,使代码意图更清晰。

import math
a, b, c, d = 1, 2, 3, 4
e = 5
print(math.e ** a, math.e ** b, math.e ** c, math.e ** d)  # 使用math.e
print(math.e ** e)  # 明确使用的是数学常数e

创建自定义模块

创建自己的模块非常简单,只需创建一个新的.py文件,并在其中编写代码即可。

以下是创建步骤:

  1. 在你的项目文件夹中,新建一个Python文件(例如:example.py)。
  2. 在这个文件中定义你需要的变量和函数。

例如,创建一个example.py模块文件:

# example.py 文件内容
pi = 3.14159

def square(x):
    return x ** 2

def cube(x):
    return x ** 3

def circumference(radius):
    return 2 * pi * radius

def area(radius):
    return pi * radius ** 2
  1. 在另一个Python程序(如main.py)中,导入并使用这个自定义模块。
# main.py 文件内容
import example

print(example.pi)  # 输出: 3.14159
print(example.square(3))  # 输出: 9
print(example.cube(3))  # 输出: 27
print(example.circumference(3))  # 输出: 18.849539999999998
print(example.area(3))  # 输出: 28.27431

总结

本节课中我们一起学习了Python模块。

  • 模块是一个包含Python代码的文件,用于代码组织和重用。
  • 使用import语句可以导入模块,包括内置模块和自定义模块。
  • 导入方式有多种:基本导入、别名导入和导入特定内容,各有适用场景。
  • 使用help("modules")可以查看所有可用模块,使用help(module_name)可以查看特定模块的详细信息。
  • 创建自定义模块只需新建一个.py文件并编写代码,然后在其他文件中导入即可使用。

合理使用模块能让你的程序结构更清晰,更易于维护。

Python入门教程:P41:变量作用域与解析规则

在本节课中,我们将要学习Python中变量的作用域以及Python查找变量时所遵循的解析规则。理解这些概念对于编写清晰、无错误的代码至关重要。

变量作用域

变量作用域指的是一个变量在程序中可见可访问的范围。简单来说,它决定了你在代码的哪个部分可以使用某个变量。

为了理解这个概念,我们先来看一个例子。假设我们有两个函数:

def function1():
    a = 1
    print(a)

def function2():
    b = 2
    print(b)

如果我们调用 function1()function2(),程序会分别打印出 12。在函数内部声明的变量拥有局部作用域。这意味着变量 afunction1 的局部变量,变量 bfunction2 的局部变量。

function1 内部,如果我们尝试打印 b,或者在 function2 内部尝试打印 a,程序都会报错,提示 NameError: name ‘b’ (或 ‘a’) is not defined。这是因为函数无法看到其他函数内部的内容。

我们可以把每个函数想象成一个独立的房子。你只能看到自己房子(函数)里发生的事情,而看不到邻居房子(其他函数)里的情况。这就是变量作用域的核心:它限定了变量的可见范围。

基于这个原理,我们甚至可以在不同的作用域内创建同名变量。

def function1():
    x = 1
    print(x)

def function2():
    x = 2
    print(x)

这里,我们有两个不同版本的 x:一个在 function1 中是局部变量,值为 1;另一个在 function2 中是局部变量,值为 2。它们互不干扰。

作用域解析顺序:LEGB规则

当我们使用一个变量时,Python会按照一个特定的顺序去查找它。这个顺序被称为 LEGB规则,即:

  1. Local:局部作用域
  2. Enclosed:闭包作用域
  3. Global:全局作用域
  4. Built-in:内置作用域

Python会按照这个顺序,从内到外逐层查找变量,直到找到为止。

1. 局部作用域

正如我们之前看到的,这是函数内部定义的变量。Python首先在局部作用域中查找变量。

2. 闭包作用域

这是一个稍高级的概念,当一个函数嵌套在另一个函数内部时,就形成了闭包。我们来看一个例子:

def function1():
    x = 1  # 这是function2的“闭包作用域”变量
    def function2():
        print(x)  # 这里会使用闭包作用域中的 x=1
    function2()

function2 内部打印 x 时,Python首先在 function2 的局部作用域中查找 x。如果没有找到,它就会向外一层,到其闭包作用域(即 function1 的作用域)中查找。在这个例子中,它会找到 x = 1

3. 全局作用域

全局作用域指的是在所有函数之外定义的变量。让我们修改之前的例子:

x = 3  # 全局变量

def function1():
    print(x)  # 这里会使用全局作用域中的 x=3

def function2():
    print(x)  # 这里也会使用全局作用域中的 x=3

现在,两个函数内部都没有定义局部变量 x。因此,当它们打印 x 时,Python在局部作用域和闭包作用域中都找不到 x,于是继续向外查找,最终在全局作用域中找到了 x = 3

4. 内置作用域

内置作用域包含了Python语言自身定义的名字,比如 printlenint 等。我们来看一个与内置变量冲突的例子:

from math import e  # 导入数学常数e,这是一个内置名称
print(e)  # 输出:2.718281828459045

e = 3  # 在全局作用域定义一个同名变量e

def function1():
    print(e)  # 这里会使用哪个e?

function1()  # 输出:3
print(e)     # 输出:3

在这个例子中,我们实际上有两个 e:一个是从 math 模块导入的内置 e,另一个是我们自己定义的全局 e。根据LEGB规则,当在函数或全局中查找 e 时,Python会先找到全局作用域中的 e = 3,因此最终打印的都是 3。内置的 e 被“遮盖”了。

总结

本节课中我们一起学习了Python中两个重要的基础概念。

  • 变量作用域:决定了变量在代码中的可见性和可访问性。主要分为局部作用域和全局作用域。
  • 作用域解析规则(LEGB):这是Python查找变量时所遵循的固定顺序:Local(局部) -> Enclosed(闭包) -> Global(全局) -> Built-in(内置)

理解这些规则能帮助你预测代码的行为,避免因变量名冲突或访问错误而导致的bug。记住,当使用一个变量时,Python总是从最内层的作用域开始,一层层向外查找。

Python超全入门教程:P42:理解 if __name__ == "__main__" 🐍

在本节课中,我们将要学习Python中一个常见但重要的结构:if __name__ == "__main__"。这个语句用于控制代码的执行方式,使得一个Python文件既可以作为独立的程序运行,也可以作为模块被其他程序导入使用。


核心概念:__name__ 变量

在Python中,每个模块(即每个.py文件)都有一个内置的变量叫做 __name__。这个变量的值取决于该模块是如何被使用的。

  • 当一个模块被直接运行时,其 __name__ 变量的值会被设置为字符串 "__main__"
  • 当一个模块被导入到另一个模块中时,其 __name__ 变量的值会被设置为该模块的文件名(不带.py后缀)。

我们可以通过一个简单的打印语句来验证这一点。

# 在一个名为 script1.py 的文件中
print(__name__)

if __name__ == "__main__" 的作用

基于 __name__ 变量的特性,我们可以在代码中使用 if __name__ == "__main__" 这个条件判断语句。它的作用是:只有当这个文件被直接运行时,才执行其下方的代码块

这带来了两个主要好处:

  1. 模块化与复用性:其他程序可以导入这个文件中的函数和类,而不会触发该文件作为独立程序时的执行逻辑。
  2. 避免意外执行:确保只有在主动运行该文件时,其中的“主程序”部分才会启动。


实践演示:创建两个脚本

为了更好地理解,我们将创建两个Python脚本文件进行演示。

上一节我们介绍了 __name__ 变量的概念,本节中我们来看看如何在实际代码中应用。

以下是创建和配置两个脚本的步骤:

  1. 创建第一个脚本 script1.py
  2. 创建第二个脚本 script2.py
  3. 在集成开发环境(IDE)中为这两个脚本分别配置独立的运行配置,以便我们可以选择运行哪一个。

场景一:直接运行与导入的区别

现在,让我们在 script1.py 中编写一些代码。

# script1.py
print(f"在 script1 中,__name__ 的值是:{__name__}")

如果我们直接运行 script1.py,输出将是:

在 script1 中,__name__ 的值是:__main__

接下来,我们在 script2.py 中导入 script1

# script2.py
import script1
print(f"在 script2 中,__name__ 的值是:{__name__}")

如果我们直接运行 script2.py,输出将是:

在 script1 中,__name__ 的值是:script1
在 script2 中,__name__ 的值是:__main__

可以看到,当 script1 被导入时,它的 __name__ 不再是 "__main__",而是其模块名 "script1"


场景二:使用 if __name__ == "__main__" 控制执行

理解了直接运行和导入的区别后,我们就可以利用 if __name__ == "__main__" 来组织代码了。

以下是 script1.py 的改进版本:

# script1.py

def favorite_food(food):
    print(f"你最喜欢的食物是:{food}")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/cdf91fcc0c4415f8238c1df2a644b2ee_14.png)

def main():
    print("这是 script1 的主程序。")
    favorite_food("披萨")
    print("再见。")

if __name__ == "__main__":
    main()

当我们直接运行 script1.py 时,if 条件成立,main() 函数被调用,程序正常执行:

这是 script1 的主程序。
你最喜欢的食物是:披萨
再见。

现在,我们在 script2.py 中导入并使用 script1 的函数,但不希望触发 script1main() 函数。

# script2.py
import script1

print("这是 script2。")
# 复用 script1 中的函数
script1.favorite_food("寿司")
print("结束。")

运行 script2.py,输出如下:

这是 script2。
你最喜欢的食物是:寿司
结束。

script1 中的 main() 函数没有被执行,这正是我们想要的效果。我们成功地从 script1 “借用”了 favorite_food 函数,而没有运行其主程序逻辑。


实际应用与最佳实践

一个非常实用的例子是Python库。许多库文件既可以作为模块被导入,提供各种功能函数;也可以直接运行,以显示帮助信息或进行自检。

script2.py 中,我们也可以遵循同样的模式,使其自身具备“可导入”和“可独立运行”两种特性。

# script2.py (最终版)
import script1

def favorite_drink(drink):
    print(f"你最喜欢的饮料是:{drink}")

def main():
    print("这是 script2 的主程序。")
    script1.favorite_food("寿司")
    favorite_drink("咖啡")
    print("再见。")

if __name__ == "__main__":
    main()

这样做是良好的编程习惯,它使得代码:

  • 更加模块化,易于管理和复用。
  • 提高可读性,清晰地分离了模块定义和程序入口。
  • 避免全局变量污染,将主逻辑封装在函数内。
  • 防止意外的代码执行

本节课中我们一起学习了 if __name__ == "__main__" 在Python中的用途。总结来说,它是一个强大的工具,允许你编写既可以作为独立脚本运行,又可以作为模块安全导入的Python代码。记住这个模式,它将使你的项目结构更加清晰和专业。

043:Python信用卡验证器 💳

在本节课中,我们将学习如何使用Python创建一个信用卡验证器程序。这个程序将遵循一个特定的算法来检查信用卡号码是否有效。我们将逐步分解这个过程,并使用字符串操作和循环来实现它。


概述 📋

信用卡验证通常使用卢恩算法(Luhn Algorithm)。我们将实现该算法的步骤,从用户那里获取一个信用卡号码,清理输入,然后进行计算以确定其有效性。


第一步:获取并清理用户输入

首先,我们需要从用户那里获取信用卡号码,并移除任何可能用于格式化的连字符(-)或空格。

以下是实现此步骤的代码:

# 步骤 1: 获取用户输入并清理
card_number = input("请输入信用卡号码: ")
# 移除连字符
card_number = card_number.replace("-", "")
# 移除空格
card_number = card_number.replace(" ", "")
# 反转字符串以便从右向左处理
card_number = card_number[::-1]

上一节我们介绍了课程目标,本节中我们来看看如何开始处理用户输入。我们使用 input() 函数获取输入,然后使用字符串的 replace() 方法移除不需要的字符。最后,我们通过切片 [::-1] 反转字符串,这为后续从右向左处理数字做好了准备。


第二步:计算奇数位数字之和

根据算法,我们需要将从右向左数,处于奇数位置上的所有数字相加。

以下是计算奇数位数字之和的代码:

# 步骤 2: 计算奇数位数字之和
sum_odd_digits = 0
for x in card_number[::2]:
    sum_odd_digits += int(x)

在清理了输入之后,我们现在开始计算。我们使用步长为2的切片 [::2] 来遍历反转后字符串中每隔一个的数字(即奇数位)。注意,我们需要将每个字符转换为整数(int(x))再进行累加。


第三步:计算偶数位数字之和

接下来,我们需要处理从右向左数,处于偶数位置上的数字。规则是:将每个数字乘以2,如果结果是两位数,则将这两个数字相加得到一个一位数。

以下是计算偶数位数字之和的代码:

# 步骤 3: 计算偶数位数字之和
sum_even_digits = 0
for x in card_number[1::2]:
    x = int(x) * 2
    if x >= 10:
        # 如果是两位数,则拆分并相加 (例如,12 -> 1 + 2 = 3)
        sum_even_digits += (x % 10) + 1
    else:
        sum_even_digits += x

上一节我们计算了奇数位之和,本节中我们来看看如何处理偶数位。我们使用切片 [1::2] 从索引1开始,每隔一个取一个数字(即偶数位)。对于每个数字,我们将其加倍。如果结果 >= 10,我们通过 (x % 10) + 1 这个公式来得到两个数字的和(例如,12 -> 2 + 1 = 3)。


第四步与第五步:计算总和并验证

最后,我们将奇数位和偶数位的和相加。如果总和能被10整除,则信用卡号码有效。

以下是完成验证的代码:

# 步骤 4: 计算总和
total = sum_odd_digits + sum_even_digits

# 步骤 5: 验证
if total % 10 == 0:
    print("有效 ✅")
else:
    print("无效 ❌")

在分别计算了奇数位和偶数位的和之后,我们现在将它们相加得到总和。验证的逻辑很简单:如果 total % 10 == 0,则号码有效


完整程序代码

以下是整合了所有步骤的完整程序:

# 步骤 1: 获取并清理输入
card_number = input("请输入信用卡号码: ")
card_number = card_number.replace("-", "")
card_number = card_number.replace(" ", "")
card_number = card_number[::-1]  # 反转字符串

# 步骤 2: 计算奇数位数字之和
sum_odd_digits = 0
for x in card_number[::2]:
    sum_odd_digits += int(x)

# 步骤 3: 计算偶数位数字之和
sum_even_digits = 0
for x in card_number[1::2]:
    x = int(x) * 2
    if x >= 10:
        sum_even_digits += (x % 10) + 1
    else:
        sum_even_digits += x

# 步骤 4 & 5: 计算总和并验证
total = sum_odd_digits + sum_even_digits
if total % 10 == 0:
    print("有效 ✅")
else:
    print("无效 ❌")

总结 🎯

本节课中我们一起学习了如何用Python实现一个信用卡验证器。我们主要完成了以下工作:

  1. 获取并清理用户输入:移除格式字符并反转字符串。
  2. 计算奇数位数字之和:使用步长为2的切片进行遍历和累加。
  3. 计算偶数位数字之和:将数字加倍,并按规则处理两位数结果。
  4. 验证结果:检查总和是否能被10整除。

这个项目很好地练习了字符串处理、循环和条件判断。你可以使用网上找到的测试信用卡号码来验证程序的正确性。

044:编写一个初学者 Python 银行程序 🏦

在本节课中,我们将学习如何使用 Python 创建一个简单的银行程序。这个项目旨在通过实践,帮助我们熟悉在项目中如何使用函数。我们将从规划功能开始,逐步实现显示余额、存款和取款等核心操作,并最终将所有代码封装到一个主函数中,使程序结构更清晰。


项目规划与函数声明

上一节我们介绍了本课程的目标。本节中,我们来看看如何开始构建程序。在创建项目时,我喜欢将其划分为更小的部分,并逐一处理。我们将首先声明银行程序所需的所有函数。

以下是程序需要的三个核心功能:

  • 显示余额:定义一个名为 show_balance 的函数。
  • 存款:定义一个名为 deposit 的函数。
  • 取款:定义一个名为 withdraw 的函数。

目前,我们先用 pass 作为占位符来定义这些函数。

def show_balance():
    pass

def deposit():
    pass

def withdraw():
    pass

定义主程序逻辑与变量

现在我们已经声明了三个核心函数。接下来,我们需要定义程序运行所需的主要变量和逻辑。

我们需要两个关键变量:

  • 余额:初始值设为 0
  • 程序运行状态:一个布尔值 is_running,用于控制程序主循环。

我们将把主要代码放在一个 while 循环中,只要 is_runningTrue,程序就会持续运行。

balance = 0
is_running = True

while is_running:
    # 主程序代码将放在这里

构建用户交互界面

while 循环内部,我们需要与用户进行交互。这包括显示欢迎信息、选项菜单,并接收用户输入。

以下是构建用户界面的步骤:

  1. 打印程序标题和装饰线。
  2. 列出用户可以选择的选项。
  3. 提示用户输入他们的选择(1-4)。
    print("******************")
    print("Banking Program")
    print("******************")
    print("1. Show Balance")
    print("2. Make Deposit")
    print("3. Make Withdrawal")
    print("4. Exit")
    choice = input("Enter your choice (1-4): ")

处理用户选择

获取用户输入后,我们需要根据不同的选择执行相应的操作。我们将使用一系列 if-elif-else 语句来处理。

以下是处理逻辑:

  • 如果选择是 "1",调用 show_balance 函数。
  • 如果选择是 "2",调用 deposit 函数。
  • 如果选择是 "3",调用 withdraw 函数。
  • 如果选择是 "4",将 is_running 设为 False 以退出循环。
  • 如果输入无效(非1-4),则提示错误信息。
    if choice == "1":
        show_balance()
    elif choice == "2":
        deposit()
    elif choice == "3":
        withdraw()
    elif choice == "4":
        is_running = False
    else:
        print("That is not a valid choice.")

当循环结束后,打印一条告别信息。

print("Thank you! Have a nice day!")

实现 show_balance 函数

上一节我们搭建了程序的主框架。本节中,我们来具体实现第一个功能:显示余额。

show_balance 函数非常简单,它只需要打印出当前的余额。我们使用格式说明符 :.2f 来确保余额始终显示两位小数。

def show_balance():
    print("******************")
    print(f"Your balance is ${balance:.2f}")
    print("******************")

实现 deposit 函数

现在我们已经可以显示余额了。接下来,我们实现存款功能,让用户可以向账户中存钱。

deposit 函数需要执行以下步骤:

  1. 提示用户输入存款金额。
  2. 将输入的字符串转换为浮点数。
  3. 验证金额是否大于0(不允许存入负数)。
  4. 如果金额有效,则将其加到总余额中。
def deposit():
    print("******************")
    amount = float(input("Enter amount to be deposited: "))
    if amount <= 0:
        print("That's not a valid amount.")
        return 0
    else:
        global balance
        balance += amount
        return amount

实现 withdraw 函数

存款功能完成后,与之对应的就是取款功能。本节我们来实现 withdraw 函数。

withdraw 函数需要处理以下逻辑:

  1. 提示用户输入取款金额。
  2. 将输入的字符串转换为浮点数。
  3. 进行两项验证:
    • 取款金额不能大于当前余额。
    • 取款金额必须大于0。
  4. 如果验证通过,则从余额中减去该金额。
def withdraw():
    print("******************")
    amount = float(input("Enter amount to be withdrawn: "))
    global balance
    if amount > balance:
        print("Insufficient funds.")
        return 0
    elif amount <= 0:
        print("Amount must be greater than 0.")
        return 0
    else:
        balance -= amount
        return amount

重构:使用 main 函数封装代码

目前,我们的变量(如 balance)是全局变量。为了更好的代码结构和可读性,我们将把所有主逻辑封装到一个 main 函数中。

这意味着 balanceis_running 将成为 main 函数的局部变量。因此,我们需要将它们作为参数传递给 show_balancedepositwithdraw 函数,并相应地修改这些函数的定义。

最后,我们使用 if __name__ == "__main__": 这个常见的Python惯用法来调用 main 函数,这确保了我们的程序既可以独立运行,也可以作为模块被导入。

def main():
    balance = 0
    is_running = True
    # ... (将之前while循环内的所有代码移到这里,并缩进)
    # 调用函数时传入balance参数,例如:show_balance(balance)

if __name__ == "__main__":
    main()

最终程序与总结

本节课中,我们一起学习并完成了一个简单的Python银行程序。我们从规划功能开始,逐步实现了显示余额、存款和取款的核心逻辑,并通过 main 函数和参数传递优化了代码结构。

以下是完整的程序代码,它包含了所有讨论过的改进,如文本装饰和参数传递:

def show_balance(balance):
    print("******************")
    print(f"Your balance is ${balance:.2f}")
    print("******************")

def deposit():
    print("******************")
    amount = float(input("Enter amount to be deposited: "))
    if amount <= 0:
        print("That's not a valid amount.")
        return 0
    else:
        return amount

def withdraw(balance):
    print("******************")
    amount = float(input("Enter amount to be withdrawn: "))
    if amount > balance:
        print("Insufficient funds.")
        return 0
    elif amount <= 0:
        print("Amount must be greater than 0.")
        return 0
    else:
        return amount

def main():
    balance = 0
    is_running = True

    while is_running:
        print("******************")
        print("Banking Program")
        print("******************")
        print("1. Show Balance")
        print("2. Make Deposit")
        print("3. Make Withdrawal")
        print("4. Exit")
        choice = input("Enter your choice (1-4): ")

        if choice == "1":
            show_balance(balance)
        elif choice == "2":
            balance += deposit()
        elif choice == "3":
            balance -= withdraw(balance)
        elif choice == "4":
            is_running = False
        else:
            print("That is not a valid choice.")

    print("Thank you! Have a nice day!")

if __name__ == "__main__":
    main()

通过这个项目,你实践了函数定义、循环控制、条件判断、用户输入处理以及基本的程序结构设计。你可以在此基础上继续扩展,例如添加用户认证、交易记录等功能。

045:使用Python编写一个老丨虎丨机游戏 🎰

在本节课中,我们将学习如何使用Python创建一个适合初学者的老丨虎丨机游戏。我们将把项目分解为几个核心功能模块,并逐一实现它们,包括旋转转轮、显示结果和计算奖金。


项目结构与核心函数

首先,我们需要规划程序的主要功能。以下是几个核心函数:

  • spin_row():用于生成一行随机的符号。
  • print_row():用于将生成的符号行美观地打印出来。
  • get_payout():用于计算并返回玩家赢得的奖金。

我们将把大部分代码逻辑写在 main() 函数中,并在程序末尾使用 if __name__ == "__main__": 来调用它,这是一种良好的编程实践。


初始化游戏变量

现在,让我们开始编写 main() 函数。首先,我们需要初始化一些游戏运行所需的变量。

以下是需要声明的变量:

  • balance:玩家的初始余额,我们设置为100。
  • symbols:一个包含游戏符号的列表,我们将使用一些表情符号来代表水果和常见图标。
import random

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/1247bd61a278dcd91103893c45c78f49_19.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/1247bd61a278dcd91103893c45c78f49_21.png)

def main():
    balance = 100
    symbols = ["🍒", "🍉", "🍋", "🔔", "⭐"]


游戏主循环与下注逻辑

上一节我们初始化了游戏变量,本节中我们来看看游戏的主循环和下注逻辑。游戏将在玩家余额大于0时持续运行。

以下是游戏主循环中的关键步骤:

  1. 打印欢迎信息和当前余额。
  2. 提示玩家输入下注金额。
  3. 验证下注金额是否有效(是否为数字、是否大于0、是否不超过当前余额)。
  4. 如果验证通过,则从余额中扣除下注金额。

    while balance > 0:
        print(f"当前余额:${balance}")
        bet = input("请输入下注金额:")

        if not bet.isdigit():
            print("请输入一个有效的数字。")
            continue
        bet = int(bet)

        if bet > balance:
            print("余额不足。")
            continue
        if bet <= 0:
            print("下注金额必须大于0。")
            continue

        balance -= bet

实现旋转与显示功能

在成功处理下注后,我们需要让老丨虎丨机“旋转”起来并显示结果。这需要用到我们之前定义的 spin_row()print_row() 函数。

首先,我们来实现 spin_row() 函数。它的目标是生成一个包含三个随机符号的列表。

def spin_row():
    symbols = ["🍒", "🍉", "🍋", "🔔", "⭐"]
    return [random.choice(symbols) for _ in range(3)]

接着,我们来实现 print_row() 函数。它的作用是将传入的列表(即一行符号)以更美观的格式打印出来。

def print_row(row):
    print(" | ".join(row))

现在,回到主循环中,在扣除下注金额后调用这些函数。

        row = spin_row()
        print("旋转中...\n")
        print_row(row)


计算奖金与更新余额

显示结果后,我们需要判断玩家是否中奖并计算奖金。这是 get_payout() 函数的工作。

该函数接收两个参数:row(符号行列表)和 bet(下注金额)。如果三个符号相同,则根据匹配的符号类型返回相应的倍数奖金;否则返回0。

def get_payout(row, bet):
    if row[0] == row[1] == row[2]:
        if row[0] == "🍒":
            return bet * 3
        elif row[0] == "🍉":
            return bet * 4
        elif row[0] == "🍋":
            return bet * 5
        elif row[0] == "🔔":
            return bet * 10
        elif row[0] == "⭐":
            return bet * 20
    return 0

在主循环中,我们调用 get_payout() 函数,根据返回值更新玩家余额并给出提示信息。

        payout = get_payout(row, bet)
        if payout > 0:
            print(f"恭喜!你赢得了 ${payout}!")
        else:
            print("很遗憾,本轮未中奖。")
        balance += payout


添加继续游戏选项与结束处理

为了让玩家能自主控制游戏进程,我们添加一个是否继续游戏的选项。

在每一轮结束后,询问玩家是否继续。如果输入的不是“Y”(无论大小写),则退出游戏循环。

        play_again = input("是否继续游戏? (Y/N): ").upper()
        if play_again != "Y":
            break

当游戏循环结束(余额为0或玩家选择退出)时,打印最终余额和结束语。

    print(f"游戏结束!你的最终余额为:${balance}")

最后,别忘了设置程序的入口点。

if __name__ == "__main__":
    main()


总结

本节课中我们一起学习了如何使用Python构建一个完整的老丨虎丨机游戏。我们实现了以下核心功能:

  1. 游戏初始化:设置余额和游戏符号。
  2. 下注验证:确保玩家输入有效的下注金额。
  3. 随机生成:使用 random.choice() 和列表生成式来模拟转轮旋转。
  4. 奖金计算:通过条件判断,根据匹配的符号类型计算奖金倍数。
  5. 用户交互:提供了继续游戏的选择,并友好地显示游戏结果。

通过这个项目,你实践了函数定义、循环控制、条件判断和列表操作等多个Python基础概念。记住,赌博有害,编程快乐!

046:用 Python 编写一个猜词游戏(Hangman)

在本节课中,我们将一起使用 Python 创建一个经典的猜词游戏(Hangman)。我们将学习如何组织代码、使用字典存储数据、处理用户输入以及实现游戏逻辑。这是一个很好的小型项目,能帮助我们巩固编程知识。

概述

我们将创建一个猜词游戏。计算机会从一个单词列表中随机选择一个单词,玩家需要一次猜一个字母。如果猜错,屏幕上会逐步显示一个绞刑架小人。在猜错六次之前猜出所有字母则获胜,否则失败。

1. 导入模块与定义单词列表

首先,我们需要导入 random 模块,以便从列表中随机选择单词。同时,我们定义一个初始的单词列表。

import random

words = {"apple", "orange", "banana", "coconut", "pineapple"}

2. 创建绞刑架小人图案字典

接下来,我们需要一个字典来存储不同错误次数下对应的绞刑架小人 ASCII 图案。字典的键是错误次数(0到6),值是一个包含三行字符串的元组。

hangman_art = {
    0: ("", "", ""),
    1: ("   O   ", "   |   ", "       "),
    2: ("   O   ", "  /|   ", "       "),
    3: ("   O   ", "  /|\\  ", "       "),
    4: ("   O   ", "  /|\\  ", "  /    "),
    5: ("   O   ", "  /|\\  ", "  / \\  "),
    6: ("   O   ", "  /|\\  ", "  / \\  ")
}

3. 定义核心函数框架

在编写主逻辑之前,我们先定义几个核心函数的框架,这有助于组织代码结构。

def display_man(wrong_guesses):
    """根据错误次数显示绞刑架小人"""
    pass

def display_hint(hint):
    """显示当前猜词进度(如 _ _ _ _ _)"""
    pass

def display_answer(answer):
    """游戏结束时显示正确答案"""
    pass

def main():
    """游戏主函数,包含主要逻辑"""
    pass

if __name__ == "__main__":
    main()

4. 实现 display_man 函数

上一节我们定义了函数框架,本节中我们来实现 display_man 函数。它的功能是根据错误次数 wrong_guesseshangman_art 字典中取出对应的图案并打印。

def display_man(wrong_guesses):
    print("**********")
    for line in hangman_art[wrong_guesses]:
        print(line)
    print("**********")

5. 实现 display_hintdisplay_answer 函数

现在,我们来实现显示提示和答案的函数。display_hint 函数将列表形式的提示(如 ['_', '_', '_'])用空格连接并打印。display_answer 函数类似,但直接打印正确答案。

def display_hint(hint):
    print(" ".join(hint))

def display_answer(answer):
    print(" ".join(answer))

6. 编写游戏主逻辑

接下来,我们填充 main 函数,这是游戏的核心。我们将在这里初始化变量、处理游戏循环和用户交互。

首先,在 main 函数开头初始化游戏状态。

def main():
    # 随机选择答案
    answer = random.choice(list(words))
    # 初始化提示(与答案等长的下划线列表)
    hint = ["_"] * len(answer)
    # 记录错误次数
    wrong_guesses = 0
    # 记录已猜过的字母
    guessed_letters = set()
    # 游戏运行状态
    is_running = True

7. 构建游戏主循环

游戏将在 while is_running: 循环中持续运行。在循环中,我们需要依次执行以下步骤:

以下是游戏主循环中每一步的详细说明:

  1. 显示当前状态:调用 display_man(wrong_guesses)display_hint(hint) 显示绞刑架小人和猜词进度。
  2. 获取用户输入:提示用户输入一个字母,并将其转换为小写。
  3. 输入验证:检查输入是否有效(单个字母、未猜过、是字母)。
  4. 处理猜测
    • 如果猜对,更新提示列表 hint
    • 如果猜错,错误次数 wrong_guesses 加一。
  5. 检查游戏结束条件
    • 如果提示列表中没有下划线 _,玩家获胜。
    • 如果错误次数达到6次,玩家失败。

以下是实现这些步骤的代码:

    while is_running:
        display_man(wrong_guesses)
        display_hint(hint)

        guess = input("请输入一个字母: ").lower()

        # 输入验证
        if len(guess) != 1 or not guess.isalpha():
            print("无效输入,请输入单个字母。")
            continue
        if guess in guessed_letters:
            print(f"字母 {guess} 已经猜过了。")
            continue

        guessed_letters.add(guess)

        # 处理猜测
        if guess in answer:
            for i in range(len(answer)):
                if answer[i] == guess:
                    hint[i] = guess
        else:
            wrong_guesses += 1

        # 检查获胜条件
        if "_" not in hint:
            display_man(wrong_guesses)
            display_answer(answer)
            print("恭喜,你赢了!")
            is_running = False

        # 检查失败条件
        if wrong_guesses >= 6:
            display_man(wrong_guesses)
            display_answer(answer)
            print("很遗憾,你输了。")
            is_running = False

8. 扩展:从外部文件导入单词列表

为了让游戏有更多单词可选,我们可以将单词列表放在一个单独的 Python 文件中。

创建一个名为 words_list.py 的新文件,内容如下:

# words_list.py
# 猜词游戏单词库
words = {
    "apple", "orange", "banana", "coconut", "pineapple",
    "elephant", "giraffe", "kangaroo", "penguin", "dolphin"
    # 可以在这里添加更多单词
}

然后,在主程序文件开头修改导入语句:

from words_list import words
# 删除或注释掉本地的 words 定义
# words = {"apple", "orange", ...}

总结

本节课中我们一起学习了如何使用 Python 创建一个完整的猜词游戏(Hangman)。我们涵盖了以下核心知识点:

  • 使用 字典 存储不同状态下的 ASCII 图案。
  • 使用 列表 来管理游戏提示和状态。
  • 使用 集合 来记录已猜过的字母,避免重复。
  • 构建 主循环 来处理游戏流程和用户交互。
  • 实现 输入验证游戏结束条件判断
  • 通过 模块导入 将单词列表分离到外部文件,提高代码可维护性。

通过这个项目,你将能够更好地理解 Python 基础语法在实际项目中的应用,并掌握小型游戏开发的基本流程。

047:面向对象编程 🐍

在本节课中,我们将要学习Python面向对象编程的核心概念。面向对象编程是一种重要的编程范式,它允许我们将数据和操作数据的方法捆绑在一起,形成“对象”。我们将通过创建“汽车”类及其对象来理解属性、方法和类的概念。

什么是对象?📦

在Python中,对象是相关属性和方法的集合。属性类似于变量,用于描述对象拥有什么。方法则是属于对象的函数,定义了对象能做什么。

环顾四周,你会发现许多现实世界的物体,例如手机、杯子和书。每个物体都可以用不同的属性来描述。例如,手机的属性可能包括版本号、是否开机和价格。杯子的属性可能包括内部液体和温度。书的属性可能包括书名和页数。

什么是类?📐

为了创建多个对象,我们需要使用类。类是一种蓝图,用于设计对象的结构和布局。它定义了对象应具有的属性和方法。

我们将创建一个Car类,并基于它来创建汽车对象。

创建Car类 🚗

首先,我们定义一个名为Car的类。为了构造汽车对象,我们需要一个特殊的方法——构造函数。

class Car:
    def __init__(self, model, year, color, for_sale):
        self.model = model
        self.year = year
        self.color = color
        self.for_sale = for_sale

构造函数__init__是一个“双下划线”方法,用于初始化对象。self参数代表我们正在创建的这个对象本身。在构造函数内部,我们为汽车对象定义了四个属性:model(型号)、year(年份)、color(颜色)和for_sale(是否出售)。

创建汽车对象

现在,我们可以使用Car类来创建具体的汽车对象。

car1 = Car("Mustang", 2024, "red", False)
car2 = Car("Corvette", 2025, "blue", True)
car3 = Car("Charger", 2026, "yellow", True)

我们创建了三个汽车对象:car1car2car3,并为每个对象传递了不同的参数值。

访问对象属性

要访问对象的属性,我们使用点号.操作符。

print(car1.model)  # 输出: Mustang
print(car1.year)   # 输出: 2024
print(car1.color)  # 输出: red
print(car1.for_sale) # 输出: False

通过这种方式,我们可以获取每个汽车对象的特定属性信息。

为类添加方法

上一节我们介绍了如何定义对象的属性,本节中我们来看看如何为对象添加行为,即方法。方法是属于对象的函数,定义了对象能执行的操作。

以下是我们可以为汽车添加的一些方法:

class Car:
    def __init__(self, model, year, color, for_sale):
        self.model = model
        self.year = year
        self.color = color
        self.for_sale = for_sale

    def drive(self):
        print(f"You drive the {self.color} {self.model}")

    def stop(self):
        print(f"You stop the {self.color} {self.model}")

    def describe(self):
        print(f"{self.year} {self.color} {self.model}")

我们为Car类添加了三个方法:drive(驾驶)、stop(停止)和describe(描述)。在每个方法中,我们使用self来访问当前对象的属性,从而在输出中显示特定汽车的信息。

调用对象方法

现在,我们可以调用这些方法来让汽车对象执行动作。

car1.drive()   # 输出: You drive the red Mustang
car1.stop()    # 输出: You stop the red Mustang
car1.describe() # 输出: 2024 red Mustang

car2.drive()   # 输出: You drive the blue Corvette
car2.stop()    # 输出: You stop the blue Corvette
car2.describe() # 输出: 2025 blue Corvette

每个汽车对象都可以调用相同的方法,但会根据自身的属性值产生不同的输出。

代码组织

随着类变得复杂,为了更好的组织代码,我们可以将类定义放在单独的Python文件中。

  1. 创建一个名为car.py的新文件。
  2. Car类的代码剪切并粘贴到car.py中。
  3. 在主Python文件中,使用from car import Car来导入Car类。

这样,主文件中的代码可以保持整洁,并且我们可以在多个项目中重用Car类。

总结

本节课中我们一起学习了Python面向对象编程的基础知识。我们了解到:

  • 对象是相关属性方法的集合。
  • 属性是描述对象特征的变量。
  • 方法是属于对象的函数,定义了对象的行为。
  • 是创建对象的蓝图,它定义了对象的结构。

通过创建Car类和多个汽车对象,我们实践了如何定义属性、创建对象、访问属性以及调用方法。面向对象编程是构建复杂、模块化程序的重要工具,希望本教程能帮助你迈出坚实的第一步。

Python超全入门教程:P48:Python类变量详解 🧩

在本节课中,我们将要学习Python中的类变量。类变量是一种在类的所有实例之间共享数据的变量。我们将通过创建一个学生类来演示类变量的定义、访问和使用,并与实例变量进行对比。


类变量与实例变量

上一节我们介绍了类的基本概念,本节中我们来看看类变量和实例变量的区别。

类变量在类的所有实例之间共享。这意味着从一个类创建的所有对象都访问同一个类变量。相反,实例变量是每个对象独有的,定义在构造函数 __init__ 内部。

以下是核心概念的定义:

  • 类变量:定义在类内部但在任何方法(包括构造函数)之外。所有实例共享。
  • 实例变量:定义在 __init__ 方法内部,使用 self 关键字。每个实例拥有自己的副本。

创建学生类与实例变量

首先,我们创建一个 Student 类,并为其定义构造函数来初始化实例变量 nameage

class Student:
    # 类变量将在下一步定义
    def __init__(self, name, age):
        self.name = name  # 实例变量
        self.age = age    # 实例变量

构造函数 __init__ 在创建对象时自动调用。参数 self 代表当前正在创建的对象。我们创建两个学生对象:

student1 = Student("Sponongebob", 30)
student2 = Student("Patrick", 35)

现在,我们可以打印每个学生的实例变量:

print(student1.name, student1.age)  # 输出: Sponongebob 30
print(student2.name, student2.age)  # 输出: Patrick 35


定义与访问类变量

现在,我们来添加一个类变量。类变量定义在类内部,但在所有方法之外。

我们在 Student 类中添加一个表示毕业年份的类变量 class_year

class Student:
    class_year = 2024  # 类变量

    def __init__(self, name, age):
        self.name = name
        self.age = age

类变量可以被任何实例访问,也可以通过类名直接访问。以下是访问方式:

# 通过实例访问(可行,但不推荐)
print(student1.class_year)  # 输出: 2024
print(student2.class_year)  # 输出: 2024

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/3755ff6b38dda80c5dd364d2e78c0bf6_25.png)

# 通过类名访问(推荐做法)
print(Student.class_year)   # 输出: 2024

最佳实践是使用类名(如 Student.class_year)来访问类变量。这样做代码意图更清晰,能明确区分类变量和实例变量。


使用类变量跟踪对象数量

类变量的一个常见用途是跟踪从类创建了多少个对象。我们添加一个类变量 num_students,并在每次创建新学生时在构造函数中将其递增。

以下是修改后的类定义:

class Student:
    class_year = 2024
    num_students = 0  # 类变量,用于计数

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Student.num_students += 1  # 通过类名修改类变量

注意,在构造函数中,我们使用 Student.num_students 而不是 self.num_students 来确保修改的是共享的类变量,而不是创建新的实例变量。

现在,每创建一个学生,计数器就会增加。我们创建几个学生并打印总数:

student1 = Student("Sponongebob", 30)
student2 = Student("Patrick", 35)
student3 = Student("Squiidward", 55)
student4 = Student("Sandy", 27)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/3755ff6b38dda80c5dd364d2e78c0bf6_40.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/3755ff6b38dda80c5dd364d2e78c0bf6_41.png)

print(f"学生总数为: {Student.num_students}")  # 输出: 学生总数为: 4


综合示例与输出

让我们将所有内容结合起来,使用一个格式化的字符串打印毕业年份和学生名单。

# 使用f-string格式化输出
print(f"我的毕业班级是 {Student.class_year} 年,共有 {Student.num_students} 名学生。")
print(f"学生名单: {student1.name}, {student2.name}, {student3.name}, {student4.name}")

输出结果将是:

我的毕业班级是 2024 年,共有 4 名学生。
学生名单: Sponongebob, Patrick, Squiidward, Sandy

你可以轻松修改类变量 class_year,所有相关的输出都会自动更新。


总结

本节课中我们一起学习了Python中的类变量。

  • 类变量定义在类内部、方法外部,使用格式为 变量名 = 值
  • 类变量在类的所有实例之间共享,这与每个对象独有的实例变量不同。
  • 访问类变量的推荐方式是通过类名(例如 ClassName.variable_name),这使代码更清晰、更易读。
  • 类变量非常适合用于存储所有对象共有的数据(如标准、设置)或跟踪类的状态(如创建的对象数量)。

通过理解和运用类变量,你可以编写出更高效、组织性更好的面向对象程序。

Python超全入门教程:P49:Python继承入门

在本节课中,我们将要学习Python中一个非常重要的概念——继承。继承允许一个类获取另一个类的属性和方法,这有助于代码的复用和扩展。我们将通过创建一个动物类,并让狗、猫和老鼠类继承它,来直观地理解这个概念。

什么是继承?🤔

继承允许一个类从另一个类那里继承属性和方法。这类似于现实生活中的孩子可以从父母那里继承特征。通过让一个类继承另一个类的属性和方法,我们可以实现代码的复用和扩展。

创建父类:动物类 🐾

首先,我们来创建一个名为 Animal 的父类。这个类将定义所有动物共有的基本属性和行为。

class Animal:
    def __init__(self, name):
        self.name = name
        self.is_alive = True

    def eat(self):
        print(f"{self.name} is eating.")

    def sleep(self):
        print(f"{self.name} is sleeping.")

在上面的代码中:

  • __init__ 是构造函数,用于初始化每个动物对象的名字,并设置 is_alive 属性为 True
  • eatsleep 是两个方法,分别表示动物的进食和睡眠行为。

创建子类:继承的实践 🐕🐈🐁

现在,我们将创建三个子类:DogCatMouse。它们都将继承自 Animal 类。

class Dog(Animal):
    pass

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/00a95a7908a23bb5eaf52875ea6b1755_6.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/00a95a7908a23bb5eaf52875ea6b1755_7.png)

class Cat(Animal):
    pass

class Mouse(Animal):
    pass

注意,在定义子类时,我们在类名后的括号中写上了父类 Animal 的名字。这表示 DogCatMouse 类将继承 Animal 类的所有属性和方法。目前,我们使用 pass 作为占位符,表示这些子类暂时没有自己独有的内容。

使用继承的类 🚀

尽管子类内部是空的,但它们已经拥有了父类的全部功能。让我们创建对象并测试一下。

dog = Dog("Scooby")
cat = Cat("Garfield")
mouse = Mouse("Mickey")

print(dog.name)        # 输出: Scooby
print(dog.is_alive)    # 输出: True
dog.eat()              # 输出: Scooby is eating.
dog.sleep()            # 输出: Scooby is sleeping.

# 同样可以用于猫和老鼠
cat.eat()              # 输出: Garfield is eating.
mouse.sleep()          # 输出: Mickey is sleeping.

可以看到,子类对象成功使用了从父类继承来的属性和方法。

继承的优势:代码复用与维护 🔧

继承的核心优势在于避免代码重复。想象一下,如果没有继承,我们需要在 DogCatMouse 每个类里都重复编写 nameis_aliveeat()sleep()。这不仅代码冗长,而且一旦需要修改(比如把 sleep 方法输出的信息从 “is sleeping” 改为 “is asleep”),就必须在每个类里逐一修改,非常低效且容易出错。

使用继承后,我们只需在父类 Animal 中修改一次:

def sleep(self):
    print(f"{self.name} is asleep.")  # 修改这里

所有继承自 Animal 的子类都会自动应用这个更改。

扩展子类:添加特有行为 🎤

上一节我们介绍了继承如何复用代码,本节中我们来看看子类如何扩展父类的功能。子类不仅可以继承,还可以拥有自己独特的属性和方法。

例如,每种动物可能有不同的叫声:

class Dog(Animal):
    def speak(self):
        print("Woof!")

class Cat(Animal):
    def speak(self):
        print("Meow!")

class Mouse(Animal):
    def speak(self):
        print("Squeak!")

# 使用特有的方法
dog.speak()  # 输出: Woof!
cat.speak()  # 输出: Meow!
mouse.speak() # 输出: Squeak!

这样,DogCatMouse 在共享通用行为(吃、睡)的同时,也具备了各自独特的行为(叫)。

总结 📚

本节课中我们一起学习了Python中的继承。

  • 继承 允许一个类(子类)获取另一个类(父类)的属性和方法,语法是 class ChildClass(ParentClass):
  • 它的主要优点是 代码复用易于维护。通用代码只需在父类中编写一次,所有子类即可使用。修改也只需在父类中进行。
  • 子类可以通过定义自己的方法来 扩展 功能,实现与父类或其他子类的差异化。

通过继承,我们可以构建出层次清晰、易于管理和扩展的代码结构。

050:多重继承与多级继承 🐍

在本节课中,我们将要学习Python面向对象编程中的两个重要概念:多重继承多级继承。我们将通过一个生动的动物世界例子,来理解一个子类如何从多个父类继承特性,以及继承链是如何层层传递的。

概述

多重继承允许一个子类继承多个父类的属性和方法。多级继承则描述了继承的层级关系,例如子类、父类、祖父类。掌握这两种继承方式,能让你设计出更灵活、结构更清晰的代码。


多重继承:一个子类,多个父类 🧬

上一节我们介绍了单一继承。本节中我们来看看多重继承。多重继承是指一个子类可以继承自多个父类。

例如,一个类 C 可以同时继承类 A 和类 B 的特性。在Python中,只需在定义子类时,将多个父类放入继承列表中即可。

我们将通过创建“猎物”和“捕食者”两个父类来演示。

以下是创建父类的代码:

class Prey:
    def flee(self):
        print(f"{self.name} is fleeing")

class Predator:
    def hunt(self):
        print(f"{self.name} is hunting")

现在,我们创建几个子类。兔子是猎物,鹰是捕食者。

以下是创建子类的代码:

class Rabbit(Prey):
    pass

class Hawk(Predator):
    pass

鱼类比较特殊,它们既是猎物(会被更大的鱼捕食),也是捕食者(会捕食更小的鱼)。因此,Fish 类需要同时继承 PreyPredator

以下是使用多重继承创建 Fish 类的代码:

class Fish(Prey, Predator):
    pass

让我们测试一下这些类的功能。首先创建各个类的对象。

以下是测试代码:

rabbit = Rabbit()
hawk = Hawk()
fish = Fish()

# 为对象添加名字属性以便输出更清晰
rabbit.name = "Bugs"
hawk.name = "Tony"
fish.name = "Nemo"

rabbit.flee()  # 输出:Bugs is fleeing
# rabbit.hunt() # 这行会报错,因为Rabbit没有hunt方法

hawk.hunt()    # 输出:Tony is hunting
# hawk.flee()  # 这行会报错,因为Hawk没有flee方法

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/bc73539bc91bcd4ebde1c7fe8b50844b_8.png)

fish.flee()    # 输出:Nemo is fleeing
fish.hunt()    # 输出:Nemo is hunting

如你所见,Fish 对象成功地同时拥有了 fleehunt 两个方法,这正是多重继承的威力。


多级继承:继承的层级结构 🏗️

理解了多重继承后,我们再来看看多级继承。在多级继承中,一个父类本身也可以是另一个类的子类,从而形成一种层级或链条关系。

例如,我们可以创建一个所有动物的基类 Animal,让 PreyPredator 继承它。这样,RabbitHawkFish 这些“孙子辈”的类就能间接获得 Animal 类的特性。

首先,我们创建 Animal 这个“祖父”类。

以下是创建 Animal 类并添加通用方法的代码:

class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f"{self.name} is eating")

    def sleep(self):
        print(f"{self.name} is sleeping")

现在,我们需要修改 PreyPredator 类,让它们继承自 Animal

以下是修改父类继承关系的代码:

class Prey(Animal):
    def flee(self):
        print(f"{self.name} is fleeing")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/bc73539bc91bcd4ebde1c7fe8b50844b_19.png)

class Predator(Animal):
    def hunt(self):
        print(f"{self.name} is hunting")

子类 RabbitHawkFish 的继承关系保持不变。但由于它们的父类现在继承自 Animal,它们也将自动获得 eatsleep 方法。

让我们创建对象并测试完整的继承链。

以下是完整的测试代码:

# 创建对象时通过Animal的构造函数传入名字
rabbit = Rabbit("Bugs")
hawk = Hawk("Tony")
fish = Fish("Nemo")

# 测试从Animal继承的方法
rabbit.eat()   # 输出:Bugs is eating
rabbit.sleep() # 输出:Bugs is sleeping
rabbit.flee()  # 输出:Bugs is fleeing

hawk.eat()     # 输出:Tony is eating
hawk.sleep()   # 输出:Tony is sleeping
hawk.hunt()    # 输出:Tony is hunting

fish.eat()     # 输出:Nemo is eating
fish.sleep()   # 输出:Nemo is sleeping
fish.flee()    # 输出:Nemo is fleeing
fish.hunt()    # 输出:Nemo is hunting

通过多级继承,我们构建了 Animal -> Prey/Predator -> Rabbit/Hawk/Fish 这样的继承层级。最底层的子类拥有了所有祖先类的方法。


总结

本节课中我们一起学习了Python中两种重要的继承机制:

  1. 多重继承:一个子类可以继承多个父类。语法是在定义子类时,在括号内用逗号分隔多个父类,例如 class Child(Parent1, Parent2)
  2. 多级继承:继承关系可以像家族树一样层层递进。一个类继承自父类,而这个父类又继承自另一个类(祖父类)。子类将拥有整个继承链上所有祖先的属性和方法。

结合使用多重继承和多级继承,你可以设计出功能强大且层次分明的类结构,这是面向对象编程的核心技巧之一。记住,良好的继承设计能让代码更易于复用和维护。

051:抽象类

概述

在本节课中,我们将要学习Python中的抽象类。抽象类是一种不能被直接实例化的类,它通常作为其他类的父类,用于定义一组必须由其子类实现的方法。通过使用抽象类,我们可以确保子类遵循特定的接口规范,从而提高代码的结构性和可维护性。

什么是抽象类?

抽象类是一种不能独立创建对象的类。它被设计为其他类的父类,即“基类”。抽象类可以包含抽象方法,这些方法只有声明而没有具体的实现。抽象类本身是不完整的,因此我们不希望创建不完整的对象。

抽象类的好处

抽象类有几个主要优点。首先,我们无法从抽象类直接创建对象,这防止了创建不完整对象的情况。其次,任何继承自抽象类的子类,如果父类中存在抽象方法,则子类必须实现这些方法。这确保了子类遵循了父类定义的接口。

使用抽象类

要使用抽象类,我们需要从Python的abc模块中导入ABC(抽象基类)和abstractmethod装饰器。

以下是导入所需模块的代码:

from abc import ABC, abstractmethod

创建抽象类

接下来,我们创建一个名为Vehicle的抽象类。这个类将继承自ABC,并包含两个抽象方法:gostop

以下是定义抽象类的代码:

class Vehicle(ABC):
    @abstractmethod
    def go(self):
        pass

    @abstractmethod
    def stop(self):
        pass

尝试实例化抽象类

为了验证抽象类不能被实例化,我们尝试创建一个Vehicle对象。运行代码后,我们会收到一个TypeError,提示无法实例化包含抽象方法的抽象类。这正是我们期望的结果。

以下是尝试实例化的代码:

# 尝试创建Vehicle对象会引发错误
# vehicle = Vehicle()  # 这行代码会报错

创建子类并实现抽象方法

既然抽象类不能直接使用,我们就需要创建继承自它的子类。任何继承自抽象类的子类都必须实现父类中所有的抽象方法。

以下是创建Car子类的步骤:

  1. 定义Car类,继承自Vehicle
  2. Car类中实现gostop方法。

以下是Car类的代码:

class Car(Vehicle):
    def go(self):
        print("You drive the car.")

    def stop(self):
        print("You stop the car.")

现在,我们可以创建Car对象并调用其方法:

car = Car()
car.go()
car.stop()

创建更多子类

为了进一步理解,我们再创建两个子类:MotorcycleBoat。每个子类都必须实现gostop方法。

以下是Motorcycle类的代码:

class Motorcycle(Vehicle):
    def go(self):
        print("You ride the motorcycle.")

    def stop(self):
        print("You stop the motorcycle.")

以下是Boat类的代码(注意:初始版本会故意遗漏stop方法以展示错误):

class Boat(Vehicle):
    def go(self):
        print("You sail the boat.")
    # 忘记实现stop方法会导致错误

如果我们尝试创建Boat对象而不实现stop方法,Python会抛出TypeError,提示我们遗漏了抽象方法。这体现了抽象类作为“检查与平衡”机制的作用,确保所有子类都完整实现了必要的功能。

修正后的Boat类代码如下:

class Boat(Vehicle):
    def go(self):
        print("You sail the boat.")

    def stop(self):
        print("You anchor the boat.")

总结

本节课中我们一起学习了Python中的抽象类。抽象类是一种不能被直接实例化的类,它通过定义抽象方法来强制其子类实现特定的接口。我们学习了如何导入ABCabstractmethod,如何创建抽象类及其子类,并理解了抽象类在确保代码结构完整性方面的重要作用。通过使用抽象类,我们可以构建出更加健壮和可维护的面向对象程序。

052:Python中的super()函数详解 🐍

在本节课中,我们将要学习Python中的super()函数。super()是一个内置函数,用于在子类中调用父类(也称为超类)的方法。通过使用super(),我们可以有效地复用代码,并扩展继承方法的功能。

继承与代码复用

上一节我们介绍了类的基本概念,本节中我们来看看如何通过继承来避免代码重复。假设我们要创建几种形状的类:圆形、正方形和三角形。它们有一些共同的属性,比如颜色和是否填充,也有一些独特的属性。

以下是这些类的初始定义,其中包含了一些重复的代码:

class Circle:
    def __init__(self, color, filled, radius):
        self.color = color
        self.filled = filled
        self.radius = radius

class Square:
    def __init__(self, color, filled, width):
        self.color = color
        self.filled = filled
        self.width = width

class Triangle:
    def __init__(self, color, filled, width, height):
        self.color = color
        self.filled = filled
        self.width = width
        self.height = height

如果我们需要修改一个共同属性(例如将filled改为is_filled),就必须在每个类中手动更改,这既繁琐又容易出错。更好的方法是使用继承。

创建父类

我们可以创建一个名为Shape的父类,将共同的属性coloris_filled放在其中。然后让其他形状类继承这个父类。

class Shape:
    def __init__(self, color, is_filled):
        self.color = color
        self.is_filled = is_filled

现在,子类可以通过super()函数来调用父类的__init__方法,从而初始化这些共同属性。

使用super()初始化

以下是使用super()重构后的子类:

class Circle(Shape):
    def __init__(self, color, is_filled, radius):
        super().__init__(color, is_filled)  # 调用父类Shape的构造函数
        self.radius = radius

class Square(Shape):
    def __init__(self, color, is_filled, width):
        super().__init__(color, is_filled)
        self.width = width

class Triangle(Shape):
    def __init__(self, color, is_filled, width, height):
        super().__init__(color, is_filled)
        self.width = width
        self.height = height

这样,共同属性的初始化逻辑只存在于Shape类中,子类只需处理自己特有的属性。

创建对象并测试

现在我们可以创建对象并访问它们的属性:

circle = Circle(color="red", is_filled=True, radius=5)
print(f"Circle color: {circle.color}")
print(f"Circle is filled: {circle.is_filled}")
print(f"Circle radius: {circle.radius} cm")

square = Square(color="blue", is_filled=False, width=6)
print(f"Square color: {square.color}")
print(f"Square is filled: {square.is_filled}")
print(f"Square width: {square.width} cm")

triangle = Triangle(color="yellow", is_filled=True, width=7, height=8)
print(f"Triangle color: {triangle.color}")
print(f"Triangle is filled: {triangle.is_filled}")
print(f"Triangle width: {triangle.width} cm")
print(f"Triangle height: {triangle.height} cm")

方法继承与重写

父类可以定义一些通用方法,子类会继承它们。例如,我们在Shape类中添加一个describe方法:

class Shape:
    # ... __init__ 方法同上 ...
    def describe(self):
        status = "filled" if self.is_filled else "not filled"
        print(f"It is {self.color} and {status}.")

所有子类都将拥有这个方法:

circle.describe()  # 输出: It is red and filled.
square.describe()  # 输出: It is blue and not filled.
triangle.describe() # 输出: It is yellow and filled.

如果子类需要不同的行为,可以重写(Override)父类的方法。例如,为Circle类定义一个自己的describe方法来计算面积:

class Circle(Shape):
    # ... __init__ 方法同上 ...
    def describe(self):
        area = 3.14 * self.radius * self.radius
        print(f"It is a circle with an area of {area} cm².")

现在调用circle.describe()将使用子类自己的版本。

使用super()扩展方法功能

有时,我们既想使用父类方法的功能,又想添加一些子类特有的功能。这时可以在子类方法中使用super()来调用父类方法。

以下是扩展describe方法的例子:

class Circle(Shape):
    # ... __init__ 方法同上 ...
    def describe(self):
        # 首先调用父类的describe方法
        super().describe()
        # 然后添加子类特有的功能
        area = 3.14 * self.radius * self.radius
        print(f"It is a circle with an area of {area} cm².")

调用circle.describe()会先输出颜色和填充状态,再输出面积信息。

我们也可以为SquareTriangle类实现类似的重写:

class Square(Shape):
    # ... __init__ 方法同上 ...
    def describe(self):
        super().describe()
        area = self.width * self.width
        print(f"It is a square with an area of {area} cm².")

class Triangle(Shape):
    # ... __init__ 方法同上 ...
    def describe(self):
        super().describe()
        area = (self.width * self.height) / 2
        print(f"It is a triangle with an area of {area} cm².")

总结

本节课中我们一起学习了Python中的super()函数。super()用于在子类中调用父类(超类)的方法。它的主要用途有两个:

  1. 在构造函数中:初始化从父类继承的共同属性,避免代码重复。
  2. 在其他方法中:扩展继承方法的功能,可以在执行子类代码的同时保留父类方法的行为。

通过使用super()和继承,我们可以构建出层次清晰、易于维护的类结构。记住,super()让你能站在“巨人(父类)的肩膀上”构建功能。

053:多态性

在本节课中,我们将要学习Python中的一个重要概念——多态性。多态性允许对象以多种形式存在,是面向对象编程的核心特性之一。我们将通过继承机制来理解并实现多态。

什么是多态性?🤔

多态性是一个编程概念。它是一个希腊词汇,意为“多种形式”或“多张面孔”。

  • Poly 意为“多”。
  • Morph 意为“形式”。

在编程中,一个对象可以呈现出多种形式之一。实现多态主要有两种方式:一种是通过继承,另一种是通过鸭子类型。本节我们将重点讨论通过继承实现的多态。

通过继承实现多态 🧬

上一节我们介绍了多态的基本概念,本节中我们来看看如何通过继承来实现它。当一个对象可以被视为其父类的类型时,就体现了多态。

我们将创建一个Shape(形状)基类,并让其他具体形状类继承它。

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

Shape类是一个抽象基类,它定义了一个抽象方法area。这意味着任何继承自Shape的类都必须实现自己的area方法。

接下来,我们创建几个具体的形状类。

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * (self.radius ** 2)

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

class Triangle(Shape):
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return 0.5 * self.base * self.height

现在,我们可以创建这些类的对象。一个Circle对象既是Circle类型,也是Shape类型。这就是它的两种“形式”。同理,SquareTriangle对象也各自有两种形式。

多态性的应用实例 💡

理解了多态的原理后,我们来看一个实际应用的例子。我们可以创建一个Shape类型的列表,并统一处理其中不同类型的对象。

以下是创建形状列表并计算面积的步骤:

  1. 创建一个空列表shapes
  2. 实例化不同的形状对象(Circle, Square, Triangle)并添加到列表中。
  3. 遍历列表,对每个形状调用其area方法。

shapes = []

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4613933901af91f6b5ef86db8d5eb0fb_6.png)

shapes.append(Circle(4))
shapes.append(Square(5))
shapes.append(Triangle(6, 7))

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4613933901af91f6b5ef86db8d5eb0fb_8.png)

for shape in shapes:
    print(f"{shape.area()} 平方厘米")

运行这段代码,你将看到每个形状计算出的面积。尽管列表中的对象具体类型不同,但我们可以将它们统一视为Shape类型来调用area方法,这正是多态性的威力。

扩展多态性:披萨的例子 🍕

为了更深入地理解,我们引入一个Pizza类。最初,Pizza类与Shape无关,因此无法放入shapes列表或调用area方法。

class Pizza:
    def __init__(self, topping, radius):
        self.topping = topping
        self.radius = radius

# 尝试添加到shapes列表会出错,因为Pizza没有area方法
# shapes.append(Pizza("pepperoni", 15)) # 这行会引发AttributeError

为了让Pizza也能计算面积并被视为一种形状,我们可以让它继承自Circle类。

class Pizza(Circle):
    def __init__(self, topping, radius):
        super().__init__(radius) # 调用父类Circle的构造函数来设置半径
        self.topping = topping

# 现在可以成功添加并计算面积
shapes.append(Pizza("pepperoni", 15))

现在,Pizza对象具有了三种形式:它是一个Pizza,也是一个Circle(因为继承),同时也是一个Shape(因为Circle继承自Shape)。因此,它可以自然地放入shapes列表并参与面积计算。

总结 📚

本节课中我们一起学习了Python中的多态性。

  • 多态性意味着“多种形式”,它允许我们将不同的对象视为同一类型进行处理。
  • 我们重点学习了通过继承实现多态:子类对象可以被当作父类类型使用。
  • 我们创建了Shape类层次结构,并演示了如何将CircleSquareTriangle甚至Pizza对象统一放入一个列表,并调用共同的area方法。
  • 多态性提高了代码的灵活性和可扩展性,使得添加新功能(如新的形状)变得更容易,而无需修改处理这些对象的通用代码。

记住,实现多态的另一种常见方式是“鸭子类型”,我们将在后续主题中讨论。

Python超全入门教程:P54:鸭子类型 🦆

在本节课中,我们将要学习Python中一个重要的概念——鸭子类型。这是一种实现多态性的方式,它不依赖于传统的继承机制,而是关注对象的行为。

概述

鸭子类型是Python实现多态性的一种方式。其核心思想是:一个对象的类型由其行为(拥有的属性和方法)决定,而不是由其声明的类决定。这遵循一句谚语:“如果它看起来像鸭子,走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。”

通过继承实现多态

在深入鸭子类型之前,我们先回顾一下如何使用继承来实现多态。我们创建几个类来演示。

首先,定义一个基础的 Animal 类,它有一个表示生物状态的属性 alive

class Animal:
    alive = True

接着,创建 Dog 类继承自 Animal 类,并为其添加一个 speak 方法。

class Dog(Animal):
    def speak(self):
        print("Woof")

同样,创建 Cat 类也继承自 Animal 类,并定义自己的 speak 方法。

class Cat(Animal):
    def speak(self):
        print("Meow")

现在,我们可以创建一个包含不同动物对象的列表,并让它们依次“说话”。由于 DogCat 都继承自 Animal 并拥有 speak 方法,它们可以被统一处理。

animals = [Dog(), Cat()]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/fd124d4f4fdba10a181c73ffd5d33915_10.png)

for animal in animals:
    animal.speak()

执行上述代码,输出结果为:

Woof
Meow

上一节我们介绍了通过继承实现多态,本节中我们来看看如何不依赖继承,仅通过行为来实现相同的效果。

引入鸭子类型

现在,我们引入一个与 Animal 毫无继承关系的 Car 类。按照传统思路,它不应该被放入动物列表中。

class Car:
    def horn(self):
        print("Honk")

如果我们尝试将 Car 对象加入 animals 列表并调用 speak 方法,程序会报错,因为 Car 类没有 speak 方法。

animals = [Dog(), Cat(), Car()] # 这里会出错

错误信息是:AttributeError: 'Car' object has no attribute 'speak'

这就是鸭子类型发挥作用的地方。我们不需要让 Car 继承 Animal,只需要让它“看起来”像动物——即拥有动物所需的最小必要行为。

我们修改 Car 类,将 horn 方法改名为 speak

class Car:
    def speak(self):  # 将方法名改为 speak
        print("Honk")
    alive = False     # 同时添加 alive 属性

现在,Car 类拥有了 speak 方法和 alive 属性。尽管它在类定义上与 Animal 无关,但它满足了被当作“动物”处理的最低要求。

再次运行循环:

animals = [Dog(), Cat(), Car()]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/fd124d4f4fdba10a181c73ffd5d33915_32.png)

for animal in animals:
    animal.speak()
    print(animal.alive)

输出结果为:

Woof
True
Meow
True
Honk
False

Car 对象成功地被当作“动物”处理了,因为它“叫起来像鸭子”(有 speak 方法),并且“看起来像鸭子”(有 alive 属性)。

核心概念总结

以下是鸭子类型的核心要点:

  • 关注行为,而非类型:Python不检查对象的类型(是否继承自某个父类),只检查对象是否拥有需要的方法或属性。
  • 最小必要接口:只要对象实现了预期的方法(如 speak)和属性(如 alive),它就可以被当作特定类型的对象使用。
  • 提高灵活性:代码不再紧密耦合于特定的类层次结构,任何对象只要符合接口,就可以无缝接入。

其核心思想可以用一个简单的条件逻辑来描述:

# 伪代码逻辑
if hasattr(object, ‘speak’) and callable(object.speak):
    # 那么可以把它当作会“说话”的对象来处理
    object.speak()

本节课总结

本节课中我们一起学习了Python的鸭子类型。

  • 我们首先回顾了通过继承实现多态的传统方法。
  • 然后,我们引入了鸭子类型的概念,它通过对象的行为(拥有的方法和属性)来实现多态,而不强制要求继承关系。
  • 通过 Car 类的例子,我们演示了只要一个对象拥有所需的 speak 方法和 alive 属性,即使它不是一个真正的“动物”,也可以被相关代码处理。
  • 鸭子类型体现了Python的动态性和灵活性,是“协议”或“接口”编程思想的体现,在Python标准库和众多框架中广泛应用。

记住这句格言:“如果它走起来像鸭子,叫起来也像鸭子,那么它就可以被当作鸭子。” 这就是鸭子类型的精髓。

055:理解聚合关系 🧩

在本节课中,我们将要学习面向对象编程中的一个重要概念——聚合。聚合描述了一种对象之间的关系,其中一个对象作为容器,可以包含一个或多个独立的对象。我们将通过创建一个简单的“图书馆”和“图书”的例子来理解这个概念。

什么是聚合?🤔

聚合代表一种关系,其中一个对象(整体)包含对一个或多个独立对象(部分)的引用。整体对象充当容器,它可以容纳其他对象。关键点在于,整体和部分可以独立存在,它们之间没有强制的生命周期依赖。

创建独立的部分:图书类 📚

首先,我们来创建代表“部分”的类。在这个例子中,我们将创建一个 Book 类。每本书都有标题和作者两个属性。

以下是 Book 类的定义:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
  • __init__ 是构造函数,在创建 Book 对象时自动调用。
  • self.title = title 将传入的 title 参数赋值给对象的 title 属性。
  • self.author = author 将传入的 author 参数赋值给对象的 author 属性。

创建整体:图书馆类 🏛️

接下来,我们创建代表“整体”的 Library 类。图书馆有自己的名字,并且可以容纳多本图书。我们将用一个列表来存储这些图书。

以下是 Library 类的初始定义:

class Library:
    def __init__(self, name):
        self.name = name
        self.books = []  # 初始化一个空列表来存放图书对象
  • self.name = name 设置图书馆的名字。
  • self.books = [] 初始化一个空列表 books,用于将来存放 Book 对象。

建立聚合关系:添加图书到图书馆 ➕

现在,我们需要一种方法将独立的 Book 对象添加到 Library 对象中。这通过在 Library 类中定义一个 add_book 方法来实现。

以下是 add_book 方法的定义:

class Library:
    def __init__(self, name):
        self.name = name
        self.books = []

    def add_book(self, book):
        self.books.append(book)
  • def add_book(self, book): 定义了一个方法,它接受一个 book 参数(一个 Book 对象)。
  • self.books.append(book) 使用列表的 append 方法,将传入的 book 对象添加到 self.books 列表中。

实践操作:创建对象并建立关系 🛠️

上一节我们介绍了如何定义类和方法,本节中我们来看看如何实际使用它们。

以下是创建对象并建立聚合关系的步骤:

  1. 创建图书馆对象my_library = Library("纽约公共图书馆")
  2. 创建独立的图书对象
    • book1 = Book("哈利·波特...", "J.K.罗琳")
    • book2 = Book("霍比特人", "J.R.R.托尔金")
    • book3 = Book("魔法的颜色", "特里·普拉切特")
  3. 将图书添加到图书馆
    • my_library.add_book(book1)
    • my_library.add_book(book2)
    • my_library.add_book(book3)

此时,my_library 对象通过其 books 列表,包含了三个 Book 对象的引用。这些 Book 对象是独立创建的,即使没有图书馆,它们依然存在。

查看聚合内容:列出所有图书 📃

为了验证我们的聚合关系,我们可以在 Library 类中添加一个方法来列出所有图书。

以下是 list_books 方法的定义:

class Library:
    # ... 之前的 __init__ 和 add_book 方法 ...

    def list_books(self):
        book_list = []
        for book in self.books:
            book_list.append(f"{book.title} 作者:{book.author}")
        return book_list

这个方法遍历 self.books 列表中的每一个 Book 对象,并将其标题和作者格式化成字符串,最后返回一个字符串列表。

我们可以这样使用它:

print(my_library.name)
for book_info in my_library.list_books():
    print(book_info)

输出结果将显示图书馆的名字和馆内所有图书的详细信息。

总结 📝

本节课中我们一起学习了Python中的聚合关系。我们了解到:

  • 聚合是一种“整体-部分”关系,但整体和部分可以独立存在。
  • 我们通过创建 Library(整体)和 Book(部分)两个类来演示。
  • Library 类通过一个列表属性来“包含” Book 对象的引用。
  • 使用 add_book 方法可以建立聚合关系。
  • 使用 list_books 方法可以查看聚合的内容。

理解聚合有助于你设计更清晰、耦合度更低的面向对象程序。它与“组合”关系的主要区别在于生命周期的独立性。

Python超全入门教程:P56:组合(Composition)概念与实践

在本节课中,我们将要学习Python面向对象编程中的一个重要概念——组合。我们将通过一个汽车、引擎和车轮的实例,来理解组合与聚合的区别,并掌握如何在类中实现组合关系。


概述:组合与聚合的区别

上一节我们介绍了聚合(Aggregation),它是一种“拥有”(has-a)的关系,其中一个对象包含对其他独立对象的引用。组合(Composition)则是一种“拥有所有权”(owns-a)的关系,被组合的对象直接拥有其组件,并且这些组件不能独立存在。这类似于租房(聚合)与买房(组合)的区别。

构建组件类:引擎与车轮

在实现组合之前,我们需要先创建将被组合的组件类。

以下是引擎(Engine)类的定义。它有一个构造函数,用于接收并设置马力(horsepower)属性。

class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower

以下是车轮(Wheel)类的定义。它同样有一个构造函数,用于接收并设置尺寸(size)属性。

class Wheel:
    def __init__(self, size):
        self.size = size

构建组合类:汽车

现在,我们来创建组合类——汽车(Car)。汽车类将“拥有”一个引擎和四个车轮。

在汽车类的构造函数中,我们不仅接收汽车的基本信息(品牌、型号),还接收引擎马力和车轮尺寸。关键步骤在于,我们在构造函数内部直接创建了EngineWheel的实例。

class Car:
    def __init__(self, make, model, horsepower, wheel_size):
        self.make = make
        self.model = model
        # 组合:在Car内部创建并拥有一个Engine对象
        self.engine = Engine(horsepower)
        # 组合:在Car内部创建并拥有四个Wheel对象
        self.wheels = [Wheel(wheel_size) for _ in range(4)]

代码解析

  • self.engine = Engine(horsepower):这行代码在Car对象内部创建了一个Engine对象。这个引擎的生命周期与这辆汽车绑定。
  • self.wheels = [Wheel(wheel_size) for _ in range(4)]:这行代码使用列表推导式创建了一个包含四个Wheel对象的列表。这四个车轮也是由这辆汽车直接拥有。

使用组合类

现在,我们可以创建汽车对象并访问其组件属性。

以下是创建和显示汽车对象信息的步骤。

  1. 创建汽车对象:我们使用关键字参数来提高代码可读性。
    car1 = Car(make="Ford", model="Mustang", horsepower=500, wheel_size=18)
    
  2. 添加显示方法:在Car类中添加一个方法来展示汽车详情。注意访问组件属性的方式:需要通过汽车对象访问其组件,再访问组件的属性。
    class Car:
        # ... __init__ 方法同上 ...
        def display_car(self):
            return f"{self.make} {self.model} | Engine: {self.engine.horsepower}HP | Wheel Size: {self.wheels[0].size} inches"
    
  3. 调用显示方法
    print(car1.display_car())
    # 输出:Ford Mustang | Engine: 500HP | Wheel Size: 18 inches
    
  4. 创建第二个对象:组合关系是独立的,每辆车都拥有自己的一套引擎和车轮。
    car2 = Car(make="Chevrolet", model="Corvette", horsepower=670, wheel_size=19)
    print(car2.display_car())
    # 输出:Chevrolet Corvette | Engine: 670HP | Wheel Size: 19 inches
    

组合的核心特征

让我们通过一个对比来总结组合的核心特征。

  • 组合(本例)Car对象在自身内部创建EngineWheel对象。如果删除car1car2,那么它们所拥有的引擎和车轮对象也会随之被销毁,无法独立存在。
  • 聚合(上节课例子)Library对象接收外部已经存在的Book对象列表。如果删除图书馆对象,其中的书籍对象仍然可以独立存在。

这体现了“所有权”的差异:组合是紧密的拥有关系,而聚合是松散的包含关系。


总结

本节课中我们一起学习了Python中的组合(Composition)。

  1. 我们明确了组合是一种“owns-a”关系,被组合对象直接创建并拥有其组件。
  2. 我们通过构建EngineWheelCar类,实践了如何在类内部实例化其他类对象来实现组合。
  3. 我们理解了组合与聚合的关键区别在于组件对象的生命周期是否依赖于主体对象。
  4. 我们掌握了访问组合对象属性的方法(如self.engine.horsepower)。

组合有助于构建更复杂、关系更紧密的对象模型,是面向对象设计中的重要工具。

057:嵌套类 🏗️

在本节课中,我们将要学习Python中的嵌套类。嵌套类是指定义在另一个类内部的类。我们将探讨它的用途、好处,并通过一个完整的示例来演示如何创建和使用嵌套类。


嵌套类的概念与好处

嵌套类是一个定义在另一个类内部的类。使用嵌套类主要有以下几个好处:

  • 逻辑分组:可以将紧密相关的类组织在一起。
  • 信息封装:可以封装那些对外部类不重要的私有细节。
  • 保持命名空间整洁:有助于减少命名冲突的可能性。

例如,如果直接定义两个同名的类,会产生命名冲突。

class Employee:
    print("这是第一个类")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_5.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_6.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_8.png)

class Employee:
    print("这是第二个类")

运行上述代码,两个类都会被执行,这清楚地表明了存在命名冲突。在大型项目中,频繁导入导出文件时,可能不易察觉此类冲突。嵌套类可以帮助我们避免这个问题。


嵌套类示例:公司与员工

上一节我们介绍了嵌套类的基本概念,本节中我们来看看如何在实际场景中应用它。

假设我们有两个组织:一个营利性公司和一个非营利组织。它们都有员工,但员工的属性可能不同。我们可以为每个组织创建一个外部类,并在其中定义同名的内部Employee类。

class Company:
    class Employee:
        pass

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_16.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_17.png)

class Nonprofit:
    class Employee:
        pass

这样,两个Employee类虽然同名,但由于处于不同的作用域(分别属于CompanyNonprofit),因此不会产生冲突,实现了命名空间的整洁和类的复用。

接下来,我们将构建一个更完整的例子,创建属于公司对象的员工对象。


构建公司类(外部类)

首先,我们定义外部类Company。它有一个构造函数,用于接收公司名称并初始化一个用于存储员工的空列表。

class Company:
    def __init__(self, company_name):
        self.company_name = company_name
        self.employees = []  # 用于存储员工对象的列表

我们还为Company类定义了两个方法框架:add_employee用于添加员工,list_employees用于列出所有员工。

    def add_employee(self, name, position):
        pass  # 稍后实现

    def list_employees(self):
        pass  # 稍后实现

现在,我们可以测试公司名称是否设置成功。

company1 = Company("蟹堡王")
print(company1.company_name)  # 输出:蟹堡王


构建员工类(内部类)并实现关联

上一节我们创建了公司类的基本结构,本节中我们来完善内部的员工类,并实现将员工添加到公司的方法。

首先,在Company类内部定义Employee类。它有自己的构造函数和获取详细信息的方法。

class Company:
    # ... 之前的 __init__ 方法 ...

    class Employee:
        def __init__(self, name, position):
            self.name = name
            self.position = position

        def get_details(self):
            return f"{self.name} - {self.position}"

现在,回到Company类的add_employee方法。我们需要在这里创建Employee对象。要访问内部类,需要使用self.Employeeself指代当前的公司对象)。

    def add_employee(self, name, position):
        # 创建新的员工对象
        new_employee = self.Employee(name, position)
        # 将员工对象添加到公司员工列表中
        self.employees.append(new_employee)


列出员工与代码演示

我们已经实现了添加员工的功能,现在来实现list_employees方法,并演示整个流程。

list_employees方法将遍历员工列表,并调用每个员工的get_details方法。

    def list_employees(self):
        # 使用列表推导式返回所有员工的详细信息
        return [emp.get_details() for emp in self.employees]

现在,让我们使用这个结构来创建公司和员工。

# 创建第一个公司
company1 = Company("蟹堡王")

# 为第一个公司添加员工
company1.add_employee("尤金·蟹老板", "经理")
company1.add_employee("海绵宝宝", "厨师")
company1.add_employee("章鱼哥", "收银员")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_62.png)

# 列出第一个公司的所有员工
print(f"{company1.company_name}的员工:")
for employee in company1.list_employees():
    print(employee)

运行上述代码,输出结果如下:

蟹堡王的员工:
尤金·蟹老板 - 经理
海绵宝宝 - 厨师
章鱼哥 - 收银员


演示类的可重用性

为了展示嵌套类带来的命名空间整洁和类的可重用性,我们可以轻松创建第二个公司及其独立的员工列表。

# 创建第二个公司
company2 = Company("海之霸")

# 为第二个公司添加员工
company2.add_employee("谢尔顿·痞老板", "经理")
company2.add_employee("凯伦", "助理")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/9e9b040187b388b4c9a73869056ddbef_74.png)

# 列出第二个公司的所有员工
print(f"\n{company2.company_name}的员工:")
for employee in company2.list_employees():
    print(employee)

运行后,输出结果如下:

海之霸的员工:
谢尔顿·痞老板 - 经理
凯伦 - 助理

可以看到,两个公司对象完全独立,它们内部的Employee类互不干扰,完美复用了类的结构。


总结 🎯

本节课中我们一起学习了Python中的嵌套类。

  • 嵌套类是一个定义在另一个类(外部类)内部的类(内部类)。
  • 它的主要好处包括:
    1. 对紧密相关的类进行逻辑分组(如将Employee置于Company内)。
    2. 封装外部类不需要知道的私有细节。
    3. 保持命名空间整洁减少命名冲突(可以在不同外部类中使用同名的内部类)。
  • 我们通过构建Company和内部Employee类的完整示例,演示了如何创建嵌套类、在外部类中实例化内部类对象,以及如何利用这种结构管理不同实体的数据。

通过使用嵌套类,你可以写出更有组织性、更模块化且不易出错的代码。

058:静态方法详解 🧑‍🏫

在本节课中,我们将要学习Python中的静态方法。我们将了解什么是静态方法,它与实例方法有何不同,以及如何定义和使用静态方法。


什么是静态方法?

静态方法是一种属于类本身,而非属于该类任何对象的方法。我们之前已经熟悉了实例方法,它们属于从类创建的单个对象,最适合对该类的实例(即对象)进行操作。而静态方法最适合作为类内部的工具函数,它们不需要访问类的数据。

接下来,我们将通过演示来区分实例方法和静态方法。


创建示例类:Employee

我们将创建一个Employee类。首先,我们需要一个构造函数。

class Employee:
    def __init__(self, name, position):
        self.name = name
        self.position = position

要创建一个员工对象,我们需要姓名和职位。我们将self.name赋值为nameself.position赋值为position


定义实例方法

我们将创建一个名为get_info的实例方法。

    def get_info(self):
        return f"Employee info: {self.name} is a {self.position}."

get_info是一个实例方法。我们从这个类创建的每个对象都将拥有自己的get_info方法来返回该对象的信息,即对象的姓名和职位。


定义静态方法

现在我们来创建一个静态方法。要创建静态方法,我们需要使用@staticmethod装饰器。静态方法最适合作为类内部的通用工具函数。

我们将定义一个方法来检查某个职位是否有效,并将其命名为is_valid_position

    @staticmethod
    def is_valid_position(position):
        valid_positions = ["manager", "cashier", "cook", "janitor"]
        return position in valid_positions

静态方法的第一个参数不是self,因为我们不处理从该类创建的任何对象。为了检查职位是否有效,我们将传入一个职位参数。我们创建了一个有效职位的列表,然后使用成员运算符检查传入的职位是否在我们的有效职位列表中。

这样,我们就创建了一个静态方法。我们不需要依赖任何对象来使用这个方法。


如何使用静态方法

要使用静态方法,我们需要使用类名,而不是从该类创建的任何对象。

print(Employee.is_valid_position("cook"))  # 输出:True
print(Employee.is_valid_position("rocket scientist"))  # 输出:False

我们输入类名,后跟静态方法is_valid_position。这个方法接受一个参数。我们检查“cook”是否是一个有效职位,输出为True。再检查“rocket scientist”,输出为False,因为在蟹堡王,“火箭科学家”不是一个有效职位。

静态方法属于类,而不属于从该类创建的任何对象。


创建对象并使用实例方法

现在,让我们创建几个员工对象。

employee1 = Employee("Eugene", "manager")
employee2 = Employee("Squidward", "cashier")
employee3 = Employee("Spongebob", "cook")

要调用实例方法,我们必须访问类的一个实例才能使用它。

print(employee1.get_info())
print(employee2.get_info())
print(employee3.get_info())

输出结果将是:

  • Eugene 是经理。
  • Squidward 是收银员。
  • Spongebob 是厨师。

对于实例方法,你需要访问一个对象,然后调用该实例方法。


静态方法与实例方法的对比

对于静态方法,你只需要访问那个类,甚至不需要从该类创建任何对象。它是一个通用的工具方法。

以下是关键区别总结:

  • 调用方式:实例方法通过对象.方法名()调用;静态方法通过类名.方法名()调用。
  • 参数:实例方法的第一个参数是self,代表对象自身;静态方法没有self参数。
  • 用途:实例方法用于操作或访问特定对象的数据;静态方法用于执行与类相关但不依赖对象状态的通用任务。

总结

本节课中我们一起学习了Python中的静态方法。静态方法是一种属于类本身而非其实例对象的方法。它们通常用作不需要访问类数据的通用工具函数。我们通过创建Employee类,对比了实例方法get_info和静态方法is_valid_position的定义与调用方式,清晰地理解了两者的区别和适用场景。

Python超全入门教程:P59:类方法详解 🧑‍🏫

在本节课中,我们将学习Python中的类方法。我们将了解类方法的定义、它与实例方法的区别,并通过一个学生管理系统的例子来演示如何使用类方法来操作类级别的数据。


类方法与实例方法的区别

上一节我们介绍了实例方法,它用于操作类的具体对象。本节中我们来看看类方法。

类方法用于执行与类本身相关的操作,而不是与类的某个特定实例相关。定义类方法时,使用装饰器 @classmethod,并且其第一个参数是 cls(代表类本身),而不是 self(代表实例)。

以下是两种方法的核心区别:

  • 实例方法:第一个参数是 self,用于访问和修改实例属性
  • 类方法:第一个参数是 cls,用于访问和修改类属性


创建示例类:Student

我们将创建一个 Student 类来演示类方法的应用。这个类将记录学生总数和所有学生的总GPA。

首先,定义类的构造方法 __init__ 和两个类变量:

class Student:
    count = 0  # 类变量,用于统计学生总数
    total_gpa = 0.0  # 类变量,用于累计所有学生的GPA总和

    def __init__(self, name, gpa):
        self.name = name  # 实例属性:学生姓名
        self.gpa = gpa    # 实例属性:学生GPA
        Student.count += 1  # 每创建一个学生,总数加1
        Student.total_gpa += gpa  # 将新学生的GPA加入总和

在构造方法中,每当创建一个新的 Student 对象时,我们都会更新类变量 counttotal_gpa


定义实例方法

实例方法用于获取单个学生的信息。以下是 get_info 方法的定义:

    def get_info(self):  # 实例方法,参数为 self
        return f"学生姓名:{self.name}, GPA:{self.gpa}"

这个方法通过 self 访问当前实例的属性。


定义类方法

现在,我们来定义类方法。类方法用于操作类级别的数据。

以下是获取学生总数的类方法 get_count

    @classmethod  # 类方法装饰器
    def get_count(cls):  # 类方法,第一个参数是 cls
        return f"学生总人数:{cls.count}"

要调用类方法,我们使用类名而不是实例名:Student.get_count()

以下是计算平均GPA的类方法 get_average_gpa

    @classmethod
    def get_average_gpa(cls):
        if cls.count == 0:  # 避免除零错误
            return 0
        average = cls.total_gpa / cls.count  # 计算公式:总和 / 人数
        return f"平均GPA:{average:.2f}"  # 格式化为两位小数

这个方法使用类变量 total_gpacount 来计算所有学生的平均GPA。


测试代码

让我们创建几个学生对象并测试我们的方法:

# 调用类方法,此时还没有学生
print(Student.get_count())

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_35.png)

# 创建三个学生对象
student1 = Student("海绵宝宝", 3.2)
student2 = Student("派大星", 2.0)
student3 = Student("珊迪", 4.0)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_37.png)

# 再次调用类方法查看总数
print(Student.get_count())

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_39.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_41.png)

# 调用实例方法查看某个学生的信息
print(student1.get_info())

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_43.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/5bfb5bb56c14c748bea4c8745236b37a_45.png)

# 调用类方法查看平均GPA
print(Student.get_average_gpa())

运行上述代码,输出结果将依次显示学生总数从0变为3,并显示第一个学生的信息以及所有学生的平均GPA。


方法使用场景总结

本节课中我们一起学习了Python中三种主要方法的使用场景:

  1. 实例方法:最适合对类的实例(对象) 进行操作。它们可以访问和修改对象的属性。
  2. 静态方法(本课未详述):最适合作为通用工具函数,它们不需要访问类或实例的任何数据。
  3. 类方法:最适合处理类级别的数据。当你需要访问或修改类变量,或者操作与类本身相关而不是与特定实例相关的逻辑时,就应该使用类方法。其标志是使用 @classmethod 装饰器和 cls 参数。

简而言之,记住这个核心区别:操作对象用 self(实例方法),操作类本身用 cls(类方法)。

060:Python魔法方法详解 🪄

在本节课中,我们将要学习Python中的魔法方法。魔法方法允许我们自定义对象在使用Python内置操作(如打印、比较、相加)时的行为,是面向对象编程中非常强大的工具。

什么是魔法方法?

上一节我们介绍了类的基本概念,本节中我们来看看魔法方法。魔法方法,也称为“Dunder方法”(双下划线方法),其特点是方法名以双下划线开头和结尾。我们最熟悉的例子是 __init__ 方法,它在创建对象时自动调用。但Python提供了许多其他魔法方法,用于自定义对象的各种行为。

创建示例类:Book

为了演示魔法方法,我们将创建一个 Book 类。这个类将包含书名、作者和页数三个属性。

class Book:
    def __init__(self, title, author, number_of_pages):
        self.title = title
        self.author = author
        self.number_of_pages = number_of_pages

现在,让我们创建几个 Book 对象:

book1 = Book("The Hobbit", "J.R.R. Tolkien", 310)
book2 = Book("Harry Potter and the Philosopher's Stone", "J.K. Rowling", 223)
book3 = Book("The Lion, the Witch and the Wardrobe", "C.S. Lewis", 172)

当我们调用 Book() 类并传入参数时,会自动调用 __init__ 这个魔法方法,完成对象的初始化。

自定义打印行为:__str__

如果我们直接打印一个对象,Python默认会输出其内存地址,这通常不是我们想要的。

print(book1)  # 输出类似:<__main__.Book object at 0x...>

我们可以通过定义 __str__ 方法来定制打印对象时显示的字符串。

class Book:
    # ... __init__ 方法同上 ...

    def __str__(self):
        return f"'{self.title}' by {self.author}"

现在,打印对象会显示更友好的信息:

print(book1)  # 输出:'The Hobbit' by J.R.R. Tolkien
print(book2)  # 输出:'Harry Potter and the Philosopher's Stone' by J.K. Rowling

自定义对象比较:__eq____lt__

默认情况下,即使两个对象的属性值完全相同,Python也会认为它们是不同的对象。

print(book1 == book2)  # 输出:False

我们可以通过 __eq__ 方法定义“相等”的含义。例如,我们认为两本书如果书名和作者相同,就是同一本书(忽略页数差异)。

class Book:
    # ... 之前的代码 ...

    def __eq__(self, other):
        return self.title == other.title and self.author == other.author

现在,如果创建另一本《霍比特人》,即使页数不同,也会被视为相等。

book1_alt = Book("The Hobbit", "J.R.R. Tolkien", 400)
print(book1 == book1_alt)  # 输出:True

同样,我们可以用 __lt__(小于)和 __gt__(大于)方法来比较对象。这里我们根据页数来比较。

class Book:
    # ... 之前的代码 ...

    def __lt__(self, other):
        return self.number_of_pages < other.number_of_pages

    def __gt__(self, other):
        return self.number_of_pages > other.number_of_pages

print(book2 < book3)  # 输出:False (223 < 172 为假)
print(book2 > book3)  # 输出:True  (223 > 172 为真)

自定义加法操作:__add__

尝试将两个书对象相加会引发错误。我们可以用 __add__ 方法定义“加法”的意义,例如计算两本书的总页数。

class Book:
    # ... 之前的代码 ...

    def __add__(self, other):
        total_pages = self.number_of_pages + other.number_of_pages
        return f"{total_pages} pages"

print(book2 + book3)  # 输出:395 pages

自定义成员检查:__contains__

我们可能想检查某个关键词(如“Lion”)是否出现在书的标题或作者名中。默认使用 in 操作符会报错。

# print(“Lion” in book3)  # 会引发 TypeError

定义 __contains__ 方法可以实现这个功能。

class Book:
    # ... 之前的代码 ...

    def __contains__(self, keyword):
        return keyword in self.title or keyword in self.author

print("Lion" in book3)   # 输出:True
print("Rowling" in book2) # 输出:True
print("Rowling" in book3) # 输出:False

自定义索引访问:__getitem__

我们可能希望通过类似字典键的方式访问对象的属性。默认情况下,对象不支持下标操作。

# print(book1[“title”])  # 会引发 TypeError

__getitem__ 方法允许我们自定义这种行为。

class Book:
    # ... 之前的代码 ...

    def __getitem__(self, key):
        if key == "title":
            return self.title
        elif key == "author":
            return self.author
        elif key == "pages":
            return self.number_of_pages
        else:
            return f"Key '{key}' was not found"

print(book1["title"])   # 输出:The Hobbit
print(book2["author"])  # 输出:J.K. Rowling
print(book3["pages"])   # 输出:172
print(book1["audio"])   # 输出:Key 'audio' was not found

总结

本节课中我们一起学习了Python的魔法方法。魔法方法是名称以双下划线包围的特殊方法,当对象参与Python的内置操作(如打印、比较、相加、使用 in[] 操作符)时,它们会被自动调用。通过定义这些方法,我们可以深度定制对象的行为,使代码更直观、更符合逻辑。掌握魔法方法是编写高级、优雅的Python面向对象代码的关键一步。

061:理解Python的property装饰器 🏗️

在本节课中,我们将要学习Python中的property装饰器。这个装饰器允许我们将一个方法定义为属性,从而可以像访问属性一样访问它。其主要好处在于,当我们读取、写入或删除属性时,可以添加额外的逻辑控制。

创建基础类

首先,我们创建一个Rectangle(矩形)类。这个类需要一个构造函数来初始化矩形的宽度和高度。

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

在上面的代码中,我们使用了下划线前缀(如_width)来命名属性。这是一种约定,表示这些属性是“受保护的”,建议仅在类内部使用,而不直接从外部访问。

使用Getter方法读取属性

上一节我们介绍了基础类的创建,本节中我们来看看如何使用property装饰器来创建getter方法。Getter方法允许我们在读取属性时执行额外的逻辑。

以下是创建getter方法的步骤:

  1. 为每个属性定义一个方法。
  2. 在该方法上方使用@property装饰器。
  3. 在方法内部返回处理后的属性值。

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    @property
    def width(self):
        # 添加逻辑:返回带一位小数和单位的字符串
        return f"{self._width:.1f} centimeters"

    @property
    def height(self):
        return f"{self._height:.1f} centimeters"

现在,当我们访问rectangle.width时,实际上调用的是width()方法,它会返回格式化后的字符串,而不是原始的_width值。

使用Setter方法写入属性

我们已经学会了如何安全地读取属性,接下来看看如何安全地设置属性。Setter方法允许我们在给属性赋值时添加验证逻辑。

以下是创建setter方法的步骤:

  1. 首先确保已用@property定义了getter方法。
  2. 使用@属性名.setter装饰器来定义setter方法。
  3. 在方法内部对传入的值进行验证。

class Rectangle:
    # ... __init__ 和 getter 方法同上 ...

    @width.setter
    def width(self, new_width):
        if new_width > 0:
            self._width = new_width
        else:
            print("Width must be greater than 0")

    @height.setter
    def height(self, new_height):
        if new_height > 0:
            self._height = new_height
        else:
            print("Height must be greater than 0")

现在,当我们尝试设置rectangle.width = -5时,setter方法会拦截这个操作,检查值是否大于0,并打印错误信息,而内部的_width属性不会被修改。

使用Deleter方法删除属性

最后,我们来了解如何定义deleter方法。虽然在实际编程中不常用,但它允许我们在删除属性时执行清理操作。

以下是创建deleter方法的步骤:

  1. 使用@属性名.deleter装饰器。
  2. 定义同名方法,在方法内部执行删除操作。
class Rectangle:
    # ... __init__, getter, setter 方法同上 ...

    @width.deleter
    def width(self):
        del self._width
        print("Width has been deleted")

    @height.deleter
    def height(self):
        del self._height
        print("Height has been deleted")

当我们执行del rectangle.width时,会调用这个deleter方法,删除内部的_width属性并打印确认信息。

完整代码示例与运行

让我们将以上所有部分组合起来,看看完整的类是如何工作的。

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    @property
    def width(self):
        return f"{self._width:.1f} centimeters"

    @width.setter
    def width(self, new_width):
        if new_width > 0:
            self._width = new_width
        else:
            print("Width must be greater than 0")

    @width.deleter
    def width(self):
        del self._width
        print("Width has been deleted")

    @property
    def height(self):
        return f"{self._height:.1f} centimeters"

    @height.setter
    def height(self, new_height):
        if new_height > 0:
            self._height = new_height
        else:
            print("Height must be greater than 0")

    @height.deleter
    def height(self):
        del self._height
        print("Height has been deleted")

# 使用示例
rectangle = Rectangle(3, 4)
print(rectangle.width)   # 输出: 3.0 centimeters
print(rectangle.height)  # 输出: 4.0 centimeters

rectangle.width = 5      # 成功设置
print(rectangle.width)   # 输出: 5.0 centimeters

rectangle.height = -1    # 触发验证,打印: Height must be greater than 0
print(rectangle.height)  # 输出: 4.0 centimeters (值未改变)

del rectangle.width      # 打印: Width has been deleted
# 此时再访问 rectangle.width 会引发 AttributeError

总结

本节课中我们一起学习了Python的property装饰器。我们了解到:

  • @property 可以将一个方法变成“属性式”的getter。
  • @属性名.setter 可以创建setter方法,在赋值时添加验证逻辑。
  • @属性名.deleter 可以创建deleter方法,在删除属性时执行特定操作。

通过使用property,我们能够以更优雅和安全的方式控制对类属性的访问、修改和删除,这是封装(Encapsulation)原则的一个重要体现。

062:装饰器 🍦

在本节课中,我们将要学习Python中的装饰器。装饰器是一种强大的工具,它允许我们扩展一个函数的功能,而无需修改该函数本身的代码。我们将通过一个制作冰淇淋的生动例子来理解其核心概念和工作原理。

什么是装饰器?🤔

装饰器是一个函数,它能够扩展另一个函数的行为,而不修改那个基础函数

我们可以将基础函数作为参数传递给装饰器函数。例如,假设我们有一个获取冰淇淋的基础函数 get_ice_cream,你可以传入一个冰淇淋口味。有些人可能想在冰淇淋上加糖屑,而另一些人可能只想要普通的香草冰淇淋。我们可以通过使用装饰器来添加糖屑,从而扩展获取冰淇淋这个函数的行为,但我们可能不想改变基础函数本身,因为有些人不喜欢糖屑。

请这样理解装饰器:我们是在为基础函数添加一些东西,而不改变它

如何创建装饰器?🔨

让我们从基础函数开始创建装饰器。

首先,我们创建一个基础函数 get_ice_cream,目前它没有参数。这个函数只打印一条消息:“这是你的冰淇淋🍦”。

def get_ice_cream():
    print("这是你的冰淇淋🍦")

要调用这个函数,只需执行 get_ice_cream()

现在,我们来创建一个装饰器。装饰器本身也是一个函数。

我们定义一个名为 add_sprinkles 的装饰器函数。这个函数将接收一个参数,即我们要装饰的函数,我们将其简称为 func

在装饰器函数内部,我们需要定义一个名为 wrapper 的内部函数。目前它没有参数,稍后会设置。在这个 wrapper 函数内部,我们将调用接收到的那个函数(func)。最后,装饰器函数返回这个 wrapper 函数。

def add_sprinkles(func):
    def wrapper():
        func()
    return wrapper

这就是创建装饰器的基本形式。

如何应用装饰器?✨

要将装饰器应用到基础函数上,你需要在定义该函数之前,使用 @ 符号加上装饰器的名称。

所以,add_sprinkles 是装饰器,基础函数是 get_ice_cream

@add_sprinkles
def get_ice_cream():
    print("这是你的冰淇淋🍦")

目前,我们的装饰器还没有做任何额外的事情。当我们调用 get_ice_cream() 时,它仍然只打印“这是你的冰淇淋🍦”。

为了真正“添加糖屑”,我们需要在装饰器的 wrapper 函数中添加一些代码。例如,在调用基础函数之前,打印一条“你添加了糖屑🎉”的消息。

def add_sprinkles(func):
    def wrapper():
        print("你添加了糖屑🎉")
        func()
    return wrapper

现在,当我们调用 get_ice_cream() 时,输出将是:

你添加了糖屑🎉
这是你的冰淇淋🍦

我们装饰了基础函数 get_ice_cream,但没有修改它,只是扩展了它。

为什么需要 wrapper 函数?🛡️

你可能注意到,我们在装饰器内部定义了一个嵌套的 wrapper 函数。这是必要的。

如果我们去掉 wrapper 函数,直接在装饰器里调用 func(),那么一旦应用装饰器,函数就会立即被执行,而不是在我们真正调用 get_ice_cream 时才执行。

wrapper 函数的作用是将代码“包装”起来,确保装饰器添加的行为只在目标函数被调用时才会执行。

应用多个装饰器 🎨

你可以为一个基础函数应用多个装饰器。让我们再创建一个添加巧克力酱的装饰器。

以下是 add_fudge 装饰器的定义:

def add_fudge(func):
    def wrapper():
        print("你添加了巧克力酱🍫")
        func()
    return wrapper

现在,我们可以将两个装饰器都应用到 get_ice_cream 函数上。装饰器的应用顺序是从下往上(或从内到外)执行的。

@add_fudge
@add_sprinkles
def get_ice_cream():
    print("这是你的冰淇淋🍦")

调用 get_ice_cream() 将输出:

你添加了巧克力酱🍫
你添加了糖屑🎉
这是你的冰淇淋🍦

处理带参数的函数 🧩

如果基础函数接受参数怎么办?例如,我们的 get_ice_cream 函数可能需要接收一个口味参数。

我们需要修改基础函数和装饰器中的 wrapper 函数来接收参数。

首先,修改基础函数:

def get_ice_cream(flavor):
    print(f"这是你的{flavor}味冰淇淋🍦")

然后,我们需要修改装饰器中的 wrapper 函数,使其能够接收任意数量的位置参数和关键字参数。这通过 *args**kwargs 来实现。

更新后的 add_sprinkles 装饰器:

def add_sprinkles(func):
    def wrapper(*args, **kwargs):
        print("你添加了糖屑🎉")
        func(*args, **kwargs)
    return wrapper

同样地,更新 add_fudge 装饰器:

def add_fudge(func):
    def wrapper(*args, **kwargs):
        print("你添加了巧克力酱🍫")
        func(*args, **kwargs)
    return wrapper

现在,我们可以调用带口味的函数了:

@add_fudge
@add_sprinkles
def get_ice_cream(flavor):
    print(f"这是你的{flavor}味冰淇淋🍦")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/460272a5d085c620652be12f9c399d3e_67.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/460272a5d085c620652be12f9c399d3e_69.png)

get_ice_cream("香草")
# 输出:
# 你添加了巧克力酱🍫
# 你添加了糖屑🎉
# 这是你的香草味冰淇淋🍦

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/460272a5d085c620652be12f9c399d3e_71.png)

get_ice_cream("巧克力")
# 输出:
# 你添加了巧克力酱🍫
# 你添加了糖屑🎉
# 这是你的巧克力味冰淇淋🍦

总结 📚

本节课中我们一起学习了Python装饰器。

  • 装饰器是一个函数,它扩展另一个函数的行为,而不修改那个基础函数
  • 创建装饰器需要定义一个接收函数作为参数的函数,并在其内部定义一个 wrapper 函数来包装核心逻辑,最后返回这个 wrapper 函数。
  • 使用 @decorator_name 的语法将装饰器应用到函数上。
  • 你可以为一个函数应用多个装饰器,它们会按顺序执行。
  • 为了使装饰器能处理带参数的函数,需要在 wrapper 函数中使用 *args**kwargs 来接收和传递所有参数。

装饰器是Python中非常强大和优雅的特性,广泛应用于日志记录、权限检查、性能测试等场景,能够帮助你写出更清晰、更模块化的代码。

Python超全入门教程:P63:Lambda函数详解 🐍

在本节课中,我们将要学习Python中的Lambda函数。Lambda函数是一种小型、匿名的函数,通常用于一次性操作。我们将了解其语法、用途以及如何在实际编程中应用它们。


什么是Lambda函数?

Lambda函数是一种小型、匿名的函数,通常用于一次性操作。它们可以接受任意数量的参数,但只能包含一个表达式。

Lambda函数的主要优点包括:

  • 保持命名空间清洁:由于它们是匿名的,我们无需为仅使用一次的函数费心起名。
  • 与高阶函数配合使用:它们常与sortmapfilterreduce等高阶函数结合使用,这将在后续课程中详细讨论。

Lambda函数的基本语法如下:

lambda 参数: 表达式

你可能会在map等高阶函数中看到这种写法。不过,本节课我们主要聚焦于Lambda函数本身的语法。


Lambda函数语法详解

上一节我们介绍了Lambda函数的基本概念,本节中我们来看看如何具体定义和使用它。

虽然可以将Lambda函数赋值给一个变量,但这并非其主要用途。它们更常见于高阶函数内部。不过,为了演示语法,我们先从赋值给变量开始。

以下是一个将数字翻倍的Lambda函数示例:

double = lambda x: x * 2

我们创建了一个名为double的变量,并将一个Lambda函数赋值给它。这个函数接受一个参数x,并返回x * 2的结果。

现在,double变量包含了一个函数,我们可以像调用普通函数一样调用它。

print(double(2))  # 输出:4
print(double(3))  # 输出:6
print(double(4))  # 输出:8


多参数Lambda函数

Lambda函数可以接受多个参数。接下来,我们创建一个将两个数字相加的Lambda函数。

以下是实现两个数字相加的Lambda函数:

add = lambda x, y: x + y

这个函数接受两个参数xy,并返回它们的和。

我们可以这样调用它:

print(add(2, 3))  # 输出:5

使用条件表达式

我们也可以在Lambda函数的表达式中使用条件逻辑。让我们编写一个找出两个数中较大值的函数。

以下是找出两个数中较大值的Lambda函数:

max_value = lambda x, y: x if x > y else y

这个函数比较xy,如果x大于y则返回x,否则返回y

调用示例:

print(max_value(4, 5))  # 输出:5
print(max_value(6, 5))  # 输出:6

类似地,我们可以找出较小值:

min_value = lambda x, y: x if x < y else y
print(min_value(6, 7))  # 输出:6
print(min_value(8, 7))  # 输出:7


字符串操作与逻辑判断

Lambda函数同样适用于字符串操作和更复杂的逻辑判断。

1. 拼接字符串
以下Lambda函数用于拼接姓和名:

full_name = lambda first, last: first + " " + last
print(full_name("Spongebob", "Squarepants"))  # 输出:Spongebob Squarepants

2. 判断数字是否为偶数
以下Lambda函数检查一个数字是否为偶数:

is_even = lambda x: x % 2 == 0
print(is_even(4))  # 输出:True
print(is_even(5))  # 输出:False

这里使用了取模运算符%x % 2 == 0表示x除以2的余数为0,即为偶数。

3. 年龄验证
以下Lambda函数用于进行简单的年龄验证:

age_check = lambda age: True if age >= 18 else False
print(age_check(21))  # 输出:True
print(age_check(12))  # 输出:False

总结

本节课中我们一起学习了Python中的Lambda函数。

我们了解到,Lambda函数是小型、匿名的函数,主要用于一次性操作。其核心语法是lambda 参数: 表达式。它们可以接受任意数量的参数,但只能包含一个表达式。

使用Lambda函数有助于保持代码命名空间的清洁,因为我们无需为临时函数命名。它们在高阶函数(如sortmapfilterreduce)中尤其有用,这将是后续课程的重点。

通过多个示例,我们练习了如何使用Lambda函数进行数学运算、字符串拼接以及逻辑判断。

Python超全入门教程:P64:map()函数详解 🗺️

在本节课中,我们将要学习Python中的map()函数。map()函数是一个强大的内置函数,它能够将一个指定的函数应用到集合(如列表)中的每一个元素上,并返回一个包含所有结果的新迭代器。我们将通过一个将摄氏温度转换为华氏温度的例子来详细讲解其用法。


概述

map()函数的核心作用是将一个函数映射到一个可迭代对象的所有元素上。它至少需要两个参数:一个函数和一个可迭代对象(如列表)。函数会被应用到可迭代对象的每个元素上。

其基本语法可以表示为:

map(function, iterable, ...)


准备数据:创建摄氏温度列表

首先,我们需要一个待处理的数据集合。这里我们创建一个包含多个摄氏温度的列表。

celsius_temps = [0, 10, 20, 30, 40]
print(celsius_temps)

方法一:使用预定义函数

上一节我们创建了数据,本节中我们来看看如何使用map()函数进行转换。首先,我们需要定义一个转换函数。

以下是转换函数celsius_to_fahrenheit的定义,它实现了摄氏温度到华氏温度的转换公式:F = C * 9/5 + 32

def celsius_to_fahrenheit(temp):
    return temp * 9 / 5 + 32

定义好函数后,我们就可以将其与温度列表一起传入map()函数。

fahrenheit_temps_map = map(celsius_to_fahrenheit, celsius_temps)

map()函数返回的是一个map对象,它是一个迭代器。我们可以直接遍历它。

以下是遍历map对象并打印结果的代码:

for temp in fahrenheit_temps_map:
    print(temp)

如果你希望直接得到一个列表,可以使用list()函数将map对象转换。

fahrenheit_temps_list = list(map(celsius_to_fahrenheit, celsius_temps))
print(fahrenheit_temps_list)


方法二:使用Lambda函数

除了预定义函数,我们还可以使用更简洁的Lambda(匿名)函数作为map()的参数,这样可以避免为简单的操作单独命名函数。

Lambda函数的语法是:lambda 参数: 表达式

以下是使用Lambda函数实现同样转换的代码:

fahrenheit_temps_lambda = list(map(lambda temp: temp * 9 / 5 + 32, celsius_temps))
print(fahrenheit_temps_lambda)

这种方法代码更加紧凑,特别适合只使用一次的小型操作。


总结

本节课中我们一起学习了Python的map()函数。

  • map()函数用于将指定函数应用于可迭代对象的每个元素。
  • 它接受一个函数和一个可迭代对象作为主要参数。
  • 其返回值是一个map对象迭代器,可以遍历或转换为列表。
  • 我们可以传递预定义的函数名,也可以直接使用Lambda匿名函数来实现映射操作。

通过将摄氏温度列表转换为华氏温度的实际例子,你应该已经掌握了map()函数的基本用法。它是一个用于数据批量处理的实用工具。

Python入门教程:P65:filter()函数详解 🎯

在本节课中,我们将学习Python中的filter()函数。filter()函数用于从可迭代对象中筛选出满足特定条件的元素,并返回一个迭代器。我们将通过具体示例来理解其工作原理和使用方法。


什么是filter()函数?

filter()函数返回所有通过某个条件测试的元素。它接受一个函数和一个可迭代对象作为参数。在下面的示例中,我们将创建一个学生成绩列表,并筛选出及格(60分及以上)的成绩。


使用自定义函数筛选

首先,我们声明一个自定义函数来检查成绩是否及格。以下是具体步骤:

  1. 定义一个名为is_passing的函数,它接受一个成绩作为参数。
  2. 在函数内部,检查成绩是否大于或等于60。
  3. 如果条件成立,返回True;否则返回False

以下是代码示例:

grades = [91, 83, 75, 67, 45, 52, 60]

def is_passing(grade):
    return grade >= 60

passing_grades = filter(is_passing, grades)

如果直接打印passing_grades,会得到一个filter对象。为了查看具体元素,我们可以遍历它:

for grade in passing_grades:
    print(grade)

输出结果为:91, 83, 75, 67, 60。所有成绩均大于或等于60分。

如果需要将结果转换为列表,可以使用list()函数进行类型转换:

passing_grades_list = list(filter(is_passing, grades))
print(passing_grades_list)

输出结果为:[91, 83, 75, 67, 60]。


使用Lambda函数简化代码

除了自定义函数,我们还可以使用Lambda函数来简化代码。Lambda函数是一种匿名函数,适用于简单的条件检查。

以下是使用Lambda函数的示例:

passing_grades = filter(lambda grade: grade >= 60, grades)
print(list(passing_grades))

输出结果与之前相同:[91, 83, 75, 67, 60]。使用Lambda函数可以避免为简单函数命名,保持代码简洁。


filter()函数与列表推导式的比较

虽然filter()函数功能强大,但列表推导式是另一种实现相同功能的简洁方法。以下是使用列表推导式筛选及格成绩的示例:

passing_grades = [grade for grade in grades if grade >= 60]
print(passing_grades)

输出结果同样为:[91, 83, 75, 67, 60]。列表推导式通常更直观,但了解filter()函数的使用场景仍然很重要。


总结

本节课中,我们一起学习了Python中的filter()函数。我们了解了如何通过自定义函数或Lambda函数来筛选可迭代对象中的元素,并将结果转换为列表。此外,我们还比较了filter()函数与列表推导式的异同。掌握这两种方法将帮助你在不同场景下灵活处理数据筛选任务。

066:reduce()函数详解 🧮

在本节课中,我们将要学习Python中的reduce()函数。这个函数用于将一个集合(如列表)中的所有元素“缩减”为单个值,例如计算总和。虽然在实际编程中,for循环通常是更常见的选择,但了解reduce()函数有助于你理解函数式编程的思路,并在阅读他人代码时能够识别它。

概述与导入

reduce()函数并非Python内置函数,要使用它,需要从functools模块中导入。

from functools import reduce

基本用法

reduce()函数的基本工作原理是:接收一个函数和一个集合作为参数。该函数必须接受两个参数。reduce()会使用该函数对集合中的前两个元素进行计算,然后将结果与第三个元素进行计算,依此类推,直到遍历完整个集合,最终得到一个单一的结果。

上一节我们介绍了reduce()的基本概念,本节中我们来看看它的具体应用。

示例:计算价格总和

假设我们有一个包含多个价格的列表,目标是计算所有价格的总和。

首先,我们创建一个价格列表:

prices = [10.99, 4.99, 7.50, 12.75, 14.49]

接下来,我们使用reduce()函数来计算总和。我们需要定义一个函数来告诉reduce()如何组合两个元素。在这个例子中,就是简单的加法。

以下是使用reduce()的步骤:

  1. 定义一个加法函数。
  2. 将该函数和价格列表传递给reduce()
  3. 接收并打印结果。
from functools import reduce

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b675c5d3f46218ee5d9c19714456dffe_9.png)

prices = [10.99, 4.99, 7.50, 12.75, 14.49]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b675c5d3f46218ee5d9c19714456dffe_11.png)

def add(x, y):
    return x + y

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b675c5d3f46218ee5d9c19714456dffe_13.png)

total = reduce(add, prices)
print(f"总价为:${total:.2f}")

运行这段代码,输出结果为:总价为:$50.72

使用Lambda表达式简化代码

在Python中,对于这种简单的、只用一次的函数,我们通常使用lambda表达式来定义匿名函数,这样可以使代码更简洁。

以下是使用lambda表达式重写的代码:

from functools import reduce

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/b675c5d3f46218ee5d9c19714456dffe_17.png)

prices = [10.99, 4.99, 7.50, 12.75, 14.49]

total = reduce(lambda x, y: x + y, prices)
print(f"总价为:${total:.2f}")

这段代码的功能与之前完全相同。lambda x, y: x + y定义了一个匿名函数,它接受两个参数xy,并返回它们的和。

工作原理详解

reduce()函数的工作流程可以分解为以下几个步骤,以列表[10.99, 4.99, 7.50, 12.75, 14.49]为例:

  1. 取前两个元素:10.994.99,执行 add(10.99, 4.99),得到 15.98
  2. 将上一步的结果 15.98 与第三个元素 7.50 结合,执行 add(15.98, 7.50),得到 23.48
  3. 将结果 23.48 与第四个元素 12.75 结合,执行 add(23.48, 12.75),得到 36.23
  4. 最后,将结果 36.23 与第五个元素 14.49 结合,执行 add(36.23, 14.49),得到最终结果 50.72

这个过程就像是将列表“折叠”成了一个值。

reduce()for循环的对比

reduce()函数提供了一种函数式编程的解决方案。它的优点是意图明确——看到reduce就知道代码的目的是将集合归约为一个值,有时可读性更好。

然而,在大多数情况下,使用for循环来实现同样的功能可能对初学者更直观,也更容易调试。例如,计算总和的for循环版本:

prices = [10.99, 4.99, 7.50, 12.75, 14.49]
total = 0
for price in prices:
    total += price
print(f"总价为:${total:.2f}")

两种方法都可以达到目的。选择哪一种取决于个人习惯、团队规范以及具体的应用场景。了解reduce()函数有助于你成为一个更全面的Python程序员。

总结

本节课中我们一起学习了Python的reduce()函数。

  • 功能reduce()函数用于将一个集合中的所有元素通过指定的函数累积计算,最终缩减为一个单一的值。
  • 导入:使用前需要从functools模块导入:from functools import reduce
  • 语法reduce(function, iterable),其中function是一个接受两个参数的函数。
  • 常用写法:通常与lambda表达式结合使用,使代码更简洁,例如 reduce(lambda x, y: x + y, list)
  • 对比reduce()提供了函数式编程风格,而for循环是更命令式的风格。两者各有适用场景,掌握reduce()有助于理解更广泛的代码。

现在,你已经掌握了reduce()函数的基本用法,可以在需要将序列“折叠”成单一结果时考虑使用它了。

067:Python排序详解 🧑‍💻

在本节课中,我们将学习Python中对不同数据结构进行排序的方法。我们将涵盖列表、元组、字典和自定义对象,并详细解释每种情况下的排序技巧。

列表排序 📋

列表是Python中最基础的数据结构之一,对其进行排序也最为直接。Python为列表提供了内置的sort()方法。

以下是列表排序的步骤:

  1. 创建一个列表。
  2. 调用列表的sort()方法。
  3. 如需逆序,可传入参数reverse=True

代码示例:

# 对字符串列表排序
fruits = ['banana', 'orange', 'apple', 'coconut']
fruits.sort()
print(fruits)  # 输出: ['apple', 'banana', 'coconut', 'orange']

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_12.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_14.png)

# 对数字列表排序
numbers = [3, 1, 5, 2, 4]
numbers.sort()
print(numbers)  # 输出: [1, 2, 3, 4, 5]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_16.png)

# 逆序排序
fruits.sort(reverse=True)
print(fruits)  # 输出: ['orange', 'coconut', 'banana', 'apple']

sort()方法会直接修改原列表,使其元素按指定顺序排列。


上一节我们介绍了如何使用sort()方法对列表进行原地排序。本节中我们来看看如何对元组进行排序。

元组排序 📦

元组是不可变序列,因此它没有sort()方法。对元组排序需要使用内置的sorted()函数,该函数会返回一个新的排序后的列表。

以下是元组排序的步骤:

  1. 使用sorted()函数对元组进行排序,得到一个列表。
  2. 如需保留元组类型,可将sorted()的结果用tuple()函数转换。
  3. 使用reverse=True参数实现逆序。

代码示例:

fruits_tuple = ('banana', 'orange', 'apple', 'coconut')

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_31.png)

# 使用sorted函数排序,返回列表
sorted_list = sorted(fruits_tuple)
print(sorted_list)  # 输出: ['apple', 'banana', 'coconut', 'orange']

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_33.png)

# 将排序结果转换回元组
sorted_tuple = tuple(sorted(fruits_tuple))
print(sorted_tuple)  # 输出: ('apple', 'banana', 'coconut', 'orange')

# 逆序排序
reverse_sorted_tuple = tuple(sorted(fruits_tuple, reverse=True))
print(reverse_sorted_tuple)  # 输出: ('orange', 'coconut', 'banana', 'apple')

sorted()函数不会修改原始元组,而是返回一个新的排序后的序列。


了解了列表和元组的排序后,我们进入更复杂的部分。本节中我们来看看如何对字典进行排序。

字典排序 📖

字典由键值对组成,排序时需要指定是按还是按进行排序。字典本身是无序的(在Python 3.7之前),但我们可以通过sorted()函数获取排序后的视图或创建新的有序字典。

以下是字典排序的几种情况:

  1. 按键排序:使用sorted()函数对字典的键进行排序。
  2. 按值排序:使用sorted()函数并配合key参数,指定按值排序。
  3. 保留键值对:使用items()方法获取键值对元组,排序后再重构字典。

代码示例:

fruits_dict = {'banana': 105, 'orange': 73, 'apple': 72, 'coconut': 354}

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_41.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_43.png)

# 1. 按键排序(返回排序后的键列表)
sorted_keys = sorted(fruits_dict)
print(sorted_keys)  # 输出: ['apple', 'banana', 'coconut', 'orange']

# 2. 按键排序并保留键值对
sorted_dict_by_key = dict(sorted(fruits_dict.items()))
print(sorted_dict_by_key)
# 输出: {'apple': 72, 'banana': 105, 'coconut': 354, 'orange': 73}

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_45.png)

# 3. 按键逆序排序
reverse_sorted_dict_by_key = dict(sorted(fruits_dict.items(), reverse=True))
print(reverse_sorted_dict_by_key)
# 输出: {'orange': 73, 'coconut': 354, 'banana': 105, 'apple': 72}

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_47.png)

# 4. 按值排序(升序)
sorted_dict_by_value = dict(sorted(fruits_dict.items(), key=lambda item: item[1]))
print(sorted_dict_by_value)
# 输出: {'apple': 72, 'orange': 73, 'banana': 105, 'coconut': 354}

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_49.png)

# 5. 按值逆序排序(降序)
reverse_sorted_dict_by_value = dict(sorted(fruits_dict.items(), key=lambda item: item[1], reverse=True))
print(reverse_sorted_dict_by_value)
# 输出: {'coconut': 354, 'banana': 105, 'orange': 73, 'apple': 72}

核心概念解释:

  • dict.items():返回一个包含字典所有(键, 值)元组的视图对象。
  • key=lambda item: item[1]:这是一个lambda函数,它告诉sorted()函数使用每个元组(即每个键值对)的第二个元素(索引1,即“值”)作为排序依据。item[0]代表键。


字典的排序涉及对键值对的操作。本节中我们将应用类似的lambda函数概念,来对自定义对象列表进行排序。

对象排序 🍎

当我们有一个由自定义类实例组成的列表时,排序需要指定依据对象的哪个属性。这同样通过sorted()函数的key参数配合lambda函数来实现。

以下是对象排序的步骤:

  1. 定义一个类,例如Fruit,包含namecalories属性。
  2. 创建该类的对象列表。
  3. 使用sorted()函数,并通过key参数指定排序依据的属性(如lambda fruit: fruit.name)。

代码示例:

class Fruit:
    def __init__(self, name, calories):
        self.name = name
        self.calories = calories

    def __repr__(self):
        return f"{self.name}: {self.calories}"

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_61.png)

# 创建水果对象列表
fruits = [
    Fruit('banana', 105),
    Fruit('apple', 72),
    Fruit('orange', 73),
    Fruit('coconut', 354)
]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_63.png)

# 1. 按名称属性排序
sorted_by_name = sorted(fruits, key=lambda fruit: fruit.name)
print(sorted_by_name)
# 输出: [apple: 72, banana: 105, coconut: 354, orange: 73]

# 2. 按名称逆序排序
reverse_sorted_by_name = sorted(fruits, key=lambda fruit: fruit.name, reverse=True)
print(reverse_sorted_by_name)
# 输出: [orange: 73, coconut: 354, banana: 105, apple: 72]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_65.png)

# 3. 按热量属性排序
sorted_by_calories = sorted(fruits, key=lambda fruit: fruit.calories)
print(sorted_by_calories)
# 输出: [apple: 72, orange: 73, banana: 105, coconut: 354]

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_67.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/4819c5f43593a74f62af660809ee2da6_69.png)

# 4. 按热量逆序排序
reverse_sorted_by_calories = sorted(fruits, key=lambda fruit: fruit.calories, reverse=True)
print(reverse_sorted_by_calories)
# 输出: [coconut: 354, banana: 105, orange: 73, apple: 72]

核心概念解释:

  • __repr__方法:定义对象的官方字符串表示形式,便于打印查看。
  • key=lambda fruit: fruit.calorieslambda函数从每个Fruit对象中提取calories属性作为排序键。


总结 📝

本节课中我们一起学习了Python中多种数据结构的排序方法:

  • 列表:使用内置的list.sort()方法进行原地排序。
  • 元组:使用sorted()函数生成新的排序列表,可转换为元组。
  • 字典:使用sorted()函数配合items()方法和key参数,可以按键或按值排序,并重构字典。
  • 自定义对象:使用sorted()函数配合key参数和lambda函数,指定依据对象的某个属性进行排序。

对于字典和对象的排序,关键在于理解并使用key=lambda x: ...来指定排序所依据的元素或属性。掌握这些技巧,你就能灵活应对Python中的各种排序需求。

068:递归入门 🧠

在本节课中,我们将要学习一个重要的编程概念——递归。我们将通过简单的例子,对比迭代与递归两种方法,帮助你理解递归的工作原理、优点和需要注意的问题。

什么是递归?

递归可以被看作是一个在自身内部调用自己的函数。它的核心思想是将一个复杂问题分解成一系列可重复的基本步骤。这些步骤既可以用循环(如 forwhile 循环)迭代解决,也可以用我们即将讨论的递归方法解决。

迭代与递归的对比:以“行走”为例

为了更好地理解,我们以一个“行走”的任务为例。行走可以被分解为重复执行“迈出一步”这个基本动作。

以下是实现“行走”功能的两种方法。

迭代方法

在迭代方法中,我们在函数内部使用循环来重复执行动作。

def walk_iterative(steps):
    for step in range(1, steps + 1):
        print(f"You take step number {step}.")

# 调用函数,行走100步
walk_iterative(100)

这段代码通过一个 for 循环,将“迈出一步”的动作重复了100次,从而模拟了行走的过程。这是一种直观的迭代解决方案。

递归方法

现在,让我们看看如何用递归解决同样的问题。递归的关键在于函数在内部调用自身。

def walk_recursive(steps):
    if steps == 0:  # 基线条件:何时停止
        return
    walk_recursive(steps - 1)  # 函数调用自身
    print(f"You take step number {steps}.")

# 调用函数
walk_recursive(100)

代码解析

  1. 基线条件if steps == 0: return。这是递归的停止条件,防止函数无限调用自身。
  2. 递归调用walk_recursive(steps - 1)。函数用更小的参数(steps - 1)调用自己,逐步逼近基线条件。
  3. 打印步骤:在递归调用之后打印,实现了从1到100的计数。

递归的工作原理与调用栈
每次函数调用自身时,都会在内存的“调用栈”上添加一个“帧”。程序会从最新的帧(栈顶)开始执行,直到满足基线条件,然后依次返回并解决之前的帧。如果递归层次过深(例如尝试 walk_recursive(1000)),可能会超出调用栈的最大深度限制,引发 RecursionError

另一个例子:计算阶乘

阶乘是另一个展示递归简洁性的经典例子。n 的阶乘(记作 n!)是所有小于及等于 n 的正整数的积。

迭代方法计算阶乘

def factorial_iterative(x):
    result = 1
    for y in range(1, x + 1):
        result *= y
    return result

print(factorial_iterative(10))  # 输出:3628800

递归方法计算阶乘

def factorial_recursive(x):
    if x == 1:  # 基线条件
        return 1
    else:
        return x * factorial_recursive(x - 1)  # 递归调用

print(factorial_recursive(10))  # 输出:3628800

公式表示
factorial(n) = n * factorial(n-1),且 factorial(1) = 1

对于阶乘这类问题,递归的代码通常比迭代更简洁、更直观地反映了问题的数学定义。

如何选择:迭代 vs. 递归

以下是选择时需要考虑的几个要点:

  • 性能:递归通常比迭代慢,因为涉及多次函数调用和调用栈操作。
  • 可读性:对于某些问题(如遍历树形结构、分治算法),递归的代码更简洁、更易于理解。
  • 栈溢出风险:递归深度过大可能导致 RecursionError
  • 问题本质:如果问题天然具有自相似性(“重复相同的模式”),递归可能是更自然的解决方案。

总结

本节课中我们一起学习了递归的核心概念。我们了解到:

  1. 递归是函数在内部调用自身的一种技术。
  2. 递归必须包含一个基线条件,以确保递归能够停止。
  3. 递归通过调用栈来管理函数调用,深度过大会导致栈溢出。
  4. 递归能将复杂问题分解为重复的基本步骤,虽然可能比迭代慢,但代码通常更简洁优雅。
  5. 在编程中,应根据问题的性质、性能要求和代码可读性,在迭代和递归之间做出合适的选择。

掌握递归思维,将为学习更复杂的数据结构(如树、图)和算法(如快速排序、深度优先搜索)打下坚实的基础。

069:异常处理 🚨

在本节课中,我们将要学习Python中的异常处理。异常是程序运行过程中可能遇到的错误或意外情况,它会中断程序的正常流程。通过学习如何处理异常,我们可以让程序更加健壮和友好。

什么是异常?

异常是一个中断程序正常流程的事件。当Python解释器遇到无法处理的错误时,它会创建一个异常对象并“抛出”它。如果这个异常没有被捕获和处理,程序就会崩溃并停止运行。

常见的异常类型

以下是几种常见的Python异常类型:

  • ZeroDivisionError:当你尝试将一个数字除以零时触发。例如,执行 1 / 0 会引发此异常。
  • TypeError:当你尝试对一个错误数据类型的值执行操作时触发。例如,执行 1 + “one” 会引发此异常。
  • ValueError:当你尝试将一个错误数据类型的值进行类型转换时触发。例如,执行 int(“pizza”) 会引发此异常,因为“pizza”无法转换为整数。

如何处理异常:tryexceptfinally

上一节我们介绍了异常的概念,本节中我们来看看如何优雅地处理它们。Python使用 tryexceptfinally 代码块来捕获和处理异常。

1. try

try 块用于包裹可能引发异常的“危险”代码。例如,任何接收用户输入的代码都应放在 try 块中,因为用户可能输入任何内容。

try:
    number = int(input(“请输入一个数字:”))
    result = 1 / number
    print(f“1 除以 {number} 的结果是 {result}”)

2. except

except 块紧随 try 块之后。如果 try 块中的代码引发了异常,程序会立即跳转到对应的 except 块执行,而不会让程序崩溃。

以下是处理特定异常的方法:

try:
    number = int(input(“请输入一个数字:”))
    result = 1 / number
    print(f“1 除以 {number} 的结果是 {result}”)
except ZeroDivisionError:
    print(“不能除以零。”)
except ValueError:
    print(“请输入有效的数字。”)

注意:虽然可以捕获所有异常(except Exception:),但这通常被视为不好的做法,因为它过于宽泛,无法向用户提供具体的错误信息。最佳实践是尽可能捕获并处理特定的异常。

3. finally

finally 块是可选的,但它一旦被定义,其中的代码无论是否发生异常都会执行。它通常用于执行一些清理工作,例如关闭文件或释放资源。

try:
    number = int(input(“请输入一个数字:”))
    result = 1 / number
    print(f“1 除以 {number} 的结果是 {result}”)
except ZeroDivisionError:
    print(“不能除以零。”)
except ValueError:
    print(“请输入有效的数字。”)
finally:
    print(“程序执行完毕,进行清理工作。”)

总结

本节课中我们一起学习了Python异常处理的核心知识。我们了解到异常是程序运行时的错误事件,并掌握了使用 tryexceptfinally 代码块来捕获和处理异常的方法。记住,良好的异常处理能让你的程序更稳定,并为用户提供更清晰的反馈。你可以查阅官方Python文档以获取更详尽的异常类型列表。

070:Python文件检测

在本节课中,我们将学习如何使用Python进行基本的文件检测。这是Python文件处理系列的第一个主题。在开始读写文件之前,我们首先需要掌握如何检测文件是否存在以及判断其类型。

我们将导入os模块,该模块为Python程序提供了与操作系统交互的功能。请确保在代码顶部导入此模块。

导入模块与准备工作

首先,我们需要导入os模块。

import os

为了演示,我在项目文件夹中创建了一个名为test.txt的纯文本文件。文件内容无关紧要,我们暂时不涉及文件读取。

使用相对路径检测文件

我们可以使用相对文件路径或绝对文件路径。首先介绍相对路径。如果目标文件与Python脚本位于同一目录,相对路径只需包含文件名和扩展名。

以下是检测文件是否存在的基本步骤。

fpath = "test.txt"
if os.path.exists(fpath):
    print(f"The location '{fpath}' exists.")
else:
    print(f"That location doesn't exist.")

运行上述代码,如果test.txt文件存在,将输出“The location 'test.txt' exists.”。如果文件扩展名错误或文件不存在,则会输出“That location doesn't exist.”。

如果文件位于子目录中,例如stuff文件夹内,则需要在路径中包含目录名。

fpath = "stuff/test.txt"

使用绝对路径检测文件

绝对路径指定了文件在计算机上的完整位置。例如,在Windows系统上,路径可能类似于C:\Users\Name\Desktop\test.txt

在Python字符串中,反斜杠\是转义字符。我们可以通过使用双反斜杠\\或正斜杠/来解决这个问题。

fpath = "C:/Users/Name/Desktop/test.txt"
# 或
fpath = "C:\\Users\\Name\\Desktop\\test.txt"

使用绝对路径检测文件的方法与相对路径相同。

判断路径是文件还是目录

检测到路径存在后,我们还可以进一步判断它是文件还是目录(文件夹)。

os.path模块提供了isfile()isdir()方法来实现这一功能。

以下是判断路径类型的完整示例。

fpath = "C:/Users/Name/Desktop/test.txt"

if os.path.exists(fpath):
    if os.path.isfile(fpath):
        print(f"That is a file.")
    elif os.path.isdir(fpath):
        print(f"That is a directory.")
else:
    print(f"That location doesn't exist.")

运行代码,如果路径指向一个文件,将输出“That is a file.”;如果指向一个目录,则输出“That is a directory.”。

核心方法总结

本节我们介绍了文件检测的核心方法,以下是它们的简要说明。

  • os.path.exists(path):检查指定路径是否存在,返回布尔值。
  • os.path.isfile(path):检查指定路径是否是一个文件。
  • os.path.isdir(path):检查指定路径是否是一个目录。

课程总结

本节课中,我们一起学习了Python基础文件检测。我们首先导入了os模块,然后学习了如何使用相对路径和绝对路径来检测文件或目录是否存在。最后,我们还掌握了如何区分一个路径指向的是文件还是目录。掌握这些基础知识是后续进行文件读写操作的重要前提。在接下来的课程中,我们将开始学习如何读取和写入文件。

071:使用Python写入文件 (.txt, .json, .csv) 📝

在本节课中,我们将学习如何使用Python将数据写入到不同类型的文件中。我们将涵盖三种最常用的文件格式:纯文本文件 (.txt)、JSON文件 (.json) 和 CSV文件 (.csv)。通过学习,你将能够将程序中的数据持久化保存到本地磁盘。


写入纯文本文件 (.txt) 📄

我们首先从最简单的纯文本文件开始。假设我们有一些文本数据需要保存到文件中。

以下是写入文本文件的基本步骤:

  1. 定义一个变量来存储文本数据。
  2. 创建一个文件路径,可以是相对路径或绝对路径。
  3. 使用 with open(...) as ... 语句打开文件。
  4. 使用文件对象的 .write() 方法写入数据。

让我们通过一个例子来实践。我们想保存一句话:“I like pizza”。

# 定义要保存的文本数据
text_data = "I like pizza"

# 定义文件路径(相对路径,文件将保存在与.py文件相同的目录下)
file_path = "output.txt"

# 使用 with 语句打开文件并写入
with open(file_path, "w") as file:
    file.write(text_data)

print(f"文本文件 '{file_path}' 已创建。")

运行这段代码后,你会在当前目录下看到一个名为 output.txt 的新文件,其内容就是 “I like pizza”。

理解代码细节

  • with 语句:这是一个上下文管理器。它确保文件在使用完毕后会被正确关闭,即使过程中发生错误也是如此。这是一种最佳实践,可以避免因未关闭文件而导致的问题。
  • open() 函数:用于打开文件。它接收两个主要参数:
    • file_path:文件的路径。
    • mode:打开文件的模式。"w" 表示写入模式。如果文件已存在,"w" 模式会覆盖原有内容。
  • 文件对象open() 函数返回一个文件对象(这里命名为 file)。我们可以调用这个对象的方法,例如 .write() 来写入内容。

文件路径:相对路径与绝对路径

在上面的例子中,我们使用了相对路径 "output.txt"。这意味着文件会创建在当前Python脚本所在的目录。

你也可以使用绝对路径,将文件保存到系统的任何位置,例如你的桌面。

# Windows 系统的绝对路径示例(使用双反斜杠或正斜杠)
file_path_absolute = "C:\\Users\\YourName\\Desktop\\output.txt"
# 或者
file_path_absolute = "C:/Users/YourName/Desktop/output.txt"

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_3.png)

with open(file_path_absolute, "w") as file:
    file.write(text_data)

文件打开模式

除了 "w"(写入)模式,还有其他几种有用的模式:

  • "x":独占创建模式。只有当文件不存在时才会创建并写入。如果文件已存在,则会引发 FileExistsError 错误。这可以防止意外覆盖重要文件。
  • "a":追加模式。如果文件存在,新内容会添加在文件末尾,而不是覆盖原有内容。

让我们看看如何使用 "x" 模式并处理可能出现的错误:

file_path = "output.txt"
try:
    with open(file_path, "x") as file:
        file.write("这是新内容。")
    print(f"文件 '{file_path}' 已创建。")
except FileExistsError:
    print(f"错误:文件 '{file_path}' 已存在,未执行写入操作。")

对于 "a"(追加)模式,我们可以这样使用:

file_path = "output.txt"
with open(file_path, "a") as file:
    file.write("\n这是追加的一行。")  # `\n` 是换行符

写入列表等集合数据

如果你想写入一个列表中的所有项目,不能直接将列表传递给 .write() 方法,因为它只接受字符串。你需要遍历列表,逐项写入。

employees = ["Eugene", "Squidward", "SpongeBob", "Patrick"]
file_path = "employees.txt"

with open(file_path, "w") as file:
    for employee in employees:
        file.write(employee + "\n")  # 在每个名字后添加换行符

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_9.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_11.png)

print(f"列表已写入文件 '{file_path}'。")

运行后,employees.txt 文件中的内容将是每个名字单独占一行。


上一节我们介绍了如何写入纯文本文件,本节中我们来看看如何写入结构更清晰的JSON文件。

写入JSON文件 (.json) 🗂️

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,使用“键-值对”来组织数据,非常适合存储结构化的信息,比如字典。

Python的 json 模块可以轻松地将字典等对象转换为JSON格式的字符串并写入文件。

以下是写入JSON文件的步骤:

  1. 导入 json 模块。
  2. 准备一个字典形式的数据。
  3. 使用 with open(...) as ... 语句打开文件(模式为 "w")。
  4. 使用 json.dump() 方法将字典写入文件。

让我们看一个例子,保存一个员工的详细信息:

import json

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_20.png)

# 准备要保存的字典数据
employee_data = {
    "name": "SpongeBob",
    "age": 30,
    "job": "cook"
}

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_22.png)

file_path = "employee.json"

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/859204f45fe5aa639c7358065f684022_24.png)

# 写入JSON文件
with open(file_path, "w") as file:
    json.dump(employee_data, file)

print(f"JSON文件 '{file_path}' 已创建。")

打开生成的 employee.json 文件,你会看到类似这样的内容:

{"name": "SpongeBob", "age": 30, "job": "cook"}

美化JSON输出

默认输出的JSON是紧凑格式,没有缩进。为了提高可读性,json.dump() 方法提供了一个 indent 参数。

with open(file_path, "w") as file:
    json.dump(employee_data, file, indent=4)  # 使用4个空格进行缩进

使用 indent=4 后,文件内容将变得层次分明:

{
    "name": "SpongeBob",
    "age": 30,
    "job": "cook"
}

我们已经学会了如何写入文本和JSON文件,最后我们来处理表格数据常用的CSV格式。

写入CSV文件 (.csv) 📊

CSV(Comma-Separated Values,逗号分隔值)文件通常用于存储表格数据,类似于Excel表格。每一行代表一条记录,每个字段由逗号分隔。

Python的 csv 模块专门用于读写CSV文件。

以下是写入CSV文件的步骤:

  1. 导入 csv 模块。
  2. 准备一个二维列表(列表的列表)作为数据。外层列表的每个元素代表一行,内层列表的元素代表该行的各列。
  3. 使用 with open(...) as ... 语句打开文件(模式为 "w",并建议设置 newline='' 以避免多余空行)。
  4. 创建一个 csv.writer 对象。
  5. 使用 writer.writerows() 方法一次性写入所有行,或使用 writer.writerow() 方法逐行写入。

让我们创建一个包含员工信息的表格:

import csv

# 准备二维列表数据:第一行是表头,后面是数据行
employees_2d = [
    ["name", "age", "job"],        # 表头
    ["SpongeBob", 30, "cook"],     # 第一行数据
    ["Patrick", 37, "unemployed"], # 第二行数据
    ["Sandy", 27, "scientist"]     # 第三行数据
]

file_path = "employees.csv"

# 写入CSV文件。设置 newline='' 防止在Windows系统下出现多余空行
with open(file_path, "w", newline='') as file:
    writer = csv.writer(file)
    writer.writerows(employees_2d)  # 一次性写入所有行

print(f"CSV文件 '{file_path}' 已创建。")

运行代码后,你会得到一个 employees.csv 文件。用文本编辑器打开,内容如下:

name,age,job
SpongeBob,30,cook
Patrick,37,unemployed
Sandy,27,scientist

如果用Excel或Numbers等电子表格软件打开,它会自动识别为表格。

逐行写入

除了 writerows(),你也可以使用 writerow() 来逐行写入,这在动态生成数据时很有用。

with open(file_path, "w", newline='') as file:
    writer = csv.writer(file)
    for row in employees_2d:
        writer.writerow(row)


总结 🎯

本节课中我们一起学习了使用Python写入三种常见文件格式的方法:

  1. 纯文本文件 (.txt):使用 open(file_path, “w”) 和文件对象的 .write() 方法。适用于保存简单的字符串或日志。
  2. JSON文件 (.json):使用 json 模块的 json.dump(data, file) 方法。非常适合保存字典等结构化的“键-值对”数据,并可利用 indent 参数美化输出。
  3. CSV文件 (.csv):使用 csv 模块的 csv.writer 对象及其 .writerows().writerow() 方法。是存储和交换表格数据的标准格式。

关键点在于理解不同的文件格式对应不同的数据结构和Python模块。通过 with 语句管理文件操作,可以确保资源被正确释放,是编写健壮代码的好习惯。现在,你可以尝试将程序中的数据输出到文件,实现数据的持久化存储了。

072:使用Python读取文件(.txt, .json, .csv)📁

在本节课中,我们将学习如何使用Python读取三种常见格式的文件:纯文本文件(.txt)、JSON文件(.json)和CSV文件(.csv)。我们将从文件路径的指定开始,逐步讲解读取每种文件的具体方法,并学习如何处理可能出现的异常。


概述

在之前的课程中,我们已经创建了一些示例文件用于练习。本节课的目标是掌握读取这些文件内容的核心技巧。我们将使用Python内置的open()函数以及jsoncsv模块来完成这项任务。


准备工作:指定文件路径

首先,我们需要告诉Python要读取哪个文件。这通过指定文件路径来实现。文件路径可以是相对路径,也可以是绝对路径。为了清晰起见,这里我们使用绝对路径。

在Windows系统中,复制文件路径时得到的是反斜杠\。在Python字符串中,反斜杠是转义字符,因此我们需要将其替换为双反斜杠\\或正斜杠/

以下是获取并设置文本文件路径的示例:

file_path = "C:/Users/YourName/Desktop/input.txt"

读取纯文本文件 (.txt)

上一节我们介绍了如何指定文件路径,本节中我们来看看如何安全地打开并读取一个纯文本文件。

读取文件时,使用with语句是一种最佳实践。with语句是一个上下文管理器,它会自动处理文件的打开和关闭,确保资源被正确释放,避免因未关闭文件而导致程序出现意外行为。

open()函数接受两个主要参数:文件路径和模式。要读取文件,模式应设置为'r'(代表read,读取)。

以下是读取文本文件的完整代码示例:

try:
    with open(file_path, 'r') as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print("文件未找到。")
except PermissionError:
    print("您没有读取该文件的权限。")

代码解释:

  1. with open(...) as file::打开文件并将返回的文件对象赋值给变量file
  2. content = file.read():调用文件对象的.read()方法,将文件的全部内容作为一个字符串读取到变量content中。
  3. try...except:用于捕获并处理可能出现的FileNotFoundError(文件未找到)和PermissionError(权限不足)异常,使程序更加健壮。

执行上述代码后,如果文件存在且可读,控制台将输出文件内容,例如:“I like pizza. It's really good.”


读取JSON文件 (.json)

读取JSON文件与读取文本文件略有不同,因为我们需要将JSON字符串解析为Python能够操作的数据结构(如字典或列表)。为此,我们需要导入Python内置的json模块。

以下是读取JSON文件的步骤:

首先,导入json模块并指定JSON文件的路径。

import json

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/8f60d14447ea4c81899e9ca66fb585e8_16.png)

json_file_path = "C:/Users/YourName/Desktop/input.json"

然后,使用with语句打开文件,并利用json.load()方法加载文件内容。

try:
    with open(json_file_path, 'r') as file:
        content = json.load(file)
        print(content)
except FileNotFoundError:
    print("JSON文件未找到。")
except PermissionError:
    print("您没有读取该JSON文件的权限。")

json.load(file)方法会读取文件内容,并自动将其解析为Python字典或列表。之后,你就可以像操作普通Python字典一样访问其中的数据了。

例如,如果input.json文件内容如下:

{"name": "Spongebob", "age": 30, "job": "Cook"}

你可以通过键来访问对应的值:

print(content['name'])  # 输出: Spongebob
print(content['age'])   # 输出: 30
print(content['job'])   # 输出: Cook

读取CSV文件 (.csv)

CSV(逗号分隔值)文件通常用于存储表格数据。要读取CSV文件,我们需要导入Python内置的csv模块。

以下是读取CSV文件的核心步骤:

首先,导入csv模块并指定CSV文件的路径。

import csv

csv_file_path = "C:/Users/YourName/Desktop/input.csv"

然后,使用with语句打开文件,并利用csv.reader()方法创建一个读取器对象。

try:
    with open(csv_file_path, 'r') as file:
        csv_reader = csv.reader(file)
        for row in csv_reader:
            print(row)
except FileNotFoundError:
    print("CSV文件未找到。")
except PermissionError:
    print("您没有读取该CSV文件的权限。")

代码解释:

  1. csv.reader(file):创建一个CSV读取器对象csv_reader
  2. for row in csv_reader::遍历读取器对象。每次迭代,row变量都是一个列表,代表CSV文件中的一行数据,列表中的每个元素对应该行的一个单元格。

假设input.csv内容如下:

name,age,job
Spongebob,30,Cook
Patrick,35,Unemployed
Sandy,27,Scientist

上述代码将逐行输出:

['name', 'age', 'job']
['Spongebob', '30', 'Cook']
['Patrick', '35', 'Unemployed']
['Sandy', '27', 'Scientist']

如果需要获取特定列的数据,可以通过列表索引来访问每一行的元素。例如,要获取所有人的姓名(第一列),可以这样做:

for row in csv_reader:
    print(row[0])  # 打印每行的第一个元素(姓名列)

总结

本节课中我们一起学习了使用Python读取三种常见文件格式的方法:

  1. 读取.txt文件:使用open(file_path, 'r').read()方法,并结合try-except块处理异常。
  2. 读取.json文件:导入json模块,使用json.load()方法将文件内容解析为Python数据结构。
  3. 读取.csv文件:导入csv模块,使用csv.reader()创建读取器对象,并通过循环遍历来获取每一行数据。

掌握这些基础的文件读取操作,是进行数据处理、分析和自动化任务的重要第一步。

073:如何测量Python代码的执行时间 ⏱️

在本节课中,我们将学习如何在Python中测量代码的执行时间。这对于性能分析、优化代码以及比较不同算法或实现方式的效率至关重要。我们将通过一个简单的示例,演示如何精确地计算一段代码从开始到结束所花费的时间。

概述

测量代码执行时间的核心思想是:在目标代码执行前记录一个时间点,在代码执行后再记录一个时间点,然后计算这两个时间点之间的差值。这个差值就是代码的执行时间。

我们将使用Python内置的 time 模块来完成这个任务,特别是其中的 perf_counter() 方法,它能提供高精度的时间测量。

导入所需模块

首先,我们需要导入 time 模块。

import time

测量执行时间的步骤

以下是测量代码执行时间的具体步骤。

1. 记录开始时间

在你要测量的代码块开始执行之前,使用 time.perf_counter() 方法记录一个时间点,并将其赋值给一个变量(例如 start_time)。

start_time = time.perf_counter()

time.perf_counter() 会返回一个任意的时间点,其值本身并不重要,重要的是它与后续时间点之间的差值。

2. 执行待测代码

接下来,放置你想要测量执行时间的代码。为了演示,我们将创建一个简单的循环。

# 你的代码放在这里
for i in range(1000000):
    pass

这个循环迭代一百万次,但循环体内不做任何操作(pass语句)。我们将测量执行这个空循环所需的时间。

3. 记录结束时间

在待测代码执行完毕后,再次调用 time.perf_counter() 方法,记录另一个时间点,并将其赋值给另一个变量(例如 end_time)。

end_time = time.perf_counter()

4. 计算并输出耗时

最后,计算结束时间与开始时间的差值,这个差值就是代码的执行时间(以秒为单位)。然后,我们可以将这个时间打印出来。

elapsed_time = end_time - start_time
print(f"执行耗时:{elapsed_time:.1f} 秒")

在上面的打印语句中,我们使用了格式化字符串(f-string),并通过 :.1f 指定了输出格式,即只显示小数点后一位数字,这样可以使输出结果更整洁。

完整代码示例

将以上所有步骤组合起来,就得到了一个完整的测量代码执行时间的程序。

import time

# 1. 记录开始时间
start_time = time.perf_counter()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c845f0f1ef028ec39c68f2f027bb94a0_40.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/c845f0f1ef028ec39c68f2f027bb94a0_41.png)

# 2. 执行待测代码
for i in range(1000000):
    pass

# 3. 记录结束时间
end_time = time.perf_counter()

# 4. 计算并输出耗时
elapsed_time = end_time - start_time
print(f"执行耗时:{elapsed_time:.1f} 秒")

运行这段代码,你将看到类似“执行耗时:0.0 秒”的输出。由于现代计算机速度很快,一百万次空循环的执行时间可能非常短,显示为0.0秒。你可以尝试增加循环次数(例如 range(10000000)range(100000000))来观察更明显的耗时变化。

总结

本节课中,我们一起学习了如何在Python中测量代码的执行时间。我们掌握了以下核心步骤:

  1. 使用 import time 导入时间模块。
  2. 在代码执行前,用 start_time = time.perf_counter() 记录开始时间。
  3. 执行你想要测量的代码块。
  4. 在代码执行后,用 end_time = time.perf_counter() 记录结束时间。
  5. 通过公式 elapsed_time = end_time - start_time 计算执行耗时,并打印结果。

这个方法简单有效,是进行基础性能分析和代码优化的实用工具。你可以将它应用到任何你想了解其运行效率的代码片段上。

075:用Python编写一个闹钟 🕰️

在本节课中,我们将学习如何使用Python创建一个功能完整的闹钟程序。我们将使用timedatetimepygame模块来实现时间获取、时间比较以及播放提示音的功能。


准备工作

上一节我们介绍了项目目标,本节中我们来看看实现这个闹钟需要哪些准备工作。

以下是实现闹钟所需的导入模块:

  • import time
    • 用于控制程序每秒更新一次。
  • import datetime
    • 用于处理时间的字符串表示形式。
  • import pygame
    • 用于播放闹钟提示音。

注意:你可能需要安装pygame包。安装方法是在终端中运行以下命令:

pip install pygame

设置闹钟时间

准备工作完成后,我们开始编写核心功能。首先,创建一个函数来设置闹钟。

我们定义一个set_alarm函数,它接收一个代表军用时间格式(HH:MM:SS)的字符串参数alarm_time

def set_alarm(alarm_time):
    pass

为了确保程序在直接运行时才执行设置闹钟的逻辑,我们使用以下判断语句:

if __name__ == "__main__":

在这个判断语句内,我们提示用户输入闹钟时间,并调用set_alarm函数。

    alarm_time = input("请输入闹钟时间 (格式:HH:MM:SS): ")
    set_alarm(alarm_time)

set_alarm函数内部,我们先打印一条设置成功的消息。

def set_alarm(alarm_time):
    print(f"闹钟已设置为:{alarm_time}")

获取并比较时间

设置好闹钟时间后,我们需要持续检查当前时间是否到了设定的闹钟时间。

set_alarm函数中,我们创建一个布尔变量is_running来控制主循环。

    is_running = True

然后,我们进入一个while循环。在循环中,我们使用datetime模块获取当前的时、分、秒。

    while is_running:
        current_time = datetime.datetime.now().strftime("%H:%M:%S")
        print(current_time)

获取当前时间后,我们将其与用户设定的alarm_time进行比较。如果两者相等,则触发闹钟。

        if current_time == alarm_time:
            print("时间到!该起床了!⏰")
            is_running = False

为了让时钟每秒更新一次,而不是疯狂循环,我们在每次循环结束时让程序暂停一秒。

        time.sleep(1)

播放提示音

当时间匹配时,除了打印信息,我们还需要播放声音来提醒用户。这就是pygame发挥作用的地方。

首先,你需要一个MP3格式的音频文件。你可以从YouTube音频库等资源中下载非商业用途的音效或音乐,并将其放在项目文件夹中。

假设音频文件名为my_music.mp3,我们在函数开头定义其路径。

    sound_file = "my_music.mp3"

在触发闹钟的if语句内部(即打印“该起床了”之后),我们初始化pygame的混音器并加载、播放音乐。

            pygame.mixer.init()
            pygame.mixer.music.load(sound_file)
            pygame.mixer.music.play()

默认情况下,程序会在播放声音后立即结束。为了让声音持续播放直到结束,我们添加一个循环,检查音乐是否仍在播放。

            while pygame.mixer.music.get_busy():
                time.sleep(1)

可选步骤:如果你运行程序时看到“Hello from the pygame community...”的输出,可以进入Python环境下的pygame包目录,找到__init__.py文件,注释掉或删除底部的相关打印语句来屏蔽它。


总结

本节课中我们一起学习了如何用Python构建一个简单的命令行闹钟。我们主要完成了以下工作:

  1. 使用datetime模块获取和格式化当前时间。
  2. 通过循环和time.sleep(1)实现时钟的每秒更新。
  3. 比较当前时间与用户输入的闹钟时间。
  4. 利用pygame.mixer模块加载并播放MP3音频文件作为闹铃。
  5. 通过检查pygame.mixer.music.get_busy()的状态让铃声持续播放。

这个项目涵盖了时间处理、用户输入、条件判断和外部库使用等多个基础概念,是一个很好的综合练习。你可以尝试为其添加更多功能,例如设置多个闹钟、选择不同的铃声或添加图形界面。

Python超全入门教程:P76:Python多线程入门 🧵

在本节课中,我们将要学习Python中的多线程编程。多线程是一种允许程序同时执行多个任务的技术,类似于我们日常生活中的“多任务处理”。我们将通过一个简单的家务模拟例子,来理解多线程的基本概念、创建方法以及如何管理线程。


什么是多线程?🤔

多线程用于并发执行多个任务。可以将其想象成我们在同时处理几件不同的事情。例如,一个人可以同时学习、听音乐和吃东西。

多线程特别适用于IO密集型任务。IO即输入/输出,例如读取文件或从API获取数据。这类任务通常需要一些时间来完成,并且我们无法精确预知其结束时间。

导入模块与基础概念

要使用多线程,我们需要导入Python的threading模块。

import threading

我们通过访问threading模块,然后调用Thread构造函数并传入一个目标函数来创建线程。

一个顺序执行的例子

为了演示,假设我们有一系列家务要做:遛狗、取邮件和倒垃圾。我们首先定义三个函数来处理这些家务。

import time

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/df30b99d4a6a270538b1500402a6549f_5.png)

def walk_dog():
    time.sleep(8)  # 模拟遛狗需要8秒
    print("You finish walking the dog.")

def take_out_trash():
    time.sleep(2)  # 模拟倒垃圾需要2秒
    print("You take out the trash.")

def get_mail():
    time.sleep(4)  # 模拟取邮件需要4秒
    print("You get the mail.")

现在,让我们按顺序调用这些函数,看看会发生什么。

walk_dog()
take_out_trash()
get_mail()

运行结果将是:程序会先等待8秒完成遛狗,然后花2秒倒垃圾,最后花4秒取邮件。这些函数都在同一个线程(主线程)上运行,因此它们必须按顺序一个接一个地完成。

引入多线程:并发执行

上一节我们看到了顺序执行的局限性。本节中,我们来看看如何使用多线程来同时执行所有任务。

我们可以为每个家务创建一个线程对象,并同时启动它们。

# 创建线程对象
chore1 = threading.Thread(target=walk_dog)
chore2 = threading.Thread(target=take_out_trash)
chore3 = threading.Thread(target=get_mail)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/df30b99d4a6a270538b1500402a6549f_12.png)

# 启动所有线程
chore1.start()
chore2.start()
chore3.start()

现在,这三个函数将并发执行。由于倒垃圾只需2秒,它会最先完成;取邮件需4秒,其次完成;遛狗需8秒,最后完成。任务的完成顺序与它们被调用的顺序无关,只取决于各自所需的执行时间。

使用 join() 方法等待线程完成

在上面的例子中,主线程在启动所有子线程后会立即继续执行。如果我们想在所有家务完成后打印一条确认信息,就需要让主线程等待所有子线程结束。

这时,我们可以使用线程对象的 join() 方法。

以下是使用 join() 方法的步骤:

chore1 = threading.Thread(target=walk_dog)
chore2 = threading.Thread(target=take_out_trash)
chore3 = threading.Thread(target=get_mail)

chore1.start()
chore2.start()
chore3.start()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/df30b99d4a6a270538b1500402a6549f_20.png)

# 等待所有线程完成
chore1.join()
chore2.join()
chore3.join()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/df30b99d4a6a270538b1500402a6549f_22.png)

print("All chores are complete.")

现在,print(“All chores are complete.”) 这行代码会等到所有线程(即所有家务)都完成后才执行。

向线程函数传递参数

有时,我们的目标函数需要接收参数。例如,walk_dog 函数可能需要知道狗的名字。

我们可以通过 Thread 构造函数的 args 关键字参数来传递参数。args 接收一个元组。

def walk_dog(first_name, last_name):
    time.sleep(8)
    print(f"You finish walking {first_name} {last_name}.")

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/df30b99d4a6a270538b1500402a6549f_30.png)

# 创建线程并传递参数
chore1 = threading.Thread(target=walk_dog, args=("Scooby", "Doo",))

重要提示:如果只有一个参数,必须在元组末尾加上逗号,例如 args=(“Scooby”,),以向Python表明这是一个元组,而不是一个普通的括号表达式。


总结

本节课中我们一起学习了Python多线程的基础知识。

  • 核心概念:多线程用于并发执行多个任务,尤其适合IO密集型操作。
  • 创建线程:通过 threading.Thread(target=function_name) 创建线程对象,并使用 start() 方法启动。
  • 管理线程:使用 join() 方法可以让主线程等待子线程完成后再继续执行。
  • 传递参数:通过 args 参数向目标函数传递参数,注意参数需要放在元组中。

通过模拟处理家务的例子,我们直观地看到了多线程如何提升程序效率,让多个任务能够同时进行。记住,多线程任务的完成顺序取决于各自任务的执行时间,而非启动顺序。

077:使用Python连接API获取数据 🐍

在本节课中,我们将学习如何使用Python连接到一个外部API,并从中获取数据。我们将以有趣的宝可梦API为例,演示如何查询特定宝可梦的信息,例如它的名字、ID、身高和体重。


第一步:导入必要的库

首先,我们需要导入requests库来发送HTTP请求。这个库不是Python标准库的一部分,所以可能需要先安装它。

import requests

如果运行上述代码时出现“ModuleNotFoundError: No module named 'requests'”的错误,说明你需要安装这个库。

以下是安装步骤:

  1. 打开你的终端(在PyCharm或VS Code中通常有内置终端)。
  2. 输入命令:pip install requests
  3. 等待安装完成。

安装成功后,再次运行导入语句就不会报错了。


第二步:理解API与构建请求URL

上一节我们介绍了如何准备开发环境,本节中我们来看看如何构建API请求。

我们将使用一个公开的宝可梦API。根据其文档,要查询一个宝可梦(例如皮卡丘),我们需要使用特定的URL格式。

基础URL是:https://pokeapi.co/api/v2/
要查询宝可梦,需要在后面加上pokemon/和宝可梦的名字。

因此,完整的请求URL格式为:

{基础URL}pokemon/{宝可梦名称}

例如,查询皮卡丘的URL就是:https://pokeapi.co/api/v2/pokemon/pikachu

为了方便,我们首先将基础URL存储在一个变量中。

base_url = "https://pokeapi.co/api/v2/"


第三步:创建函数并发送请求

接下来,我们创建一个函数来获取宝可梦信息。这个函数将接收一个宝可梦的名字作为参数。

def get_pokemon_info(pokemon_name):
    # 构建完整的请求URL
    url = f"{base_url}pokemon/{pokemon_name.lower()}"
    # 发送GET请求
    response = requests.get(url)
    return response

在函数外部,我们可以指定想查询的宝可梦,并调用这个函数。

my_pokemon = "Pikachu"
pokemon_response = get_pokemon_info(my_pokemon)

第四步:处理API响应

发送请求后,我们会收到一个响应对象。我们需要检查这个响应的状态码,以确保请求成功(例如,状态码200表示成功)。

以下是常见的HTTP状态码:

  • 200: 成功 (OK)
  • 404: 未找到 (Not Found)

我们在函数中添加逻辑来处理响应:

def get_pokemon_info(pokemon_name):
    url = f"{base_url}pokemon/{pokemon_name.lower()}"
    response = requests.get(url)

    if response.status_code == 200:
        # 请求成功,将JSON格式的响应内容转换为Python字典
        pokemon_data = response.json()
        return pokemon_data
    else:
        # 请求失败,打印错误信息
        print(f"Failed to retrieve data. Status code: {response.status_code}")
        return None

现在,当我们调用get_pokemon_info(“Pikachu”)时,如果成功,它将返回一个包含皮卡丘所有信息的字典。


第五步:提取并显示所需信息

成功获取数据字典后,我们就可以从中提取我们关心的具体信息了。

字典通过来访问对应的。从API返回的数据中,我们可以找到nameidheightweight等键。

# 获取宝可梦信息字典
pokemon_info = get_pokemon_info(“Pikachu”)

if pokemon_info: # 检查字典是否存在(即请求是否成功)
    name = pokemon_info[‘name‘].capitalize()
    id_num = pokemon_info[‘id‘]
    height = pokemon_info[‘height‘]
    weight = pokemon_info[‘weight‘]

    print(f“Name: {name}“)
    print(f“ID: {id_num}“)
    print(f“Height: {height}“)
    print(f“Weight: {weight}“)

运行代码,你将会看到皮卡丘的信息被打印出来。你可以尝试将“Pikachu”替换成其他宝可梦的名字(如“charizard”“bulbasaur”)来查询不同的数据。


总结 🎯

本节课中我们一起学习了使用Python连接API的核心步骤:

  1. 安装并导入 requests 库。
  2. 理解API文档,构建正确的请求URL。
  3. 发送GET请求 并获取响应对象。
  4. 检查HTTP状态码 以确认请求成功。
  5. 将成功的响应从JSON格式转换为Python字典
  6. 从字典中提取并处理我们需要的数据。

这就是使用Python从外部API获取数据的基本流程。你可以将这个方法应用到其他提供API的服务上,获取各种各样的数据。

078:使用PyQt5搭建基础GUI应用 🖥️

在本节课中,我们将学习如何使用PyQt5库创建一个基础的图形用户界面(GUI)应用程序。我们将从安装PyQt5开始,逐步构建一个带有自定义标题、尺寸和图标的基本窗口。


概述

PyQt5是一个强大的Python GUI框架,允许我们创建具有丰富功能的桌面应用程序。本节将引导你完成搭建第一个PyQt5窗口的所有必要步骤。


安装PyQt5包

首先,我们需要安装PyQt5包。我们将使用Python的包管理器Pip来完成这个任务。

以下是安装步骤:

  1. 打开终端(Pycharm或VS Code都内置了终端)。
  2. 输入命令:pip install PyQt5
  3. 等待下载和安装完成。

安装完成后,你的Python环境中的site-packages文件夹内将包含PyQt5包,我们可以通过导入来使用它。


导入必要的模块

要开始编写代码,我们需要导入一些必要的模块。

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
  • sys模块提供了对Python解释器使用的变量的访问。
  • PyQt5.QtWidgets模块中,我们导入QApplicationQMainWindowQMainWindow是创建主窗口的基础类。

创建主窗口类

上一节我们导入了必要的模块,本节中我们来看看如何创建自定义的主窗口。

我们将创建一个继承自QMainWindow的类,这允许我们定制自己的窗口。

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
  • 我们定义了一个名为MainWindow的类,它继承自QMainWindow
  • 在类的__init__构造函数中,我们调用super().__init__()来初始化父类。目前我们没有额外的参数需要传递。

编写应用程序启动代码

定义了窗口类之后,我们需要编写启动应用程序的代码。

以下是启动PyQt5应用程序的标准流程:

  1. 创建一个QApplication实例。
  2. 创建我们的MainWindow实例。
  3. 显示窗口。
  4. 启动应用程序的事件循环。
def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
  • QApplication(sys.argv)创建应用程序对象,sys.argv参数允许PyQt5处理可能存在的命令行参数。
  • MainWindow()创建我们的窗口对象。
  • window.show()方法将默认隐藏的窗口显示出来。
  • app.exec_()方法启动应用程序的事件循环,它会等待用户输入(如点击按钮、按键或关闭窗口)。
  • sys.exit(app.exec_())确保程序能够干净地退出。

自定义窗口属性

现在我们已经有了一个可以运行的基本窗口,接下来让我们对其进行一些自定义设置,比如标题、大小和位置。

MainWindow类的__init__方法中,我们可以添加以下代码:

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("我的第一个酷炫GUI")
        self.setGeometry(700, 300, 500, 500)
  • self.setWindowTitle("我的第一个酷炫GUI"):设置窗口的标题。
  • self.setGeometry(x, y, width, height):设置窗口的初始位置和大小。参数依次是:X坐标、Y坐标、宽度、高度。例如,(700, 300, 500, 500)表示窗口出现在屏幕坐标(700, 300)的位置,并且宽高均为500像素。

设置窗口图标

为了让我们的应用程序更具个性化,我们可以为窗口设置一个自定义图标。

首先,需要从PyQt5.QtGui模块导入QIcon类。

from PyQt5.QtGui import QIcon

然后,在MainWindow__init__方法中,在设置几何属性之后,添加设置图标的代码:

        self.setWindowIcon(QIcon("profile_p.jpg"))
  • self.setWindowIcon(QIcon("profile_p.jpg")):将当前目录下名为profile_p.jpg的图片设置为窗口图标。你需要将这里的文件名替换为你自己的图片文件名。

确保你的图片文件与Python脚本文件在同一个目录下,或者提供正确的相对或绝对路径。


总结

本节课中我们一起学习了如何使用PyQt5创建基础的GUI应用程序。我们完成了从安装PyQt5、导入模块、创建主窗口类、编写应用程序启动代码,到自定义窗口标题、大小、位置和图标的完整流程。你现在已经拥有了一个可以运行和显示的基本窗口,这是构建更复杂GUI应用的第一步。在接下来的课程中,我们将学习如何向这个窗口添加标签、按钮等更多控件。

079:PyQt5标签控件入门 🏷️

在本节课中,我们将学习如何使用PyQt5创建和自定义标签控件。标签是图形用户界面中用于显示文本或图像的基本元素。我们将从创建一个简单的标签开始,逐步学习如何设置其字体、大小、颜色、背景、样式以及对齐方式。


导入必要的模块

首先,我们需要导入创建标签所需的PyQt5模块。核心的标签类位于 QWidget 模块中。

from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel


创建主窗口和标签

在上一节中,我们导入了必要的模块。本节中,我们来看看如何创建主窗口并在其中添加一个标签。

我们将在主窗口的构造函数中创建标签。首先,声明一个 QLabel 对象,并传入要显示的文本作为第一个参数。第二个参数 self 表示当前的主窗口对象。

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 800, 600)  # 设置窗口位置和大小

        # 创建一个标签
        self.label = QLabel("Hello", self)

运行程序后,你会看到一个显示“Hello”的标签,但字体可能非常小。


设置标签字体

为了让标签文本更清晰,我们需要设置字体。这需要导入 QFont 类。

from PyQt5.QtGui import QFont

以下是设置标签字体的步骤:

  1. 调用标签的 setFont 方法。
  2. 在方法中创建一个 QFont 对象,指定字体名称和大小。
        # 设置字体
        font = QFont("Arial", 40)
        self.label.setFont(font)

设置后,标签文本将使用指定的字体和大小显示。


设置标签几何属性

接下来,我们调整标签在窗口中的位置和尺寸。使用 setGeometry 方法可以设置标签的X坐标、Y坐标、宽度和高度。

        # 设置标签的位置和大小 (x, y, width, height)
        self.label.setGeometry(0, 0, 500, 100)

现在,标签位于窗口左上角,宽度为500像素,高度为100像素。


使用样式表美化标签

PyQt5支持类似CSS的样式表,可以方便地设置控件的外观。我们将为标签添加颜色、背景等样式。

以下是可用的样式属性示例:

  • 颜色:使用 color 属性设置文本颜色,值可以是颜色名、RGB值或十六进制值。
  • 背景色:使用 background-color 属性设置背景颜色。
  • 字体粗细:使用 font-weight 属性设置字体为粗体。
  • 字体样式:使用 font-style 属性设置字体为斜体。
  • 文本装饰:使用 text-decoration 属性为文本添加下划线。
        # 设置样式表
        self.label.setStyleSheet("""
            color: #333333;                /* 深灰色文本 */
            background-color: #87CEEB;     /* 浅蓝色背景 */
            font-weight: bold;             /* 粗体 */
            font-style: italic;            /* 斜体 */
            text-decoration: underline;    /* 下划线 */
        """)

应用样式表后,标签将具有自定义的颜色和样式。


设置标签文本对齐方式

为了精确控制文本在标签区域内的位置,我们需要设置对齐方式。这需要导入 Qt 模块,它包含了对齐标志。

from PyQt5.QtCore import Qt

setAlignment 方法用于设置对齐方式。你可以使用 Qt 类中的标志来指定水平和垂直对齐。

以下是基本的对齐操作:

  • 垂直对齐
    • Qt.AlignTop:顶部对齐
    • Qt.AlignBottom:底部对齐
    • Qt.AlignVCenter:垂直居中(默认)
  • 水平对齐
    • Qt.AlignLeft:左对齐(默认)
    • Qt.AlignRight:右对齐
    • Qt.AlignHCenter:水平居中
        # 垂直顶部对齐
        self.label.setAlignment(Qt.AlignTop)
        # 水平居中对齐
        self.label.setAlignment(Qt.AlignHCenter)

要同时设置水平和垂直对齐,可以使用按位或运算符 | 来组合标志。

        # 同时设置水平居中和垂直顶部对齐
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)

此外,Qt.AlignCenter 是同时实现水平和垂直居中的快捷方式。

        # 文本在标签区域内完全居中(水平+垂直)
        self.label.setAlignment(Qt.AlignCenter)

课程总结 🎯

本节课中我们一起学习了PyQt5标签控件的基础知识。我们从创建最简单的文本标签开始,然后逐步学会了如何通过设置字体、调整几何尺寸、应用CSS-like样式表以及控制文本对齐方式来全方位地自定义标签的外观。掌握这些技能是构建更复杂、更美观的PyQt5图形界面的重要第一步。

080:PyQt5图像处理入门 🖼️

在本节课中,我们将学习如何在PyQt5应用程序中添加和显示图像。你将学会使用QLabelQPixmap来加载图片,并掌握调整图像大小和位置的基本技巧。

准备工作

在开始之前,你需要准备一张图片。你可以使用自己的个人资料图片或任何其他图片。我们将使用这张图片进行后续操作。

以下是实现此功能所需的导入语句:

from PyQt5.QtWidgets import QLabel
from PyQt5.QtGui import QPixmap

创建标签并设置几何属性

上一节我们介绍了所需的导入,本节中我们来看看如何创建用于显示图像的标签。

在窗口类的构造函数中,我们首先创建一个QLabel对象,并设置其大小和位置。

self.label = QLabel(self)
self.label.setGeometry(0, 0, 250, 250)

这段代码创建了一个标签,并将其放置在窗口的左上角(坐标(0, 0)),宽度和高度均为250像素。

加载并显示图像

现在标签已经创建好了,但它是空的。接下来,我们需要加载图像并将其显示在标签上。

以下是加载图像的步骤:

  1. 使用QPixmap类加载图像文件。
  2. 将加载的QPixmap对象设置到标签上。
self.pixmap = QPixmap('profile_p.jpg')
self.label.setPixmap(self.pixmap)

代码中的'profile_p.jpg'是图像文件的路径。如果图片与你的Python文件在同一目录下,直接使用文件名即可。

让图像自适应标签大小

运行上述代码后,你可能会发现图像没有填满整个标签区域。为了让图像根据标签的大小进行缩放,我们需要启用一个属性。

只需调用标签的setScaledContents方法并传入True

self.label.setScaledContents(True)

启用此功能后,无论你如何调整标签的尺寸,图像都会自动缩放以填充整个标签区域。

调整图像在窗口中的位置

我们可以通过改变标签的几何属性,轻松地将图像定位在窗口的不同位置。

以下是几种常见的定位方法:

  • 右上角:将X坐标设置为窗口宽度减去标签宽度。
  • 右下角:将X坐标设置为窗口宽度减去标签宽度,Y坐标设置为窗口高度减去标签高度。
  • 左下角:将X坐标设置为0,Y坐标设置为窗口高度减去标签高度。
  • 居中:将X坐标设置为(窗口宽度 - 标签宽度) // 2,Y坐标设置为(窗口高度 - 标签高度) // 2。这里使用整数除法//以确保像素坐标为整数。

例如,将图像居中的代码如下:

window_width = self.width()
window_height = self.height()
label_width = self.label.width()
label_height = self.label.height()

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/f0597931941f80aaa4093f39764426b1_41.png)

x = (window_width - label_width) // 2
y = (window_height - label_height) // 2

self.label.setGeometry(x, y, label_width, label_height)

总结

本节课中我们一起学习了在PyQt5中处理图像的基础知识。我们掌握了如何使用QLabelQPixmap加载并显示图片,如何通过setScaledContents(True)让图像自适应标签大小,以及如何通过计算坐标将图像精准地定位在窗口的任意位置。这些是构建具有丰富图像界面应用程序的重要基础技能。

081:PyQt5布局管理器详解 🧩

在本节课中,我们将要学习PyQt5中的布局管理器。布局管理器是用于自动排列窗口内控件(如按钮、标签)的工具,它能帮助我们创建整洁、响应式的用户界面,而无需手动计算每个控件的位置。

我们将讨论三种主要的布局管理器:垂直布局、水平布局和网格布局。为了保持代码的整洁和可维护性,我们还会学习一种常见的代码组织方法。


导入必要的模块

首先,我们需要导入PyQt5中用于创建布局和控件的相关类。以下是所需的导入语句:

from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout
  • QLabel:用于创建文本或图像标签。
  • QWidget:一个通用的窗口控件,可以作为其他控件的容器。
  • QVBoxLayout:垂直布局管理器。
  • QHBoxLayout:水平布局管理器。
  • QGridLayout:网格布局管理器。

组织代码:初始化用户界面函数

在PyQt5编程中,一个常见的良好实践是将所有与用户界面设置相关的代码集中在一个单独的函数中。这有助于保持主窗口类的代码清晰有序。

我们将在主窗口类中定义一个名为 initUI 的函数:

def initUI(self):
    # 所有UI初始化代码将写在这里
    pass

当创建窗口对象后,我们会调用 self.initUI() 来初始化界面。


理解中央控件

QMainWindow 主窗口对象有特定的设计结构,不能直接设置布局管理器。我们需要一个中间步骤:

  1. 创建一个通用的 QWidget 作为中央控件
  2. 将布局管理器设置给这个中央控件。
  3. 最后,将这个中央控件设置为主窗口的中央部件。

以下是实现代码:

def initUI(self):
    # 1. 创建中央控件
    central_widget = QWidget()
    # 2. 将中央控件设置给主窗口
    self.setCentralWidget(central_widget)
    # 后续的布局和控件都将添加到这个 central_widget 上

创建示例控件

为了演示布局的效果,我们需要一些控件。让我们创建五个带有不同背景颜色的标签:

# 创建五个标签
label1 = QLabel(“标签 1”)
label1.setStyleSheet(“background-color: red;”)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/931479e9504648531dcc81a67fa2b566_1.png)

![](https://github.com/OpenDocCN/cs-notes-pt1-zh/raw/master/docs/py-cmpl-intro/img/931479e9504648531dcc81a67fa2b566_2.png)

label2 = QLabel(“标签 2”)
label2.setStyleSheet(“background-color: yellow;”)

label3 = QLabel(“标签 3”)
label3.setStyleSheet(“background-color: green;”)

label4 = QLabel(“标签 4”)
label4.setStyleSheet(“background-color: blue;”)

label5 = QLabel(“标签 5”)
label5.setStyleSheet(“background-color: purple;”)

如果不使用布局管理器,这些标签会相互重叠,只能看到最上面的一个。


使用垂直布局管理器 📏

上一节我们创建了控件,本节中我们来看看如何使用垂直布局管理器将它们有序排列。

垂直布局会将控件从上到下依次排列。

以下是使用垂直布局的步骤:

  1. 创建 QVBoxLayout 对象。
  2. 使用 addWidget() 方法将控件逐个添加到布局中。
  3. 将这个布局设置给之前创建的中央控件。
# 创建垂直布局
v_layout = QVBoxLayout()
# 将标签添加到布局中
v_layout.addWidget(label1)
v_layout.addWidget(label2)
v_layout.addWidget(label3)
v_layout.addWidget(label4)
v_layout.addWidget(label5)
# 将布局设置给中央控件
central_widget.setLayout(v_layout)

执行后,五个标签将垂直排列。


使用水平布局管理器 ↔️

理解了垂直布局后,水平布局就很容易掌握了。它的逻辑相同,只是排列方向变为从左到右。

只需将 QVBoxLayout 替换为 QHBoxLayout

# 创建水平布局
h_layout = QHBoxLayout()
h_layout.addWidget(label1)
h_layout.addWidget(label2)
h_layout.addWidget(label3)
h_layout.addWidget(label4)
h_layout.addWidget(label5)
central_widget.setLayout(h_layout)

现在,五个标签将水平排列。


使用网格布局管理器 🔲

对于更复杂的界面,我们需要网格布局。它允许我们将控件放置在一个由行和列组成的网格中。

使用 QGridLayout 时,在添加控件时需要额外指定该控件所在的行和列索引(索引从0开始)。

以下是使用网格布局的示例:

# 创建网格布局
grid_layout = QGridLayout()
# 添加控件,并指定位置 (行, 列)
grid_layout.addWidget(label1, 0, 0) # 第0行,第0列
grid_layout.addWidget(label2, 0, 1) # 第0行,第1列
grid_layout.addWidget(label3, 1, 0) # 第1行,第0列
grid_layout.addWidget(label4, 1, 1) # 第1行,第1列
grid_layout.addWidget(label5, 1, 2) # 第1行,第2列
central_widget.setLayout(grid_layout)

通过调整行和列的索引,你可以自由地控制每个控件在窗口中的位置。


总结

本节课中我们一起学习了PyQt5中三种核心的布局管理器:

  1. 垂直布局 (QVBoxLayout):用于将控件从上到下排列。
  2. 水平布局 (QHBoxLayout):用于将控件从左到右排列。
  3. 网格布局 (QGridLayout):用于将控件放入一个灵活的网格中,通过行和列索引精确定位。

我们还学习了通过创建 initUI 函数来组织UI代码,以及理解 QMainWindow 需要通过中央控件 (QWidget) 来使用布局管理器这一重要概念。掌握这些布局工具是构建结构清晰、美观的PyQt5应用程序的基础。

082:PyQt5按钮控件入门指南 🎯

在本节课中,我们将学习如何在PyQt5中创建和使用按钮控件。我们将从导入必要的模块开始,逐步创建一个带有交互功能的按钮,并学习如何通过信号与槽机制将按钮点击事件与特定功能连接起来。


导入必要模块

首先,我们需要导入创建按钮和标签所需的PyQt5模块。

from PyQt5.QtWidgets import QPushButton, QLabel

上一节我们介绍了必要的导入,本节中我们来看看如何创建主窗口和初始化用户界面。


初始化主窗口与用户界面

在之前关于布局管理器的主题中,我们在主窗口的构造函数中定义了一个用于初始化用户界面的方法 initializeUI。确保在构造函数中调用此方法,所有用户界面的创建和管理都将在此方法中进行。

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initializeUI()

    def initializeUI(self):
        # 在这里创建和管理用户界面
        pass

创建按钮控件

以下是创建按钮控件的步骤。

首先,调用 QPushButton 构造函数来创建一个按钮。通常,在创建控件时,我们会使用 self 前缀将其定义为实例变量,例如 self.button。但为了演示不使用 self 前缀时会发生什么,我们先创建一个局部变量 button

button = QPushButton('点击我')

接着,将这个按钮添加到窗口中,并设置其几何位置。为了简化,我们直接使用坐标而非布局管理器。

self.setGeometry(150, 200, 200, 100)

按钮的字体可能较小,我们可以通过设置样式表来调整。

button.setStyleSheet('font-size: 30px;')

现在,我们有了一个可显示的按钮。接下来,我们需要为其添加点击功能。


定义按钮点击功能

我们需要在 MainWindow 类中定义一个方法,作为按钮点击时执行的功能。

def on_click(self):
    print('按钮被点击了')

目前,点击按钮不会有任何反应。我们需要建立按钮的信号与槽连接。


连接信号与槽

信号是控件交互时发出的事件,槽是响应信号执行的动作或方法。我们将按钮的 clicked 信号连接到 on_click 方法。

button.clicked.connect(self.on_click)

现在,每次点击按钮,都会执行 on_click 方法,并在控制台打印消息。

然而,由于 button 是局部变量,on_click 方法无法识别它。为了解决这个问题,我们需要将按钮定义为实例变量。

self.button = QPushButton('点击我')

相应地,更新所有对 button 的引用为 self.button


完善按钮功能

除了打印消息,我们还可以在点击时改变按钮的文本。

def on_click(self):
    print('按钮被点击了')
    self.button.setText('已点击')

我们还可以在点击后禁用按钮,使其无法再次点击。

self.button.setDisabled(True)

与标签控件交互

为了展示按钮如何影响其他控件,我们创建一个标签,并在按钮点击时改变其文本。

首先,在构造函数中创建标签并设置其初始文本和样式。

self.label = QLabel('你好')
self.label.setGeometry(150, 300, 200, 100)
self.label.setStyleSheet('font-size: 50px;')

然后,在 on_click 方法中更新标签的文本。

def on_click(self):
    print('按钮被点击了')
    self.button.setText('已点击')
    self.button.setDisabled(True)
    self.label.setText('再见')

现在,点击按钮不仅会改变按钮自身的状态,还会更新标签的显示内容。


总结

本节课中我们一起学习了PyQt5中按钮控件的基本使用。我们了解了如何创建按钮、设置其样式和位置,以及如何通过信号与槽机制为其添加交互功能。关键点包括:

  1. 创建按钮:使用 QPushButton 构造函数。
  2. 设置样式:通过 setStyleSheet 方法调整外观。
  3. 信号与槽:使用 clicked.connect() 将按钮点击事件连接到自定义方法。
  4. 实例变量:确保控件定义为实例变量(使用 self 前缀),以便在类方法中访问。
  5. 交互功能:按钮可以触发自身文本变化、禁用状态,以及更新其他控件(如标签)的内容。

通过掌握这些基础知识,你已经能够为PyQt5应用程序创建基本的交互界面了。

083:PyQt5复选框使用指南 🎯

在本节课中,我们将学习如何在PyQt5中使用复选框(Checkbox)控件。我们将从创建复选框开始,逐步学习如何设置其样式、初始状态,以及如何为其添加交互功能。

导入必要模块

要使用复选框,首先需要导入相关的模块。以下是必需的导入语句:

from PyQt5.QtWidgets import QCheckBox
from PyQt5.QtCore import Qt

QCheckBox类用于创建复选框控件。Qt模块包含了许多非GUI相关的核心类,其中定义了一些有用的常量,例如表示复选框状态的Qt.Checked

创建复选框

上一节我们介绍了必要的导入,本节中我们来看看如何在主窗口的构造函数中创建一个复选框。

self.checkbox = QCheckBox("你喜欢食物吗?", self)

以上代码创建了一个文本为“你喜欢食物吗?”的复选框,并将其父窗口设置为当前窗口(self)。第一个参数是复选框显示的文本,第二个参数是其父控件。

设置样式与几何属性

创建复选框后,我们可能希望调整其外观和位置。以下是设置样式和几何属性的方法:

self.checkbox.setStyleSheet("font-size: 30px; font-family: Arial;")
self.checkbox.setGeometry(10, 10, 500, 100)

setStyleSheet方法允许我们使用类似CSS的语法来设置控件样式,例如字体大小和字体族。setGeometry方法用于设置控件的位置和大小,参数依次为x坐标、y坐标、宽度和高度。

设置初始状态

默认情况下,复选框处于未选中状态。我们可以通过编程方式设置其初始状态。

self.checkbox.setChecked(True)  # 设置为选中状态
self.checkbox.setChecked(False) # 设置为未选中状态

调用setChecked方法并传入TrueFalse,可以分别在窗口加载时将复选框设置为选中或未选中状态。

添加交互功能

目前复选框还没有任何交互功能。我们需要将复选框的状态变化信号连接到一个槽函数上,以实现交互。

首先,在主窗口类中定义一个方法作为槽函数:

def checkbox_changed(self, state):
    if state == Qt.Checked:
        print("你喜欢食物。")
    else:
        print("你不喜欢食物。")

然后,将复选框的stateChanged信号连接到这个槽函数:

self.checkbox.stateChanged.connect(self.checkbox_changed)

当用户点击复选框时,stateChanged信号会被触发,并调用checkbox_changed方法。该方法的state参数会接收复选框的当前状态值。

理解状态值

在槽函数中,我们通过判断state参数的值来执行不同的操作。复选框的状态值如下:

  • 0 (Qt.Unchecked): 表示复选框未选中。
  • 2 (Qt.Checked): 表示复选框已选中。
  • 1 (Qt.PartiallyChecked): 表示部分选中状态(通常用于三态复选框,本课不涉及)。

在代码中直接使用数字(如2)不够直观。使用Qt.CheckedQt.Unchecked这些常量会使代码更易读、更易维护。

本节课中我们一起学习了PyQt5中复选框的基本用法。我们掌握了如何创建复选框、设置其样式和位置、控制其初始状态,以及最重要的——如何通过信号与槽机制为其添加交互逻辑,从而根据用户的选择执行不同的操作。

084:PyQt5单选按钮教程 🎛️

在本节课中,我们将学习如何在PyQt5中使用单选按钮(Radio Buttons)。我们将涵盖如何创建单选按钮、如何将它们分组,以及如何响应用户的选择。


导入必要的模块

首先,我们需要从PyQt5的QtWidgets模块中导入必要的类。

from PyQt5.QtWidgets import QRadioButton, QButtonGroup

QRadioButton 类用于创建单选按钮,而 QButtonGroup 类用于将多个单选按钮组合在一起,确保同一时间只能选择组内的一个选项。


创建单选按钮

在窗口的构造函数中,我们将创建三个单选按钮,代表不同的支付方式。

self.radio1 = QRadioButton("Visa", self)
self.radio2 = QRadioButton("Mastercard", self)
self.radio3 = QRadioButton("Gift Card", self)

以上代码创建了三个单选按钮,分别对应Visa、Mastercard和Gift Card三种支付选项。


设置单选按钮的位置

由于我们没有使用布局管理器,需要手动设置每个单选按钮的位置和大小。

self.radio1.setGeometry(0, 0, 300, 50)
self.radio2.setGeometry(0, 50, 300, 50)
self.radio3.setGeometry(0, 100, 300, 50)

这里,我们使用 setGeometry 方法设置每个按钮的坐标和尺寸。Y坐标依次增加50像素,使按钮垂直排列。


应用样式表

默认的字体可能较小,我们可以使用样式表来统一设置所有单选按钮的样式。

self.setStyleSheet("""
    QRadioButton {
        font-size: 40px;
        font-family: Arial;
        padding: 10px;
    }
""")

这段代码将窗口内所有 QRadioButton 的字体大小设置为40像素,字体为Arial,并添加10像素的内边距。


理解单选按钮分组

默认情况下,所有单选按钮都属于同一个组,这意味着只能同时选中一个。但在实际应用中,我们可能需要多个独立的选项组。

例如,除了支付方式,我们可能还需要选择支付场景(店内支付或在线支付)。为此,我们创建另外两个单选按钮。

self.radio4 = QRadioButton("In Store", self)
self.radio5 = QRadioButton("Online", self)
self.radio4.setGeometry(0, 150, 300, 50)
self.radio5.setGeometry(0, 200, 300, 50)

现在,我们有五个单选按钮,但前三个(支付方式)和后两个(支付场景)在逻辑上应该属于不同的组。


创建按钮组

为了实现独立的分组,我们需要使用 QButtonGroup 类。

self.button_group1 = QButtonGroup(self)
self.button_group2 = QButtonGroup(self)

self.button_group1.addButton(self.radio1)
self.button_group1.addButton(self.radio2)
self.button_group1.addButton(self.radio3)

self.button_group2.addButton(self.radio4)
self.button_group2.addButton(self.radio5)

这样,radio1radio2radio3 属于 button_group1,而 radio4radio5 属于 button_group2。用户可以在每个组内独立选择一个选项。


连接信号与槽

为了让单选按钮在用户选择时执行某些操作,我们需要将它们的 toggled 信号连接到一个自定义的槽函数。

首先,定义一个槽函数。

def radio_button_changed(self):
    pass  # 暂时留空

然后,将每个单选按钮的 toggled 信号连接到这个槽函数。

self.radio1.toggled.connect(self.radio_button_changed)
self.radio2.toggled.connect(self.radio_button_changed)
self.radio3.toggled.connect(self.radio_button_changed)
self.radio4.toggled.connect(self.radio_button_changed)
self.radio5.toggled.connect(self.radio_button_changed)


确定被选中的按钮

在槽函数中,我们需要确定是哪个单选按钮发出了信号,以及它是否被选中。

def radio_button_changed(self):
    radio_button = self.sender()  # 获取发出信号的按钮
    if radio_button.isChecked():  # 检查按钮是否被选中
        print(f"{radio_button.text()} is selected")

self.sender() 方法返回发出信号的控件对象。isChecked() 方法返回一个布尔值,表示按钮是否被选中。通过 text() 方法可以获取按钮上显示的文本。


测试功能

运行程序,点击不同的单选按钮,控制台将输出相应的选择信息。例如,选择“Visa”和“In Store”会分别打印:

Visa is selected
In Store is selected

总结

本节课中,我们一起学习了PyQt5中单选按钮的基本用法。我们掌握了如何创建和定位单选按钮,如何使用样式表美化界面,以及如何通过 QButtonGroup 实现独立的分组逻辑。最后,我们学习了如何连接信号与槽来响应用户的选择,并获取被选中按钮的信息。这些知识是构建交互式GUI应用程序的基础。

085:PyQt5中的单行文本输入框

在本节课中,我们将学习如何在PyQt5中创建和使用单行文本输入框,即QLineEdit部件。我们将学习如何创建它、设置其样式、获取用户输入的文本,并将其与按钮点击事件结合,实现一个简单的交互功能。

导入模块与创建窗口

首先,我们需要从PyQt5.QtWidgets模块中导入必要的类,包括QApplicationQMainWindowQLineEditQPushButton。我们将创建一个主窗口类,并在其中初始化用户界面。

from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QPushButton
import sys

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 设置窗口标题和初始大小
        self.setWindowTitle('Line Edit 示例')
        self.setGeometry(100, 100, 400, 200)

创建单行文本输入框

上一节我们介绍了窗口的创建,本节中我们来看看如何添加一个单行文本输入框。在initUI方法中,我们创建一个QLineEdit实例,并将其添加到窗口中。

        # 创建一个单行文本输入框
        self.line_edit = QLineEdit(self)
        self.line_edit.setGeometry(10, 10, 200, 40)

设置输入框样式

创建输入框后,其默认字体可能较小。我们可以使用样式表(StyleSheet)来调整其外观,例如增大字体和更改字体族。

        # 设置输入框的样式(字体大小和字体族)
        self.line_edit.setStyleSheet("font-size: 25px; font-family: Arial;")

添加按钮以获取文本

单行文本输入框本身不会执行任何操作。为了响应用户输入,我们需要添加一个按钮。当按钮被点击时,我们将获取输入框中的文本并进行处理。

以下是创建和设置按钮的步骤:

  1. 创建一个QPushButton实例。
  2. 设置按钮的几何位置和大小。
  3. 使用样式表调整按钮的字体。
  4. 将按钮的clicked信号连接到一个自定义方法。
        # 创建一个按钮
        self.button = QPushButton('提交', self)
        self.button.setGeometry(220, 10, 100, 40)
        self.button.setStyleSheet("font-size: 25px; font-family: Arial;")

        # 将按钮的点击信号连接到submit方法
        self.button.clicked.connect(self.submit)

定义处理函数

现在,我们需要定义当按钮被点击时调用的submit方法。在这个方法中,我们将获取单行文本输入框中的文本,并将其打印出来。

    def submit(self):
        # 获取输入框中的文本
        text = self.line_edit.text()
        # 打印问候语
        print(f"你好,{text}!")

设置占位符文本

为了提高用户体验,我们可以在输入框中设置占位符文本,提示用户应该输入什么内容。

        # 设置输入框的占位符文本
        self.line_edit.setPlaceholderText("请输入你的名字")

运行应用程序

最后,我们需要创建应用程序实例和主窗口,并启动应用程序的事件循环。

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

本节课中我们一起学习了PyQt5中QLineEdit部件的基本用法。我们掌握了如何创建单行文本输入框、设置其样式、添加占位符文本,以及如何通过按钮点击事件获取并处理用户输入的文本。这些是构建交互式图形用户界面的基础。

Python超全入门教程:P86:PyQt5中轻松添加CSS样式属性

在本节课中,我们将学习如何在PyQt5中使用setStyleSheet方法为应用程序添加类似CSS的样式。我们将从设置全局样式开始,然后学习如何为单个控件定制样式,最后介绍使用伪类实现交互效果。


上一节我们介绍了PyQt5的基本控件和布局。本节中,我们来看看如何美化这些控件。

首先,我们需要导入必要的模块并创建一个带有按钮的基本窗口。

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QHBoxLayout

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 创建三个按钮
        self.button1 = QPushButton('按钮一')
        self.button2 = QPushButton('按钮二')
        self.button3 = QPushButton('按钮三')

        # 为按钮设置对象名,以便后续单独设置样式
        self.button1.setObjectName('button1')
        self.button2.setObjectName('button2')
        self.button3.setObjectName('button3')

        # 创建中央部件和水平布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QHBoxLayout()
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        layout.addWidget(self.button3)
        central_widget.setLayout(layout)

现在,窗口已经创建好了。接下来,我们将使用setStyleSheet方法为整个窗口应用样式。

为所有按钮设置全局样式

我们可以通过选择控件类名(如QPushButton)来为所有同类控件设置样式。以下是如何操作:

        self.setStyleSheet("""
            QPushButton {
                font-size: 40px;
                font-family: Arial;
                padding: 15px 75px;
                margin: 25px;
                border: 3px solid black;
                border-radius: 15px;
            }
        """)

这段代码为所有QPushButton设置了字体大小、字体、内边距、外边距、边框和圆角。

为单个按钮设置特定样式

如果只想改变某个特定按钮的样式,我们需要使用其对象名进行选择。以下是具体方法:

        self.setStyleSheet("""
            QPushButton {
                font-size: 40px;
                font-family: Arial;
                padding: 15px 75px;
                margin: 25px;
                border: 3px solid black;
                border-radius: 15px;
            }
            QPushButton#button1 {
                background-color: red;
            }
            QPushButton#button2 {
                background-color: green;
            }
            QPushButton#button3 {
                background-color: blue;
            }
        """)

这里,#button1 选择了对象名为button1的按钮,并单独将其背景色设置为红色。其他按钮同理。

使用更丰富的颜色值

除了颜色名称,我们还可以使用十六进制、RGB或HSL值来定义更精确的颜色。

以下是不同颜色格式的示例:

  • 十六进制#ff5733
  • RGBrgb(255, 87, 51)
  • HSLhsl(11, 100%, 60%)

例如,将按钮二的颜色改为特定的绿色:

            QPushButton#button2 {
                background-color: hsl(120, 100%, 40%);
            }

添加交互效果(悬停状态)

我们可以使用CSS伪类(如:hover)为控件添加交互效果。以下是为按钮添加悬停时变亮的效果:

            QPushButton#button1:hover {
                background-color: hsl(0, 100%, 70%);
            }
            QPushButton#button2:hover {
                background-color: hsl(120, 100%, 60%);
            }
            QPushButton#button3:hover {
                background-color: hsl(240, 100%, 70%);
            }

当鼠标悬停在按钮上时,其背景色的亮度会增加,产生视觉反馈。


本节课中我们一起学习了PyQt5中setStyleSheet方法的核心用法。我们掌握了如何为所有同类控件设置全局样式,如何通过对象名为单个控件定制样式,以及如何使用颜色值和伪类来增强界面的视觉效果和交互性。通过结合这些技巧,你可以轻松地创建出美观且专业的PyQt5应用程序界面。

087:使用Python创建数字时钟

在本节课中,我们将学习如何使用Python的PyQt5库构建一个数字时钟小部件。我们将从设置环境开始,逐步创建窗口、设计布局、显示时间,并最终实现一个每秒自动更新的数字时钟。


导入必要的库

首先,我们需要导入构建应用程序所需的模块。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
from PyQt5.QtCore import QTimer, QTime, Qt
from PyQt5.QtGui import QFont, QFontDatabase
  • sys 模块用于处理系统相关的操作。
  • PyQt5.QtWidgets 提供了创建GUI应用程序的基本构件。
  • PyQt5.QtCore 包含非GUI的核心功能,如计时器。
  • PyQt5.QtGui 用于处理字体等图形界面元素。

创建数字时钟类

我们将创建一个继承自 QWidget 的类,作为我们自定义的数字时钟小部件。

class DigitalClock(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

在构造函数中,我们调用了父类的构造函数,并执行 initUI 方法来初始化用户界面。


初始化用户界面

initUI 方法负责设置窗口的标题、大小、布局和样式。

    def initUI(self):
        self.setWindowTitle('数字时钟')
        self.setGeometry(600, 300, 300, 100)

        # 创建时间标签
        self.time_label = QLabel(self)
        self.time_label.setAlignment(Qt.AlignCenter)

        # 设置字体和颜色
        font_id = QFontDatabase.addApplicationFont("DS-DIGI.TTF")
        font_family = QFontDatabase.applicationFontFamilies(font_id)[0]
        my_font = QFont(font_family, 150)
        self.time_label.setFont(my_font)
        self.time_label.setStyleSheet("color: hsl(120, 100%, 50%);")

        # 设置窗口背景色
        self.setStyleSheet("background-color: black;")

        # 设置布局
        vbox = QVBoxLayout()
        vbox.addWidget(self.time_label)
        self.setLayout(vbox)

        # 创建并启动计时器
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

        # 初始更新时间显示
        self.update_time()

以下是上述代码中关键步骤的说明:

  1. 设置窗口属性:定义了窗口标题和初始位置、大小。
  2. 创建时间标签QLabel 用于显示时间文本,并设置为居中对齐。
  3. 加载自定义字体:通过 QFontDatabase 加载项目文件夹中的 DS-DIGI.TTF 字体文件,并应用到标签上。
  4. 设置样式:使用CSS样式表将时间文本颜色设置为亮绿色,窗口背景设置为黑色。
  5. 管理布局:使用 QVBoxLayout 垂直布局管理器将标签添加到窗口中。
  6. 设置计时器:创建一个 QTimer,将其 timeout 信号连接到 update_time 方法,并设置为每1000毫秒(1秒)触发一次。


更新时间的方法

update_time 方法用于获取当前时间,并将其格式化为字符串显示在标签上。

    def update_time(self):
        current_time = QTime.currentTime().toString("hh:mm:ss AP")
        self.time_label.setText(current_time)
  • QTime.currentTime() 获取当前时间。
  • .toString("hh:mm:ss AP") 将时间格式化为“时:分:秒 上午/下午”的字符串格式。

启动应用程序

最后,我们需要编写启动应用程序的代码。这部分代码通常放在文件末尾。

if __name__ == '__main__':
    app = QApplication(sys.argv)
    clock = DigitalClock()
    clock.show()
    sys.exit(app.exec_())
  1. 创建一个 QApplication 实例,它是所有PyQt5桌面应用程序的基础。
  2. 实例化我们的 DigitalClock 类。
  3. 调用 show() 方法显示窗口。
  4. app.exec_() 启动应用程序的主事件循环,使窗口保持运行直到被关闭。sys.exit() 确保应用程序能正确退出。

总结

本节课中我们一起学习了如何使用PyQt5创建一个功能完整的数字时钟。我们涵盖了从导入库、创建自定义窗口部件、设计用户界面、使用计时器动态更新时间,到加载自定义字体美化显示的全过程。通过这个项目,你掌握了PyQt5中窗口、标签、布局、计时器和字体处理等核心概念,为开发更复杂的GUI应用程序打下了基础。

088:使用Python创建秒表程序 🕒

在本节课中,我们将学习如何使用Python的PyQt5库来创建一个功能完整的秒表应用程序。我们将从设置基础窗口开始,逐步添加时间显示、控制按钮和计时逻辑。


导入必要的库

首先,我们需要导入构建应用程序所需的所有模块和组件。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout
from PyQt5.QtCore import QTimer, QTime, Qt
  • sys:用于处理系统变量。
  • PyQt5.QtWidgets:提供应用程序的界面组件(控件)。
  • PyQt5.QtCore:提供核心功能,如计时器和时间处理。

构建应用程序骨架

上一节我们导入了必要的库,本节中我们来看看如何搭建应用程序的基本框架。

我们将创建一个继承自QWidgetStopwatch类,并设置主程序入口。

class Stopwatch(QWidget):
    def __init__(self):
        super().__init__()
        # 初始化代码将放在这里

if __name__ == '__main__':
    app = QApplication(sys.argv)
    stopwatch = Stopwatch()
    stopwatch.show()
    sys.exit(app.exec_())

这段代码创建了一个基本的PyQt5应用程序窗口。当直接运行此脚本时,它会显示一个空窗口。


初始化用户界面组件

现在,我们有了一个窗口框架。接下来,我们需要在Stopwatch类的__init__方法中初始化所有必要的组件。

以下是需要创建的组件列表:

  • self.time:一个QTime对象,用于存储和计算时间。
  • self.time_label:一个QLabel控件,用于显示时间。
  • self.start_button, self.stop_button, self.reset_button:三个QPushButton控件,用于控制秒表。
  • self.timer:一个QTimer对象,用于定期触发时间更新。

我们还需要调用一个方法来设置这些组件的布局和样式,这个方法我们称之为init_ui


设计用户界面布局

init_ui方法中,我们将安排控件的位置并设置它们的样式。

首先,设置窗口标题。

self.setWindowTitle('Stopwatch')

接着,使用布局管理器来排列控件。我们将使用垂直布局(QVBoxLayout)来放置时间标签和按钮组,使用水平布局(QHBoxLayout)来水平排列三个按钮。

vbox = QVBoxLayout()
hbox = QHBoxLayout()

# 将按钮添加到水平布局
hbox.addWidget(self.start_button)
hbox.addWidget(self.stop_button)
hbox.addWidget(self.reset_button)

# 将标签和按钮组添加到垂直布局
vbox.addWidget(self.time_label)
vbox.addLayout(hbox)

# 将垂直布局设置为主窗口的布局
self.setLayout(vbox)

为了让时间标签看起来更美观,我们将其文本居中对齐,并应用一些CSS样式。

self.time_label.setAlignment(Qt.AlignCenter)
self.setStyleSheet("""
    QPushButton, QLabel {
        font-size: 20px;
        padding: 10px;
        font-weight: bold;
        font-family: Calibri;
    }
    QLabel {
        font-size: 48px;
        background-color: hsl(210, 100%, 90%);
        border-radius: 20px;
    }
""")


连接信号与槽

界面已经设计好了,但按钮还没有功能。我们需要将按钮的“点击”信号连接到对应的功能函数(槽)。

以下是需要连接的信号:

  • 开始按钮 -> self.start 方法
  • 停止按钮 -> self.stop 方法
  • 重置按钮 -> self.reset 方法
  • 计时器超时信号 -> self.update_display 方法
self.start_button.clicked.connect(self.start)
self.stop_button.clicked.connect(self.stop)
self.reset_button.clicked.connect(self.reset)
self.timer.timeout.connect(self.update_display)

实现核心功能方法

现在,我们来逐一实现控制秒表逻辑的方法。

1. start 方法
此方法启动计时器,使其每10毫秒触发一次超时信号。

def start(self):
    self.timer.start(10) # 每10毫秒触发一次

2. stop 方法
此方法停止计时器。

def stop(self):
    self.timer.stop()

3. reset 方法
此方法停止计时器,将时间重置为00:00:00.00,并更新显示。

def reset(self):
    self.timer.stop()
    self.time = QTime(0, 0, 0, 0) # 重置为0
    self.time_label.setText(self.format_time(self.time))

4. format_time 方法
此方法接收一个QTime对象,并将其格式化为HH:MM:SS.mm的字符串。

def format_time(self, time):
    hours = time.hour()
    minutes = time.minute()
    seconds = time.second()
    milliseconds = time.msec() // 10 # 转换为2位毫秒
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:02d}"

5. update_display 方法
这是计时器超时时调用的方法。它将当前时间增加10毫秒,并更新标签的显示。

def update_display(self):
    self.time = self.time.addMSecs(10) # 增加10毫秒
    self.time_label.setText(self.format_time(self.time))

运行与测试

所有代码都已就绪。运行程序,你将看到一个带有“开始”、“停止”、“重置”按钮的秒表窗口。点击“开始”,时间开始走动;点击“停止”,时间暂停;点击“重置”,时间归零。


本节课中我们一起学习了如何使用PyQt5构建一个图形界面的秒表程序。我们涵盖了从导入库、创建窗口、设计布局、应用样式,到实现计时逻辑和连接事件响应的完整流程。你可以在此基础上进一步美化界面或添加更多功能,如记录圈数等。

089:使用Python编写一个天气应用 🌤️

在本节课中,我们将创建一个能够从API获取实时天气数据的天气应用。这是一个综合性项目,请根据自己的节奏学习,可以花几天甚至几周时间来完成。完成后,你甚至可以将其添加到你的作品集中。

概述

我们将使用Python的PyQt5库来构建图形用户界面,并通过OpenWeatherMap API获取天气数据。教程将涵盖API注册、界面设计、网络请求、数据处理和异常处理等核心内容。

1. 项目准备与API获取

首先,我们需要获取用于获取天气数据的API密钥。我们将使用OpenWeatherMap提供的免费服务。

以下是获取API密钥的步骤:

  1. 访问网站 openweathermap.org
  2. 点击“Sign In”并创建一个免费账户。
  3. 登录后,点击右上角的下拉菜单,选择“My API Keys”。
  4. 复制生成的API密钥。如果状态显示为“Inactive”,请点击切换按钮将其激活。激活过程可能需要几分钟。

注意:请妥善保管你的API密钥,我们将在后续代码中使用它。

2. 环境搭建与基础窗口

如果你是本系列教程的新手,需要先安装必要的库。我们将使用PyQt5来构建图形界面。

首先,通过pip安装PyQt5:

pip install PyQt5

安装完成后,我们需要导入必要的模块。以下是完整的导入列表:

import sys
import requests
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout
from PyQt5.QtCore import Qt

接下来,我们创建一个基础的应用程序窗口类。

class WeatherApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    weather_app = WeatherApp()
    weather_app.show()
    sys.exit(app.exec_())

这段代码创建了一个继承自QWidgetWeatherApp类,并设置了应用程序的主循环,确保窗口能正常显示和关闭。

3. 设计用户界面

上一节我们创建了基础窗口,本节中我们来设计应用的用户界面。我们将添加标签、输入框和按钮等控件。

initUI方法中,我们首先设置窗口标题,然后创建并排列各个控件。

def initUI(self):
    self.setWindowTitle('Weather App')

    # 创建控件
    self.city_label = QLabel('Enter city name', self)
    self.city_input = QLineEdit(self)
    self.get_weather_button = QPushButton('Get Weather', self)
    self.temperature_label = QLabel(self)
    self.emoji_label = QLabel(self)
    self.description_label = QLabel(self)

    # 创建垂直布局管理器并添加控件
    vbox = QVBoxLayout()
    vbox.addWidget(self.city_label)
    vbox.addWidget(self.city_input)
    vbox.addWidget(self.get_weather_button)
    vbox.addWidget(self.temperature_label)
    vbox.addWidget(self.emoji_label)
    vbox.addWidget(self.description_label)

    # 设置布局
    self.setLayout(vbox)

    # 水平居中除按钮外的所有控件
    self.city_label.setAlignment(Qt.AlignCenter)
    self.city_input.setAlignment(Qt.AlignCenter)
    self.temperature_label.setAlignment(Qt.AlignCenter)
    self.emoji_label.setAlignment(Qt.AlignCenter)
    self.description_label.setAlignment(Qt.AlignCenter)

现在,界面上的控件已经垂直排列,并且大部分控件都居中对齐了。

4. 美化界面样式

界面虽然有了布局,但样式比较简陋。本节我们将使用CSS样式表来美化应用的外观。

我们为每个控件设置一个唯一的对象名,然后通过样式表来定义它们的字体、大小等属性。

def initUI(self):
    # ... (之前的控件创建和布局代码)

    # 为控件设置对象名,用于CSS选择器
    self.city_label.setObjectName('city_label')
    self.city_input.setObjectName('city_input')
    self.get_weather_button.setObjectName('get_weather_button')
    self.temperature_label.setObjectName('temperature_label')
    self.emoji_label.setObjectName('emoji_label')
    self.description_label.setObjectName('description_label')

    # 应用样式表
    self.setStyleSheet('''
        QLabel, QPushButton {
            font-family: Calibri;
        }
        QLabel#city_label {
            font-size: 40px;
            font-style: italic;
        }
        QLineEdit#city_input {
            font-size: 40px;
        }
        QPushButton#get_weather_button {
            font-size: 30px;
            font-weight: bold;
        }
        QLabel#temperature_label {
            font-size: 75px;
        }
        QLabel#emoji_label {
            font-size: 100px;
            font-family: "Segoe UI Emoji";
        }
        QLabel#description_label {
            font-size: 50px;
        }
    ''')

应用样式后,我们的天气应用界面看起来更加美观和专业了。

5. 连接按钮与功能

界面已经就绪,现在需要让按钮点击后执行获取天气的功能。本节我们将为按钮的点击信号连接一个自定义的槽函数。

initUI方法的末尾,添加以下代码来连接信号与槽:

    self.get_weather_button.clicked.connect(self.get_weather)

然后,我们定义get_weather方法作为槽函数。目前它只是一个占位符。

def get_weather(self):
    pass

现在,点击“Get Weather”按钮就会调用get_weather方法。接下来,我们将在这个方法里实现核心的API请求逻辑。

6. 获取并处理天气数据

这是应用的核心功能部分。我们将在get_weather方法中构造API请求URL,发送网络请求,并处理返回的JSON数据。

首先,我们需要API密钥和用户输入的城市名。

def get_weather(self):
    api_key = ‘你的API密钥’ # 请替换为你的真实API密钥
    city = self.city_input.text()

    # 构造API请求URL
    url = f‘http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}’

    try:
        # 发送GET请求
        response = requests.get(url)
        response.raise_for_status() # 如果响应状态码是4xx或5xx,则抛出HTTPError异常
        data = response.json()

        # 根据状态码处理结果
        if data[‘cod’] == 200:
            self.display_weather(data)
        else:
            # 处理API返回的非200错误(如404城市未找到)
            self.display_error(f‘Error {data[“cod”]}: {data.get(“message”, “Unknown error”)}’)

    except requests.exceptions.HTTPError as http_err:
        # 处理HTTP错误(状态码4xx, 5xx)
        self.handle_http_error(response)
    except requests.exceptions.ConnectionError:
        self.display_error(‘Connection Error\nCheck your Internet connection.’)
    except requests.exceptions.Timeout:
        self.display_error(‘Timeout Error\nThe request timed out.’)
    except requests.exceptions.RequestException as req_err:
        self.display_error(f‘Request Error\n{req_err}’)

我们使用了try...except块来捕获可能发生的各种网络和请求异常,确保程序的健壮性。

7. 处理HTTP错误状态码

当API请求返回错误状态码(如404、401等)时,我们需要向用户显示友好的错误信息。本节我们实现handle_http_error方法。

我们使用match...case语句(Python 3.10+)或if...elif链来根据不同的状态码显示不同的信息。

def handle_http_error(self, response):
    status_code = response.status_code
    match status_code:
        case 400:
            msg = ‘Bad Request\nPlease check your input.’
        case 401:
            msg = ‘Unauthorized\nInvalid API key.’
        case 403:
            msg = ‘Forbidden\nAccess is denied.’
        case 404:
            msg = ‘Not Found\nCity not found.’
        case 500:
            msg = ‘Internal Server Error\nPlease try again later.’
        case 502:
            msg = ‘Bad Gateway\nInvalid response from server.’
        case 503:
            msg = ‘Service Unavailable\nServer is down.’
        case 504:
            msg = ‘Gateway Timeout\nNo response from server.’
        case _:
            msg = f‘HTTP Error occurred\n{status_code}’
    self.display_error(msg)

这样,用户就能清楚地知道问题出在哪里,例如是网络问题、输入错误还是API密钥无效。

8. 显示错误信息

当发生错误时,我们需要在应用界面上显示错误信息,并清空可能残留的天气数据和表情符号。

display_error方法负责更新温度标签来显示错误信息,并清空其他相关标签。

def display_error(self, message):
    # 显示错误信息
    self.temperature_label.setText(message)
    # 临时调整错误信息的字体大小
    self.temperature_label.setStyleSheet(‘font-size: 30px;’)
    # 清空其他标签
    self.emoji_label.clear()
    self.description_label.clear()

9. 解析并显示天气数据

如果API请求成功(状态码为200),我们将调用display_weather方法来解析数据并更新界面。

我们需要从返回的JSON数据中提取温度、天气描述和天气ID。

def display_weather(self, data):
    # 恢复温度标签的字体大小
    self.temperature_label.setStyleSheet(‘font-size: 75px;’)

    # 提取温度(开尔文)
    temp_k = data[‘main’][‘temp’]
    # 转换为华氏度(可根据需要改为摄氏度)
    temp_f = (temp_k * 9/5) - 459.67
    self.temperature_label.setText(f‘{temp_f:.0f}°F’)

    # 提取天气描述
    weather_desc = data[‘weather’][0][‘description’].title()
    self.description_label.setText(weather_desc)

    # 提取天气ID并获取对应的表情符号
    weather_id = data[‘weather’][0][‘id’]
    emoji = self.get_weather_emoji(weather_id)
    self.emoji_label.setText(emoji)

10. 根据天气ID显示表情符号

为了增加应用的趣味性,我们根据OpenWeatherMap的天气ID来显示对应的表情符号。我们创建一个静态方法get_weather_emoji来实现这个映射关系。

@staticmethod
def get_weather_emoji(weather_id):
    if 200 <= weather_id <= 232:
        return ‘⛈️’ # 雷阵雨
    elif 300 <= weather_id <= 321:
        return ‘🌧️’ # 毛毛雨/细雨
    elif 500 <= weather_id <= 531:
        return ‘🌧️’ # 雨
    elif 600 <= weather_id <= 622:
        return ‘❄️’ # 雪
    elif 701 <= weather_id <= 741:
        return ‘🌫️’ # 雾/薄雾
    elif weather_id == 762:
        return ‘🌋’ # 火山灰
    elif weather_id == 771:
        return ‘💨’ # 飑
    elif weather_id == 781:
        return ‘🌪️’ # 龙卷风
    elif weather_id == 800:
        return ‘☀️’ # 晴
    elif 801 <= weather_id <= 804:
        return ‘☁️’ # 多云
    else:
        return ‘’ # 未知天气,返回空字符串

现在,应用会根据不同的天气状况显示生动的表情符号了。

总结

本节课中我们一起学习了如何使用Python和PyQt5构建一个完整的桌面天气应用。我们涵盖了从项目规划、API获取、GUI设计、网络请求、数据解析到异常处理的完整开发流程。

核心要点回顾

  1. GUI框架:使用PyQt5的QWidget, QLabel, QLineEdit, QPushButton等控件构建界面。
  2. 布局管理:使用QVBoxLayout进行垂直布局,并使用Qt.AlignCenter进行对齐。
  3. 样式美化:通过setStyleSheet方法应用CSS样式表来美化控件外观。
  4. 网络请求:使用requests库向OpenWeatherMap API发送GET请求获取JSON格式的天气数据。
  5. 数据处理:解析JSON数据,提取温度、描述等信息,并进行单位换算。
  6. 异常处理:使用try...except块妥善处理网络错误、HTTP状态码错误等,提升应用健壮性。
  7. 功能增强:根据天气ID映射显示对应的表情符号,提升用户体验。

你可以在此基础上继续扩展功能,例如添加多城市管理、天气预报、更详细的天气信息(湿度、风速等)或更换更精美的图标。这个项目是巩固Python GUI编程和网络请求知识的绝佳实践。

posted @ 2026-03-29 09:25  布客飞龙II  阅读(4)  评论(0)    收藏  举报