对的,这是一篇学习笔记,和上一篇没啥关系。
此刻,我写不来 Python,我也记不得多少关于 Python 的知识点。上次写 Python 代码是2017年,上次有学 Python 的想法是 2023 年,不过只学了半天。
今天,准备从0到1,快速入个门。
一个 Python 项目的典型结构入下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
project_name/ # 项目根目录
├── project_name/ # 源码包 (Package) - 核心代码存放地
│ ├── __init__.py # 标识此目录为Python包,可包含包初始化代码或定义__all__列表
│ ├── module1.py # 模块1:包含相关函数、类等
│ ├── module2.py
│ └── subpackage1/ # 子包1,用于更深层次的模块化组织
│ ├── __init__.py
│ ├── module3.py
│ └── module4.py
├── tests/ # 测试目录 - 存放所有单元测试和集成测试
│ ├── __init__.py
│ ├── test_module1.py # 针对module1的测试
│ ├── test_module2.py
│ └── test_subpackage1/ # 针对子包的测试也可以有相应目录
│ ├── __init__.py
│ └── test_module3.py
├── docs/ # 项目文档目录 - 使用Sphinx等工具生成详细文档
│ ├── conf.py
│ ├── index.rst
│ └── ...
├── scripts/ # 脚本目录 - 存放可执行脚本、工具脚本
│ ├── data_cleaning.py
│ ├── train_model.py
│ └── start_server.py
├── data/ # (可选)数据目录 - 存放项目所需或生成的数据
│ ├── input/
│ └── output/
├── examples/ # (可选)示例目录 - 存放使用项目的示例代码
├── requirements.txt # 项目依赖清单 - 列明项目运行所需的所有第三方库及其版本
├── requirements_dev.txt # (可选)开发环境额外依赖(如测试、构建、文档工具)
├── setup.py # 打包和安装脚本 - 用于将项目打包分发给其他人安装使用
├── pyproject.toml # 现代项目构建配置文件 (遵循PEP 518)
├── README.md # 项目说明文档 - 简介、安装、快速使用等
├── LICENSE # 项目许可证文件 - 明确授权条款
└── .gitignore # Git忽略规则 - 指定哪些文件/目录不应纳入版本控制
|
一个 py 文件就是一个模块,模块代码的整体布局如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
"""
This module provides some example classes and functions to demonstrate Python code layout and style.
"""
__version__ = '0.1.0'
__author__ = 'Jane Developer'
__all__ = ['MyClass', 'my_function', 'CONSTANT1']
# Standard Library Imports
import os
import sys
# Third-Party Imports
import requests
# Local Application/Library Specific Imports
from myutils.helpers import format_data
# Global constants
CONSTANT1 = 10
DEFAULT_TIMEOUT = 30
class MyClass:
"""
A brief description of MyClass.
More detailed description of the class's purpose and functionality.
"""
CLASS_CONSTANT = 'some_value'
def __init__(self, name):
self.name = name
def method_one(self):
"""Does a specific thing."""
pass
def method_two(self):
"""Does another thing."""
pass
def my_function(param1, param2=None):
"""
A brief description of what this function does.
Args:
param1 (str): Description of the first parameter.
param2 (int, optional): Description of the second parameter. Defaults to None.
Returns:
bool: Description of the return value.
Raises:
ValueError: Under what conditions it's raised.
"""
if not param1:
raise ValueError("param1 cannot be empty.")
result = do_something_complex(param1, param2)
return result
def _private_helper_function():
"""A non-public function (indicated by the leading underscore)."""
pass
# Main execution block
if __name__ == "__main__":
# Code here runs only when the module is executed directly,
# not when it is imported.
instance = MyClass("test")
result = my_function("hello")
print(result)
|
代码可以通过包和模块组织,那么跨包或者跨模块调用代码是,就需要 import。Python 里的 import 可以导入包、导入模块、可以导入任何定义在模块顶级的名称,包括变量、常量、函数、类等。
假如有这样一个项目:
1
2
3
4
5
6
|
my_project/
├── main.py # 主程序,用于演示导入
└── my_package/ # 自定义的包
├── __init__.py # 包的初始化文件
├── module_a.py # 包内的一个模块
└── module_b.py # 包内的另一个模块
|
各个文件内容如下:
my_package/__init__.py
1
2
3
4
5
6
7
8
|
print("执行 my_package 的 __init__.py 文件")
# 定义一些包级别的变量或执行初始化代码
PACKAGE_NAME = "my_package"
VERSION = "1.0"
# 可以选择将包内模块的特定内容“提升”到包级别,方便用户直接访问
# from .module_a import hello_from_a # (先注释掉,待会演示)
# from .module_b import hello_from_b # (先注释掉,待会演示)
|
my_package/module_a.py
1
2
3
4
|
print("执行 module_a.py 模块")
def hello_from_a():
return "Hello from Module A!"
|
my_package/module_b.py
1
2
3
4
|
print("执行 module_b.py 模块")
def hello_from_b():
return "Hello from Module B!"
|
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
|
print("--- 开始执行 main.py ---")
print("\n1. 导入整个包 my_package")
import my_package # 这会执行 my_package/__init__.py
print("\n2. 从包中导入特定模块 module_a")
from my_package import module_a # 由于包已被导入,__init__.py 不会再次执行,但会执行 module_a.py
print("\n3. 从模块中导入特定函数")
from my_package.module_b import hello_from_b # 导入包内模块的具体函数
print(hello_from_b()) # 调用函数
print("\n--- main.py 执行结束 ---")
|
最后的执行结果是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
--- 开始执行 main.py ---
1. 导入整个包 my_package
执行 my_package 的 __init__.py 文件
2. 从包中导入特定模块 module_a
执行 module_a.py 模块
3. 从模块中导入特定函数
执行 module_b.py 模块
Hello from Module B!
--- main.py 执行结束 ---
|
当你用 pip install 安装一个 Python 包的时候,这个三方包会安装在同一个目录里(藏着你电脑里的某个 site-packages 目录下),比如 /opt/homebrew/lib/python3.11/site-packages,那么当你本地有多个项目依赖同一个包时,这个包的版本就被迫要保持一致了。于是乎,“依赖冲突”问题、“环境隔离与污染”问题接踵而至。甚至你要运行的多个项目依赖的 Python 版本也不尽相同。
所以你需要给每个项目准备单独的 Python 环境。
Python 标准库自带的轻量级组合,简单直接。
- 管理工具: pip(包安装) + venv(环境隔离)
- 虚拟环境: 需手动创建和管理 (python -m venv .venv)
- 依赖记录: 使用 requirements.txt文件,通过 pip freeze > requirements.txt生成,通过 pip install -r requirements.txt安装。它仅记录包列表,无法保证深层依赖的版本绝对一致。
- 优点: Python 自带,无需额外安装;简单灵活。
- 缺点: 功能相对基础,依赖解析和冲突解决能力较弱;需要手动管理环境和依赖文件。
- 适用场景: 快速尝试、运行简单脚本或初学者学习
强大的开源包管理和环境管理系统,尤其擅长处理数据科学领域的复杂依赖。
- 管理工具: conda(或更快的 mamba)
- 虚拟环境: 内置,自动管理 (conda create -n myenv python=3.10)
- 依赖记录: 使用 environment.yml文件,通过 conda env export > environment.yml导出环境。
- 核心优势: 能够管理非Python依赖(如C/C++库、R语言、CUDA工具链),预构建的二进制包丰富,解决复杂科学计算依赖的能力非常突出。
- 缺点: 包体积通常较大;生态更侧重于数据科学领域。
- 适用场景: 数据科学、机器学习、生物信息学等需要复杂计算栈或跨语言环境的项目
现代化的全能工具,集依赖管理、虚拟环境、打包发布于一身的项目生命周期管理工具。
- 管理工具: poetry
- 虚拟环境: 内置,自动创建和管理(也可指定现有环境)。
- 依赖记录: 使用 pyproject.toml (PEP 621标准) 声明依赖和项目元数据,并生成 poetry.lock 文件精确锁定所有次级依赖的版本,确保环境绝对可复现。
- 核心优势: 依赖解析能力强;统一配置(一切都在 pyproject.toml中);原生支持打包和发布到PyPI;严格的项目结构建议。
- 缺点: 需要单独安装;对某些极特殊的非PyPI依赖处理稍显复杂。
- 适用场景: 纯Python的应用程序、库开发,尤其适合团队协作和追求项目规范与可复现性的场景。
实例:创建一个简单的 Web 爬虫项目
- 创建虚拟环境
1
2
3
4
5
|
mkdir py-projects
cd py-projects
mkdir my-web-crawler
cd my-web-crawler
python -m venv .venv
|
此时你的 my-web-crawler 目录内会包含如下内容:
1
2
3
4
5
|
.venv
├── bin
├── include
├── lib
└── pyvenv.cf
|
激活虚拟环境后,python 和 pip 命令就会使用 .venv 里的:
1
|
source .venv/bin/activate
|
然后 python3 命令就指向了当前项目的 venv 里的 python3
1
2
3
4
5
6
|
# ls .venv/bin
Activate.ps1 activate.csh pip pip3.11 python3
activate activate.fish pip3 python python3.11
# which python3
/Users/danielhu/Work/py-projects/my-web-crawler/.venv/bin/python3
|
- 安装依赖,编写代码
接着安装 requests 包:
此时 requests 会被安装到 .venv/lib/python3.11/site-packages/requests 路径。
新建一个 main.py 文件,写入代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import requests
from bs4 import BeautifulSoup
url = 'https://www.danielhu.cn/'
try:
response = requests.get(url)
response.raise_for_status() # 检查请求是否成功
# 使用 BeautifulSoup 解析 HTML 并获取页面标题
soup = BeautifulSoup(response.text, 'html.parser')
page_title = soup.title.string if soup.title else 'No title found'
print(f"The title of '{url}' is: {page_title}")
except requests.RequestException as e:
print(f"An error occurred while fetching the URL: {e}")
|
安装 beautifulsoup4:
1
|
pip install beautifulsoup4
|
- 执行代码
1
2
|
# python main.py
The title of 'https://www.danielhu.cn/' is: 胡涛的个人网站 | Seven Coffee Cups
|
生成依赖清单:
1
|
pip freeze > requirements.txt
|
此时 requirements.txt 内容如下:
1
2
3
4
5
6
7
8
|
beautifulsoup4==4.13.5
certifi==2025.8.3
charset-normalizer==3.4.3
idna==3.10
requests==2.32.5
soupsieve==2.8
typing_extensions==4.15.0
urllib3==2.5.0
|
这些包都在 .venv/lib/python3.11/site-packages/requests 里面。
- 退出虚拟环境
需要退出虚拟环境,可以直接执行 deactivate
- 复现环境
现在将依赖已经全部维护到了 requirements.txt 文件里,这个 requirements.txt 会跟着源代码一起上库,但是 .venv 目录显然是不适合丢到代码库的。
当你下载源代码到一个新环境的时候,就可以通过 requirements.txt 里记录依赖清单来安装依赖,将项目跑起来:
1
|
pip install -r requirements.txt
|
对于一般项目而言,pip + venv 的方式管理依赖和虚拟环境已经够用了。如果是开发机器学习相关的项目,Conda 更合适,因为可以直接管理类似 cudatoolkit 这种非 Python 语言的依赖包。
至于 Poetry,其提供了强大的依赖管理能力(包括次级依赖的版本)和打包发布能力,对于大型项目需要多人协作的场景而言,有其优势。后面有机会再具体研究 Poetry 的用法。
1
2
3
4
5
6
7
|
mkdir my-first-flask-app
cd my-first-flask-app
python -m venv .venv
source .venv/bin/activate
pip install Flask
|
创建 app.py 文件,注释里面包含了详细的解释。
部分细节(比如关于类和函数更多的讨论)后面单独展开。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
# 导入模块:从 flask 包里导入了 Flask 类和 render_template 函数
from flask import Flask, render_template
# 创建一个 Flask 应用实例,这是构建任何 Flask 应用的第一步。
# `__name__` 是一个特殊的 Python 变量,它表示当前模块的名称。
# Python 里的模块名也就是 py 源文件的文件名,比如 app.py 这个文件的模块名就是 app
app = Flask(__name__)
# 定义常量,约定不能修改,但这不是强限制,只是约定俗成。命名风格为大写,下划线连接。
WELCOME_MESSAGE = "Hello, Python and Flask World!"
# 定义变量,命名风格为小写,下划线连接。
website_visits = 0
# 定义一个函数。
def generate_greeting(name):
"""
这是一个生成问候语的函数。
它接受一个参数 `name`,并返回一个拼接好的字符串。
"""
# 字符串拼接:使用加号 (+) 或 f-string(更现代、推荐的方式)将变量嵌入字符串。
return f"Hello, {name}! Welcome to your first Flask app."
# 使用装饰器定义路由:`@app.route('/')` 告诉 Flask,当用户访问网站根目录时,由下面的函数处理。
@app.route('/')
def hello_world():
# 使用 `global` 关键字声明我们要修改的是在函数外部定义的全局变量 `website_visits`。
global website_visits
website_visits += 1
# 1. 调用函数,将返回值赋给变量 `greeting`
greeting = generate_greeting("New Developer")
# 2. 使用条件语句 (if-elif-else)
# 根据访问次数显示不同的信息
if website_visits == 1:
visit_message = "This is your first visit. Awesome!"
elif website_visits < 5:
visit_message = f"You have visited this page {website_visits} times."
else:
visit_message = f"Wow! You've been here {website_visits} times! "
# 3. 使用列表(一种Python数据结构)和循环 (for loop)
features = [
"Variables and Constants",
"Conditional Statements (if/elif/else)",
"Loops (for)",
"Functions",
"Importing Modules"
]
# 创建一个空列表,用于存储处理后的功能项
numbered_features = []
# 使用 `enumerate` 在循环中获取每个元素的索引和值
for index, item in enumerate(features, start=1):
# 在循环内部进行条件判断和字符串操作
featured_item = f"Feature {index}: {item}"
if "Function" in item: # 检查字符串中是否包含"Function"
featured_item += " 👩💻" # 如果包含,就添加一个表情符号
numbered_features.append(featured_item) # 将处理好的字符串添加到新列表中
# 使用 `render_template` 函数将数据渲染到HTML模板中。
return render_template('index.html',
welcome_message=WELCOME_MESSAGE,
greeting=greeting,
visit_message=visit_message,
features_list=numbered_features)
# 这是Python中常见的惯用法。
# 它确保只有当这个脚本被直接运行时(而不是被其他模块导入时),Flask开发服务器才会启动。
if __name__ == '__main__':
# 启动Flask自带的开发服务器,`debug=True` 开启了调试模式,方便开发时看到错误详情。
app.run(debug=True)
|
Flask 使用 Jinja2 模板引擎来动态生成 HTML。创建一个名为 templates的文件夹,然后在里面创建一个 index.html 文件。
(这块内容不重要,只是为了展示完整的 Flask 应用跑起来的效果。实际项目里基本是前后端分离,html 代码轮不到 Python 来渲染。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Flask App - Learning Python</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }
h1 { color: #333; }
h2 { color: #555; }
ul { list-style-type: none; padding-left: 0; }
li { margin-bottom: 10px; padding: 8px; background-color: #f4f4f4; border-left: 4px solid #007acc; }
</style>
</head>
<body>
<h1>{{ welcome_message }}</h1> <!-- 双花括号用于输出变量的值 -->
<p><strong>{{ greeting }}</strong></p>
<p><em>{{ visit_message }}</em></p>
<h2>Python Basics You're Seeing in Action:</h2>
<ul>
<!-- 这是在模板中使用 for 循环,遍历我们传递过来的 features_list 列表 -->
{% for feature in features_list %}
<li>{{ feature }}</li> <!-- 循环内的每一次迭代都会生成一个 <li> 标签 -->
{% endfor %} <!-- 循环结束 -->
</ul>
</body>
</html>
|
最后执行 python app.py 就可以将这个应用启动起来,实际浏览器访问结果就不贴截图了。
这里涉及到的循环、函数、类,看起来还有很多知识点有待进一步学习。下面继续挨个看。
Python 中的可迭代对象 (Iterable) 是指那些能够一次返回一个元素,并且可以被 for 循环遍历的对象。在继续详细学习 for 循环之前,我们先来看“可迭代对象”有哪些:
| 类型 |
示例 |
for 循环遍历结果 |
说明 |
| 列表 (List) |
[1, 2, 3] |
逐个返回元素 1, 2, 3 |
有序,可变 |
| 元组 (Tuple) |
(1, 2, 3) |
逐个返回元素 1, 2, 3 |
有序,不可变 |
| 字符串 (String) |
"abc" |
逐个返回字符 'a', 'b', 'c' |
有序,不可变 |
| 字典 (Dict) |
{'a': 1, 'b': 2} |
默认逐个返回键 (key) 'a', 'b' |
无序 (Python 3.6+ 后有序),也可用 .values() 迭代值,.items() 迭代键值对 |
| 集合 (Set) |
{1, 2, 3} |
逐个返回元素 1, 2, 3 (顺序不定) |
无序,元素唯一 |
| range 对象 |
range(5) |
逐个返回整数 0, 1, 2, 3, 4 |
惰性生成序列,节省内存 |
| 生成器 (Generator) |
(x for x in range(3)) 或 带 yield 的函数 |
逐个返回值 0, 1, 2 |
惰性计算,极其节省内存,适合大量或无限数据 |
| 文件对象 (File Object) |
open('file.txt') |
逐行返回文件内容 |
推荐用 with 语句打开 |
| enumerate 对象 |
enumerate(['a', 'b']) |
返回索引-元素对 (0, 'a'), (1, 'b') |
常用于获取元素及其索引 |
| zip 对象 |
zip([1, 2], ['a', 'b']) |
返回元组 (1, 'a'), (2, 'b') |
将多个可迭代对象“压缩”并行迭代 |
| 字典视图对象 |
dict.keys(), dict.values(), dict.items() |
分别迭代键、值或 (键, 值) 对 |
动态反映字典的变化 |
见下例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# 综合演示 Python 循环特性
# 1. 基本的 for 循环:遍历序列(如列表)
fruits = ["apple", "banana", "cherry"]
for fruit in fruits: # 逐个取出列表中的元素
print(fruit, end=' ') # 输出: apple banana cherry
print("\n" + "-"*40)
# 2. 使用 range() 控制循环次数
for i in range(3): # 生成序列 0, 1, 2
print(f"Index: {i}", end=' ') # 输出: Index: 0 Index: 1 Index: 2
print("\n" + "-"*40)
# 3. 使用 break 提前终止循环
for num in range(5):
if num == 3:
break # 立即退出整个循环
print(num, end=' ') # 输出: 0 1 2
print("\n" + "-"*40)
# 4. 使用 continue 跳过当前迭代
for num in range(5):
if num == 2:
continue # 跳过本次循环剩余代码,进入下一次
print(num, end=' ') # 输出: 0 1 3 4 (跳过了2)
print("\n" + "-"*40)
# 5. 循环中的 else 子句
for i in range(3):
print(i, end=' ')
else: # 当循环正常结束(未被 break 中断)时执行
print("-> Loop completed normally") # 会执行
print("-"*40)
# 搜索示例:break 后 else 不会执行
search_list = [1, 2, 3, 4, 5]
target = 3
for item in search_list:
if item == target:
print(f"Found {target}!")
break
else: # 由于循环被 break 终止,这里的 else 不会执行
print(f"{target} not found.")
print("-"*40)
# 6. While 循环:基于条件重复
count = 0
while count < 3: # 当条件为 True 时执行
print(f"Count: {count}", end=' ')
count += 1 # 更新条件变量,避免无限循环
print("\n" + "-"*40)
# 7. 嵌套循环
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix: # 外层循环遍历每一行
for element in row: # 内层循环遍历行中的每个元素
print(element, end=' ')
print() # 每行结束后换行
# 输出:
# 1 2 3
# 4 5 6
# 7 8 9
print("-"*40)
# 8. 使用 enumerate() 获取索引和值
fruits = ["apple", "banana", "mango"]
for index, fruit in enumerate(fruits): # 同时获取索引和值
print(f"Index {index}: {fruit}")
# 输出:
# Index 0: apple
# Index 1: banana
# Index 2: mango
print("-"*40)
# 9. 遍历字典
person = {"name": "Alice", "age": 30, "city": "New York"}
for key in person: # 遍历键
print(key, end=' ') # 输出: name age city
print()
for key, value in person.items(): # 同时遍历键和值
print(f"{key}: {value}")
# 输出:
# name: Alice
# age: 30
# city: New York
print("-"*40)
# 10. 使用 zip() 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages): # 将两个列表对应元素组合
print(f"{name} is {age} years old.")
# 输出:
# Alice is 25 years old.
# Bob is 30 years old.
# Charlie is 35 years old.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 基础:快速生成列表
squares = [x**2 for x in range(5)] # 计算 0-4 的平方
print(squares) # 输出: [0, 1, 4, 9, 16]
# 带条件过滤:只处理满足条件的元素
even_squares = [x**2 for x in range(10) if x % 2 == 0] # 只计算偶数的平方
print(even_squares) # 输出: [0, 4, 16, 36, 64]
# 嵌套循环:模拟笛卡尔积
pairs = [(x, y) for x in ['A', 'B'] for y in [1, 2]] # 组合所有可能
print(pairs) # 输出: [('A', 1), ('A', 2), ('B', 1), ('B', 2)]
# 处理字符串:将列表中的单词转为大写
words = ["hello", "world", "python"]
upper_words = [word.upper() for word in words]
print(upper_words) # 输出: ['HELLO', 'WORLD', 'PYTHON']
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# 快速创建字典:数字映射到它的平方
square_dict = {x: x**2 for x in range(5)}
print(square_dict) # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 交换键值对
original_dict = {'a': 1, 'b': 2, 'c': 3}
swapped_dict = {v: k for k, v in original_dict.items()}
print(swapped_dict) # 输出: {1: 'a', 2: 'b', 3: 'c'}
# 基于条件过滤字典项
filtered_dict = {k: v for k, v in original_dict.items() if v > 1}
print(filtered_dict) # 输出: {'b': 2, 'c': 3}
|
1
2
3
4
5
6
7
8
9
|
# 从列表中创建去重后的集合
numbers = [1, 2, 2, 3, 4, 4, 4, 5]
unique_squares = {x**2 for x in numbers} # 计算平方并去重
print(unique_squares) # 输出: {1, 4, 9, 16, 25}
# 从字符串中创建不重复的字符集合
word = "mississippi"
unique_letters = {char for char in word}
print(unique_letters) # 输出: {'i', 'm', 'p', 's'}
|
Python 中的迭代器 (Iterator) 是用于惰性遍历数据集合的对象,它一次只产生一个元素,以实现内存节省。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# 创建一个自定义迭代器,生成一个有限的数字序列
class MyRangeIterator:
def __init__(self, start, end):
self.current = start # 初始化当前值,用于记录状态
self.end = end # 记录终点值
def __iter__(self):
# __iter__ 方法只需返回迭代器对象本身
return self
def __next__(self):
# __next__ 方法负责返回下一个值,并更新状态
if self.current >= self.end:
# 当没有更多元素时,必须抛出 StopIteration 异常来终止迭代[5](@ref)
raise StopIteration
else:
value = self.current
self.current += 1 # 更新状态,指向下一个元素
return value
# 使用自定义迭代器
my_iterator = MyRangeIterator(1, 4) # 创建一个生成 1, 2, 3 的迭代器
print("手动调用 next():")
print(next(my_iterator)) # 输出: 1 (next() 会调用 __next__ 方法)
print(next(my_iterator)) # 输出: 2
print(next(my_iterator)) # 输出: 3
# print(next(my_iterator)) # 如果取消注释,会抛出 StopIteration 异常
# 更常见的用法:用于 for 循环(for 循环会自动处理 StopIteration)
print("\n用于 for 循环:")
for num in MyRangeIterator(1, 4):
print(num) # 输出: 1, 2, 3
|
列表这类可迭代对象 (Iterable) 本身不是迭代器 (Iterator),但可以通过 iter()函数转换为迭代器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 列表是可迭代对象,但不是迭代器
my_list = [1, 2, 3, 4, 5]
print(hasattr(my_list, '__iter__')) # True,是可迭代对象
print(hasattr(my_list, '__next__')) # False,不是迭代器本身
# 使用iter()函数从列表获取迭代器
list_iterator = iter(my_list)
# 现在可以使用next()遍历
print(next(list_iterator)) # 输出: 1
print(next(list_iterator)) # 输出: 2
# 也可以用于for循环(for循环内部会自动处理迭代器)
for num in list_iterator:
print(num) # 输出: 3, 4, 5(继续从之前的位置)
|
元组、字符串、字典、集合等,都可以通过 iter() 转换为迭代器。
生成器是一种特殊的迭代器,使用yield关键字实现,自动满足迭代器协议。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 用生成器函数实现类似的MyRange功能
def my_range_generator(start, end):
current = start
while current < end:
yield current # 使用yield而不是return
current += 1
# 使用生成器
gen = my_range_generator(1, 4)
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
# print(next(gen)) # 再次调用会抛出StopIteration
# 生成器可以直接用于for循环
for num in my_range_generator(1, 4):
print(num) # 输出: 1, 2, 3
|
生成器表达式类似列表推导式,但使用圆括号并返回生成器对象。
1
2
3
4
5
6
7
8
9
10
11
12
|
# 列表推导式:立即计算所有值
list_comp = [x * 2 for x in range(5)] # [0, 2, 4, 6, 8]
# 生成器表达式:惰性计算,按需生成值
gen_exp = (x * 2 for x in range(5))
print(next(gen_exp)) # 输出: 0
print(next(gen_exp)) # 输出: 2
# 内存占用对比(对于大数据集尤其重要)
import sys
print(sys.getsizeof([x for x in range(1000000)])) # 约8.4MB
print(sys.getsizeof((x for x in range(1000000)))) # 约120字节
|
迭代器和生成器都是惰性计算,内存消耗低,两者都是一次性使用,遍历完之后不能二次遍历,但是相比之下生成器由于函数可以重新调用,使用起来要方便一些。
Python标准库提供了强大的itertools模块,包含许多有用的迭代器函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import itertools
# 无限迭代器示例
# 1. 无限计数器
counter = itertools.count(start=10, step=2)
print(next(counter)) # 10
print(next(counter)) # 12
# 2. 循环迭代
cycler = itertools.cycle(['A', 'B', 'C'])
print(next(cycler)) # 'A'
print(next(cycler)) # 'B'
# 3. 有限重复
repeater = itertools.repeat('Hello', times=3)
print(next(repeater)) # 'Hello'
print(next(repeater)) # 'Hello'
# 组合迭代器
# 1. 链式迭代:连接多个可迭代对象
chained = itertools.chain([1, 2], ['a', 'b'], [3, 4])
print(list(chained)) # [1, 2, 'a', 'b', 3, 4]
# 2. 切片迭代:对迭代器进行切片
sliced = itertools.islice(itertools.count(), 5, 10, 2)
print(list(sliced)) # [5, 7, 9]
|
生成器不仅可以从外部获取值,还可以通过send()方法接收外部传入的值。
1
2
3
4
5
6
7
8
9
10
11
|
def interactive_generator():
"""一个可以交互的生成器示例"""
value = yield "开始" # 第一次yield
while True:
value = yield f'收到: {value}' # 后续yield并接收值
# 使用示例
gen = interactive_generator()
print(next(gen)) # 输出: "开始"(初始启动)
print(gen.send(42)) # 输出: "收到: 42"
print(gen.send('你好')) # 输出: "收到: 你好"
|
Python 的函数声明里没有类型,没有返回值,看惯了静态类型语言(比如 Golang)再看 Python 函数总觉得少了点什么。动态类型语言的类型在运行时确定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
def demonstrate_arguments(a, b=2, *args, **kwargs):
"""
演示多种参数传递方式:
- a: 位置参数 (必需)
- b: 默认参数 (可选,默认为2)
- *args: 可变位置参数 (接收元组)
- **kwargs: 可变关键字参数 (接收字典)
"""
print(f"位置参数 a: {a}")
print(f"默认参数 b: {b}")
if args:
print(f"可变位置参数 args: {args}")
if kwargs:
print(f"可变关键字参数 kwargs: {kwargs}")
print("-" * 20)
# 1. 只传递必需的位置参数
demonstrate_arguments(1) # a=1, b使用默认值2
# 2. 传递位置参数和覆盖默认参数
demonstrate_arguments(1, 3) # a=1, b=3
# 3. 使用关键字参数指定,顺序可调整
demonstrate_arguments(b=4, a=2) # a=2, b=4
# 4. 传递额外的位置参数和关键字参数
demonstrate_arguments(1, 2, 3, 4, name="Alice", age=25)
# a=1, b=2, args=(3, 4), kwargs={'name': 'Alice', 'age': 25}
|
装饰器(Decorator)可以在不修改原函数代码的情况下,为其添加新功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
import time
from functools import wraps
# 1. 一个简单的计时装饰器
def timing_decorator(func):
"""记录函数运行时间的装饰器"""
@wraps(func) # 使用wraps保留原函数的元信息(如函数名);反之装饰后的函数 name 会变成 wrapper
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs) # 执行原函数
end_time = time.time()
print(f"函数 {func.__name__} 运行耗时: {end_time - start_time:.4f} 秒")
return result
return wrapper
# 2. 一个带参数的装饰器(创建需要不同权限的装饰器)
def requires_permission(permission_level):
"""检查用户权限的装饰器工厂函数"""
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('permission', 0) >= permission_level: # 字典的 get 用法,0 是默认值
return func(user, *args, **kwargs)
else:
print(f"权限不足!需要 {permission_level} 级权限,当前为 {user.get('permission', 0)} 级。")
return None
return wrapper
return decorator
# 使用装饰器
@timing_decorator
@requires_permission(permission_level=2) # 应用带参数的装饰器
def process_data(user, data_size):
"""模拟一个需要一定权限且耗时的数据处理函数"""
print(f"用户 {user['name']} 开始处理 {data_size} MB 数据...")
time.sleep(1) # 模拟耗时操作
return f"已处理 {data_size} MB 数据"
# 调用被装饰的函数
admin_user = {'name': 'Alice', 'permission': 2}
result = process_data(admin_user, 100)
print(f"结果: {result}")
|
类型注解为函数参数和返回值添加预期的类型说明,提高代码可读性和可维护性,并方便IDE进行智能提示和静态类型检查。类型注解本身不会在运行时影响程序的执行或进行强制类型检查。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
from typing import List, Dict, Tuple, Optional, Union
def process_user_data(
user_id: int, # 参数 user_id 应为整数类型
names: List[str], # 参数 names 应为字符串列表
metadata: Optional[Dict[str, Union[str, int]]] = None # 参数 metadata 是可选的字典,其值为字符串或整数,默认为None
) -> Tuple[bool, str]:
"""
处理用户数据的功能示例
参数:
user_id: 用户ID,整数类型
names: 用户可能的名字列表,字符串列表类型
metadata: 用户元数据,可选的字典型参数,默认为None
返回:
一个元组,包含一个布尔值(成功与否)和一个字符串(消息或错误信息)
"""
if not isinstance(user_id, int) or user_id <= 0:
return False, "无效的用户ID"
if not names or not all(isinstance(name, str) for name in names):
return False, "名字列表必须包含非空字符串"
print(f"处理用户 {user_id}: 主要名字 - {names[0]}")
if metadata:
print(f"元数据: {metadata}")
return True, "处理成功"
# 调用示例
success, message = process_user_data(
user_id=123,
names=["Alice", "Alicia"],
metadata={"age": 30, "role": "admin"}
)
print(f"执行结果: {success}, 信息: {message}")
# 静态类型检查工具(如mypy)可能会对下面的调用提出警告
# result = process_user_data("not_a_number", []) # 参数类型不匹配
# result = process_user_data(123, [1, 2]) # 列表元素类型不匹配
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
class Car:
"""一个模拟汽车的类"""
# 类属性 (所有实例共享)
wheels = 4 # 所有汽车通常都有4个轮子
def __init__(self, brand, model, year):
"""构造器方法,当创建类的新实例时自动调用"""
# 实例属性 (每个实例独有)
self.brand = brand # 品牌
self.model = model # 型号
self.year = year # 年份
self.mileage = 0 # 里程数,初始化为0
self.is_running = False # 引擎状态,初始化为False
def start_engine(self):
"""实例方法:启动引擎"""
if not self.is_running:
self.is_running = True
print(f"{self.brand} {self.model}的引擎已启动。")
else:
print("引擎已经在运行中。")
def stop_engine(self):
"""实例方法:停止引擎"""
if self.is_running:
self.is_running = False
print(f"{self.brand} {self.model}的引擎已停止。")
else:
print("引擎已经处于停止状态。")
def drive(self, distance):
"""实例方法:模拟驾驶汽车,增加里程数"""
if self.is_running:
self.mileage += distance
print(f"{self.brand} {self.model}行驶了{distance}公里。")
else:
print("请先启动引擎!")
def get_info(self):
"""实例方法:返回汽车的基本信息"""
return (f"这是一辆{self.year}年的{self.brand} {self.model},"
f"已行驶{self.mileage}公里。")
def honk(self):
"""实例方法:鸣笛"""
print(f"{self.brand} {self.model}: 嘀嘀!")
# 实例化Car类,创建两个汽车对象
my_car = Car("Toyota", "Corolla", 2022)
your_car = Car("Tesla", "Model 3", 2023)
# 访问属性
print(f"我的车是{my_car.brand} {my_car.model}。") # 输出: 我的车是Tesla。
print(f"你的车有{your_car.wheels}个轮子。") # 输出: 你的车有4个轮子。(访问类属性)
print(f"出厂年份: {my_car.year}") # 输出: 出厂年份: 2022
# 调用方法
my_car.start_engine() # 输出: Tesla的引擎已启动。
my_car.drive(50) # 输出: Tesla行驶了50公里。
my_car.drive(30) # 输出: Tesla行驶了30公里。
print(my_car.get_info()) # 输出: 这是一辆2022年的Tesla,已行驶80公里。
my_car.honk() # 输出: Tesla: 嘀嘀!
my_car.stop_engine() # 输出: Tesla的引擎已停止。
# 每个对象的状态是独立的
print(f"My car mileage: {my_car.mileage}") # 输出: My car mileage: 80
print(f"Your car mileage: {your_car.mileage}") # 输出: Your car mileage: 0 (你的车还没开过)
# 修改属性
my_car.mileage = 100 # 直接修改属性
print(f"里程表被调整了: {my_car.mileage}公里") # 输出: 里程表被调整了: 100公里
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
class Vehicle:
"""交通工具基类"""
def __init__(self, vehicle_type):
self.vehicle_type = vehicle_type # 所有交通工具都有类型
def start(self):
print(f"{self.vehicle_type}已启动。")
def stop(self):
print(f"{self.vehicle_type}已停止。")
class Car(Vehicle): # Car 继承自 Vehicle
"""汽车类,继承自Vehicle"""
def __init__(self, brand, model):
# 调用父类Vehicle的__init__方法,将类型固定为"汽车"
super().__init__("汽车")
self.brand = brand
self.model = model
def accelerate(self):
print(f"{self.brand} {self.model}正在加速。")
def get_info(self):
return f"这是一辆{self.brand} {self.model}。"
class Tesla(Car): # Tesla 继承自 Car
"""Tesla电动车类,继承自Car"""
def __init__(self, model, battery_capacity):
# 调用父类Car的__init__方法,品牌固定为"Tesla"
super().__init__("Tesla", model)
self.battery_capacity = battery_capacity # Tesla特有的属性:电池容量
# 方法重写 (Override): Tesla的加速行为与普通汽车不同
def accelerate(self):
print(f"Tesla {self.model}正在无声地电驱加速,续航{self.battery_capacity}kWh。")
# Tesla特有的方法
def autopilot(self):
print(f"Tesla {self.model}已开启自动驾驶辅助。")
# 使用继承链
my_tesla = Tesla("Model S", 100)
my_tesla.start() # 继承自Vehicle的方法: 输出: 汽车已启动。
print(my_tesla.get_info()) # 继承自Car的方法: 输出: 这是一辆Tesla Model S。
my_tesla.accelerate() # 调用重写后的方法: 输出: Tesla Model S正在无声地电驱加速,续航100kWh。
my_tesla.autopilot() # 调用Tesla特有的方法: 输出: Tesla Model S已开启自动驾驶辅助。
my_tesla.stop() # 继承自Vehicle的方法: 输出: 汽车已停止。
|