极简Python速查备忘录,有Java基础但长时间没用Python用来回顾复习。
1. 核心语法 (与Java的区别)
- 缩进即大括号:代码块靠缩进(通常4空格)区分,不用
{}。行末必须加:。 - 无分号:行尾不需要
;。 - 动态强类型:变量无需声明类型,运行时确定;类型不匹配会报错(不会隐式强转)。
- 一切皆对象:连
int、bool、函数都是对象,传递时传引用。 - 逻辑运算符:
&&||!->and、or、not。 - 三目:
条件 ? 真 : 假->真 if 条件 else 假。 - 主入口:Java是
main方法,Python是文件级判断if __name__ == "__main__":。
2. 基本数据类型
| 类别 | Python写法 | Java对比备注 (关键) |
|---|---|---|
| 整数 | int | 无长度限制(不会溢出)。与Java BigInteger类似。 |
| 浮点 | float | 对应Java double(只有双精度)。 |
| 布尔 | bool (值为 True / False) | 首字母大写,且是 int 的子类(True=1)。 |
| 空值 | None | 对应Java null。 |
| 字符串 | str | 不可变。可用单引号 ' 或双引号 ",无char类型(单字符也是str)。 |
字符串必记语法:
- f-string:
f"Hello {name}"(最强格式化,相当于Java模板字符串)。- 原始字符串:
r"C:\Users"(忽略转义,相当于Java不处理反斜杠)。- 多行字符串:
"""..."""。
3. 核心容器数据结构
| 类型 | 语法 | 可变性 | 对应Java | 关键特性 |
|---|---|---|---|---|
| 列表 (List) | [1, 2, 3] | 可变 | ArrayList | 可存任意类型,动态长度。 |
| 元组 (Tuple) | (1, 2, 3) 或 1,2,3 | 不可变 | List.of(...) 不可变版 | 性能优于列表,可作为字典键。 |
| 字典 (Dict) | {"a": 1, "b": 2} | 可变 | HashMap | 键可为任意不可变类型(不仅是字符串)。 |
| 集合 (Set) | {1, 2, 3} | 可变 | HashSet | 无序、去重。 |
易忘点:空字典是
{},空集合不能用{}(那是空字典),必须用set()。
4. 常用语法片段
条件判断(无 switch,用 elif):
if x > 0:
pass # 占位符,相当于空语句
elif x == 0:
print("zero")
else:
pass循环(无 Java 的 for(int i;;),只有 for-in):
# 遍历数字 0~4
for i in range(5): # range(5) -> [0,1,2,3,4]
print(i)
# 遍历列表
for item in my_list:
print(item)
# 带索引遍历(Java的带下标for)
for idx, value in enumerate(my_list):
print(idx, value)
# while循环(同Java)
while condition:
pass函数定义(无重载,同名函数后者覆盖前者):
def add(a, b=0): # 默认参数(相当于Java重载简写)
return a + b
def greet(name: str) -> str: # 类型注解(仅提示,不强制,FastAPI用)
return f"Hello {name}"
# 可变参数 (*args 收元组, **kwargs 收字典)
def test(*args, **kwargs):
pass类与对象(无 new 关键字,构造器用 __init__):
class Person:
# 类变量(相当于Java static)
species = "Human"
# 构造器(self 相当于 Java 的 this,但必须显式写在形参第一位)
def __init__(self, name):
self.name = name # 实例变量直接动态挂载
def say(self):
return self.name
# 实例化:p = Person("Tom") # 不用 new5. 异常处理(与Java异同)
- 语法:
try...except...else...finally(Java是catch,Python用except)。 - 捕获所有异常:
except Exception as e:(记得加as e取实例)。 - 没有
throws关键字(函数签名不需要声明抛出异常,全靠自觉)。
try:
risky()
except ZeroDivisionError as e:
print(e)
else: # 没异常时执行
print("ok")
finally:
print("clean")6. Python 独有的实用特性(捡回来用)
- 切片 (Slice):
list[0:10:2](起始:结束:步长),字符串、元组通用。 - 解包 (Unpacking):
a, b = (1, 2)或a, *rest = [1,2,3](Java无此语法)。 - 列表推导式 (Comprehension):
[x*2 for x in range(10) if x%2==0](秒杀Java流式,常用)。 - 链式比较:
if 0 < a < 10:(Java必须拆成&&)。
备注
- 忘记类型?用
type(var)查看。 - 忘记方法?用
dir(var)查看所有属性和方法,用help(var)查看文档。 - 导包:
import math或from math import sqrt(相当于Java的import static)。 - 变量作用域:函数内默认读写局部变量,要修改外部全局变量需加
global,修改嵌套函数变量需加nonlocal(这个容易忘,遇到闭包再查)。
Python中的类型提示
Python 本身不强制要求定义类型,但在 FastAPI 中,强烈建议(且必须)定义类型才能发挥其核心功能。
1. Python 语言的本质:动态类型(不需要定义)
从 Python 语言的底层机制来说,变量确实不需要定义类型。以下代码完全合法且能正常运行:
# 变量可以随时改变类型
a = 1 # 现在是 int
a = "hello" # 现在是 str
a = [1, 2, 3] # 现在是 list因为在运行时,Python 解释器只关心对象本身的类型,不关心变量声明时的类型。所以,如果你写纯 Python 脚本,不定义类型完全 OK。
2. Python 的类型提示(Type Hints):可选定义(不需要强制)
Python 3.5+ 引入了类型提示(如 def read_item(item_id: int)),但这仅仅是“注解”,不会影响代码的实际运行逻辑。
- 如果你写了
item_id: int,但传入了字符串"abc",Python 解释器不会报错,程序依然会跑(除非代码内部做了类型校验)。 - 它主要服务于代码编辑器(如 VSCode、PyCharm)的智能补全和静态检查工具(如
mypy)。
3. 为什么在 FastAPI 中必须定义类型?
虽然 Python 不强制,但 FastAPI 强依赖这些类型注解来实现核心功能。如果你不在 FastAPI 的路由中定义类型,它会失去两大杀手锏:
- 请求数据的自动校验与转换:当你定义
item_id: int时,FastAPI 会自动把 URL 中的字符串"123"转为整数123,如果传了"abc"它会自动返回 422 校验错误(无需你写try...except)。 - 自动生成交互式 API 文档(Swagger UI 和 ReDoc):FastAPI 会读取这些类型注解,自动生成清晰的参数说明和 JSON Schema。
结论:在 FastAPI 中,为了享受自动校验和文档生成,你“必须”定义类型,否则框架不知道如何解析和验证传入的数据。
为了更直观,举一个反例:
# 不定义类型(虽然 Python 能跑,但 FastAPI 失去校验能力)
@app.get("/items/{item_id}")
def read_item(item_id): # 没写类型
return {"item_id": item_id, "double": item_id * 2}
# 如果访问 /items/5,返回 {"item_id":"5","double":"55"}(字符串乘法,不是你想要的)# 定义类型(FastAPI 生效)
@app.get("/items/{item_id}")
def read_item(item_id: int): # 写了 int
return {"item_id": item_id, "double": item_id * 2}
# 如果访问 /items/5,返回 {"item_id":5,"double":10}(整数乘法,符合预期)总结一句话:
- 纯 Python 环境:数据类型不需要定义。
- FastAPI 环境:数据类型的定义不是给 Python 解释器看的,而是给 FastAPI 框架做校验和文档用的。为了框架正常运转,必须定义。
遍历集合时直接删除元素,导致集合大小变化,破坏迭代器
student_name = input("请输入需要删除的学生姓名:")
for student in student_set:
if student.name == student_name:
student_set.remove(student)
print("删除成功!")控制台信息如下:
请输入需要使用的教务系统功能序号:3
请输入需要删除的学生姓名:闫金超
删除成功!
Traceback (most recent call last):
File "D:\PythonProject\教务系统\edumanagement.py", line 49, in <module>
for student in student_set:
RuntimeError: Set changed size during iteration核心原因是:在遍历集合(set)的过程中直接修改了集合的大小(删除元素),会破坏迭代器的正常工作,导致 Python 抛出运行时错误
解决方案
安全的做法是:遍历集合的副本(而不是原集合),或者先找到要删除的元素,遍历结束后再执行删除操作。下面提供两种可行的修复方案:
方案1:遍历集合的副本(推荐)
student_name = input("请输入需要删除的学生姓名:")
# 遍历集合的副本(通过list()转换生成新列表),避免迭代时修改原集合
for student in list(student_set):
if student.name == student_name:
student_set.remove(student)
print("删除成功!")
# 找到并删除后可直接退出循环,提升效率
break
else:
# 如果循环正常结束(没触发break),说明没找到该学生
print("未找到该学生,删除失败!")方案2:先查找再删除
student_name = input("请输入需要删除的学生姓名:")
# 先找到要删除的学生对象
target_student = None
for student in student_set:
if student.name == student_name:
target_student = student
break
# 遍历结束后再执行删除操作
if target_student:
student_set.remove(target_student)
print("删除成功!")
else:
print("未找到该学生,删除失败!")关键代码解释
list(student_set):将原集合转换为列表,遍历的是这个静态的列表副本,即使原集合删除元素,也不会影响当前的遍历过程。break:找到目标学生并删除后,立即退出循环,避免不必要的遍历(如果集合中有多个同名学生,可根据需求决定是否保留break)。else分支(for-else结构):当循环正常结束(未触发break)时执行,用于处理“未找到学生”的场景,提升程序的健壮性。
总结
- 核心问题:遍历集合时直接删除元素,导致集合大小变化,破坏迭代器。
- 解决思路:遍历集合的副本,或先查找目标元素、遍历结束后再删除。
- 额外优化:增加“未找到学生”的提示,让程序逻辑更完整。
for - else 与 while - else
student_name = input("请输入需要删除的学生姓名:")
# 遍历集合的副本(通过list()转换生成新列表),避免迭代时修改原集合
for student in list(student_set):
if student.name == student_name:
student_set.remove(student)
print("删除成功!")
# 找到并删除后可直接退出循环,提升效率
break
else:
# 如果循环正常结束(没触发break),说明没找到该学生
print("未找到该学生,删除失败!")**else**与**for**是一体的,并不是只有**if - else**的结合
一、先彻底理解for...else的逻辑
for...else的核心规则:
for块:正常执行循环体(遍历、处理元素)。else块:只有当for循环完整执行完毕(没有被break中断)时,才会执行。
这段代码的逻辑:
- 遍历学生集合的副本,逐个检查姓名是否匹配。
- 如果找到匹配的学生:执行
remove→ 打印“删除成功” →break(强制终止循环)→else块不会执行。 - 如果遍历完所有学生都没找到匹配的:循环完整执行(没有触发break)→
else块执行 → 打印“未找到该学生”。
二、例子直观对比
场景1:找到元素,触发break → else不执行
student_set = ["张三", "李四", "王五"]
student_name = "李四"
for student in list(student_set):
if student == student_name:
student_set.remove(student)
print("删除成功!")
break
else:
print("未找到该学生,删除失败!")
# 输出:删除成功!场景2:没找到元素,循环完整执行 → else执行
student_set = ["张三", "李四", "王五"]
student_name = "赵六"
for student in list(student_set):
if student == student_name:
student_set.remove(student)
print("删除成功!")
break
else:
print("未找到该学生,删除失败!")
# 输出:未找到该学生,删除失败!三、为什么else和for缩进一致?
在Python中,缩进代表代码块的归属:
for关键字后面的缩进块(if/remove/print/break)是循环体,属于for的执行逻辑。else与for缩进一致,说明它不是循环体的一部分,而是和for绑定的同级结构(就像if...else中else和if缩进一致)。
可以把for...else理解为一个“组合结构”:
for 变量 in 可迭代对象:
循环体(满足条件时执行)
else:
循环正常结束(未被break)时执行的代码四、语法的常见用途
除了“查找并删除/修改”场景,for...else还常用于:
- 检查列表中是否存在某个元素(替代额外的标志变量)。
- 遍历完所有数据后执行收尾操作(如“未找到匹配项”提示)。
- 避免写冗余的
found = False这类标志变量,让代码更简洁。
总结
for...else是Python专属语法,else不依赖if,而是和for绑定,缩进与for一致。else块的执行条件:for循环完整执行完毕,没有被break中断。- 代码中的
else作用:专门处理“遍历完所有学生都没找到目标”的场景,比单独用标志变量更简洁。
这个语法刚开始接触会觉得“反直觉”,但理解后会发现它能让代码更优雅,尤其是处理“查找-判断”类逻辑时特别好用。
关于TypedDict类型的字典与普通字典的区别
我们可以依据需求使用TypedDict进行自定义的字典类型,让我们在开发的时候可以看到友好提示以及提醒。
它就是一个**“定制化的、带有使用说明书的”**字典。格式如下:
from typing import TypedDict, List
# 定义全局状态的数据结构
class AgentState(TypedDict):
messages: List[str] # 对话历史
current_task: str # 当前任务
final_answer: str # 最终答案
# ... 任何其他需要追踪的状态使用时与普通字典有什么区别:
- 在程序运行时(Runtime),没有任何区别;
- 但在编码时(Development Time),区别巨大。
1. 运行时(执行代码):完全一样
无论是 TypedDict 还是普通 dict,Python 解释器处理它们的方式一模一样。
- 取值都是用
state["messages"] - 赋值都是用
state["new_key"] = 123 - 打印
type(state),得到的都是<class 'dict'>
这意味着:如果你写错了键名,程序不会崩溃,依然会运行,只是结果可能不符合预期(比如取到了 None)。
2. 编码时(写代码):天差地别(核心价值)
这是 TypedDict 唯一发挥作用的地方——给你的 IDE 和静态检查工具(如 mypy)看的。
场景一:自动补全(防止手误)
假设你有如下定义:
class AgentState(TypedDict):
messages: List[str]
current_task: str使用普通字典: 你在 VSCode 里输入
state["mess"],IDE 不会给你任何提示,你只能靠自己回忆字段名。如果你少打了个age写成state["messges"],程序运行时只会返回KeyError或者None(如果用了.get()),调试起来很痛苦。使用
AgentState: 当你在 IDE 里输入state["m"]时,IDE 会立刻弹出一个下拉框,列出messages和current_task让你选择。一旦你选了messages,就绝对不会拼写错误。
场景二:类型警告(静态检查)
假设你错误地给 messages 赋了一个数字:
# 使用 AgentState
state: AgentState = {
"messages": 123, # IDE 这里会画一个红色波浪线警告:“期望 List[str],实际得到 int”
"current_task": "写代码"
}关键点:这个红色波浪线仅仅是一个警告。如果你强行运行 python main.py,程序依然会执行,不会报错。只有当你配置了 mypy 等静态检查工具并在 CI/CD 流水线中运行它时,才会构建失败。
3. 特殊的“继承”区别:创建字典时的约束
还有一个细微的区别在创建的时候:
| 操作 | 普通字典 | TypedDict(配合类型注解) |
|---|---|---|
| 缺少必填键 | 完全允许 {"a": 1} | IDE 会提示警告:“缺少字段 current_task” |
| 多余的键 | 完全允许 {"a":1, "b":2, "c":3} | IDE 会提示警告:“额外字段 c 不被允许” |
4. 总结一句话
| 维度 | 普通字典 | TypedDict |
|---|---|---|
| 本质 | 一个容器 | 还是一个容器(Python 运行时根本不区分) |
| 适用场景 | 临时的、结构不固定的数据 | 跨模块、跨智能体传递的固定结构数据(如 LangGraph 的状态) |
| 使用体验 | 全靠记忆,写错了运行时才能发现 | 写代码时 IDE 就能帮你纠错,相当于给字典加了一层“防呆设计” |
所以,可以把 TypedDict 看作是给团队协作时写的**“接口文档”**——它不会强制接管你的代码,但会提醒你和你的团队成员:“这个字典里必须有 messages,而且必须是列表!”
如果想要强制的运行时校验(写错类型程序直接报错而不是警告),那你需要去了解一下 pydantic.BaseModel,那是另一种更严格的用法。在 LangGraph 学习过程中,TypedDict 是标准做法。
Python知识点
数据容器相关(List、tuple、Str、Set、Dict)
所有有序的数据容器都可以使用切片操作[起始位置,结束位置,步长]
一般不包含结束位置的元素,步长就是指隔几个元素进行截取
- List: 有序,可重复,有索引,可修改[]表示
- str:有序,可重复,不可修改
- tuple: 有序,可用索引访问,不可修改,()表示
- Set: 有序,不能重复,无序,不支持索引访问,可修改。
{}表示,但是空集合不能用{}定义,只能用(),setExample = set(),使用{}定义出来的是空字典,有初始值的是使用{}定义,setExample = - Dict:无序,不用索引,key:value形式,不能重复{}表示
Str相关方法
strip:去除字符串中多余的空格和换行符split:按指定符号分割字符串,不包括作为分割的字符,分割好的数据为数组。literal_eval():从字符串中解析:- 字符串、字节串、数字(整数/浮点数/复数)
- 布尔值(True/False)、空值(None)
- 元组、列表、字典、集合
- 以及由以上几种类型嵌套组成的结构
不会解析执行恶意代码
pythonimport ast # 危险操作(绝不要用 eval 处理用户输入) # eval("__import__('os').system('rm -rf /')") # 这行会直接删库,极其危险! # 使用 literal_eval 处理同样的恶意字符串 try: ast.literal_eval("__import__('os').system('rm -rf /')") except ValueError as e: print(f"被拦截了,报错原因:{e}") # 输出:ValueError: malformed node or string: <_ast.Call object at 0x...>
re模块中的正则匹配方法
re.match(): 必须从文本的第一个字符开始匹配,如果开头对不上,直接报错放弃,检查字符串是否以特定前缀开头,比如检查是否以Error:开头re.search: 扫描整个文本,只要在任何一个位置找到符合规则的内容,就立马成功并返回 示例如下:
# 以下是从大模型返回结果中提取信息的代码
def _parse_output(self, text: str):
"""解析LLM的输出,提取Thought和Action。
"""
# Thought: 匹配到 Action: 或文本末尾
thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|$)", text, re.DOTALL)
# Action: 匹配到文本末尾
action_match = re.search(r"Action:\s*(.*?)$", text, re.DOTALL)
thought = thought_match.group(1).strip() if thought_match else None
action = action_match.group(1).strip() if action_match else None
return thought, action
def _parse_action(self, action_text: str):
"""解析Action字符串,提取工具名称和输入。
"""
# 这里(.*)匹配时会把后续字符包括]都匹配进去,后续发现需要一个]结尾,后面又没有字符了,就把字符串匹配的结尾回溯到最后一个]的前面
match = re.match(r"(\w+)\[(.*)\]", action_text, re.DOTALL)
if match:
return match.group(1), match.group(2)
return None, Nonere.DOTALL:re模块中的一个常量标志,作用是让正则中的.也能匹配到换行符,避免模型返回多行Thought内容时一般为换行符而提取中止匹配,导致提取信息不全.group的分组依据:分组是依据正则表达式中的小括号进行分组的,默认group(0)表示的是获取到的整体内容,group(1)就是获取到的第一个小括号中的内容。注意传入正则表达式时使用
r作为开头前缀,可以把正则表达式原封不动传给正则引擎去匹配,不会受python解释器影响,例如字符\再python解释器中也为转义字符,解释时会影响传入的正则字符串。
组包与解包
- 只有元组可以直接进行组包:
t = 1,2,3,4等效于t = (1,2,3,4) - 解包是使用*进行数据解包:python
t = [1,2,3,4] a,*b,c = t # 结果就是 a=1, b=[2,3], c=4
列表推导式
列表推导式 ---> 就是按照一定的规则快速生成一个列表的方法 --> 语法格式1:[要插入的值 for i in 序列/列表] 格式2:[要插入的值 for i in 序列/列表 if 条件],满足条件才插入
# 从一个数字列表中提取所有偶数,并计算其平方,组成一个新的列表。---> 判断偶数:num %2 ==0
num_list = [1,2,3,4]
new_list = [1**2 for i in num_list if i % 2 == 0]注意:for循环遍历字典时,默认只遍历键值,若是遍历两个值,则需要使用字典.items()
# 如果写 self.tools.items():每次拿出('get_weather', {'description': ...}),这是两个元素,正好放进 name 和 info
# 如果写 self.tools:每次只拿出 'get_weather'(一个字符串),Python 试图把它拆成 name 和 info 两个变量,但字符串只有一个元 素,直接报错:
[f"- {name}:{info['description']}" for name,info in self.tools.items()]函数相关
python中函数命名一般结合_进行命名,与java不同的是,python的函数可以之间返回多个返回值,最终返回值会封装到元组中。
类中方法命名的格式
_method受保护方法(内部使用)_parse_output()__method私有方法(名称修饰)__parse_output()method公共方法(对外暴露)run()
局部变量与全局变量
这里主要注意再函数中要是想修改全局变量的话,必须先用global关键字进行声明(只是访问不需要使用),然后才能对全局变量进行修改
num1 = 1
def fun1():
num1 = 100 # 局部变量设为100
print(num1) # 打印的是局部变量的值
fun1() # 调用函数,打印了函数内部局部变量的值,100
print(num1) # 打印的全局变量的值,1
# 要是修改全局变量
def fun2():
global num1 # 告诉python解释器,接下来使用的是全局变量num1
num1 = 100
print(num1)
fun2() # 打印的是修改后的全局变量的值 100
print(num1) # 打印的同样是全局变量的值 100参数传递方式
主要指的是调用函数时的传参方式
- 位置传参:当我们直接传入参数时,默认是位置传参,传入参数的顺序要与定义函数的参数一一对等
- 关键字传参:当我们使用 key=value 的方式进行传参时,传入的参数可以不按顺序进行传参
- 位置参数与关键字传参同时使用必须保证关键字传参位于位置传参之后
默认参数
定义函数时,可以给参数指定默认值,注意定义函数时有默认值的参数要位于函数参数列表的末尾
不定长参数
定义函数时,可以使用不定长参数,这样每次调用函数时,可以依据需要传入不同数量的参数
位置传参的不定长参数( *参数名 只接受位置传参,不接受key = value的关键字传参的方式)
参数最终都会被封装到定义时的形参中,形参是一个元组
def calc_data(*args):
# 掉用函数
data = calc_data(1,2,3,4,5,6,7,8)
data = calc_data(1,2,3)关键字传参方式的不定长参数(**参数名,把参数封装到字典中)
def calc_data(*args,**kwargs):
# 掉用函数
data = calc_data(1,2,3,4,5,6,7,8,round=2,count=0)
data = calc_data(1,2,3)匿名函数
形式如下,使用lambda表达式进行定义,注意需要使用lambda表达式进行声明所以函数不能换行,只能是一些简单函数
# 定义有参匿名函数
lambda 参数列表 :函数体
# 定义无参匿名函数
lambda : print('.............')
# 有参匿名函数,传入参数x,y直接返回x+y的值
lambda x,y : x+y
# 因为匿名函数没有名字,所以调用的时候需要把匿名函数赋值给一个变量,然后进行调用
out_line = lambda: print('......')
add = lambda x,y: x+y
out_line()
add(100, 200)典型应用场景:List中的sort自定义排序 List中的sort排序方法有两个字段分别是key、reverse,Key参数是接收function也就是函数类型的参数,reverse默认为False,也就是依据排序方式排序,若是reverse=True则依据排序方式的相反方式进行排序(长度从小到大变成从大到小)
例如指定排序方式有时比较简单,只需一句代码即可,但是key又需要接收一个函数参数,刚好使用lambda匿名函数
data_list = ["C++","Java","Python","Agent","SpringAI","LangGraph"]
data_list.sort(key = lambda item : len(item),reverse=True)
# 因为sort排序时会便利列表中的每一个元素,所以这里的item就是指便利列表时的元素,len(item)表示按照元素字符串长度进行排序,默认从小到大,要是reverse=True则排序方式就变成了从大到小。类型注解
对于Python中的数据类型,Python是若语言类型,所以定义函数参数以及定义数据类型都不需要明确指定数据类型,但是不便于开发维护。
采用类型注解的方式,让代码变得清晰易读的同时,编辑器也会依据定义的类型注解给出相应提示,易于开发
#定义变量
a: int = 695
score: float = 98.5
hobby: str = "Python"
flag: bool = True
pic: None = None
# 表示可以接收两种类型的数据
names: list[str | int]= ["A","C","E"]
phones: set[str]= {"13309091111", "15209109121"}
options: dict[str,int] = {"count":0, "total":0}
goods:tuple[str,int,int]=("手机",5999,1)类型推断
类型推断是指Python解释器自动推断出变量、表达式或函数返回值的数据类型的能力,而无需开发者显式声明。 表示定义数据的时候,python解释器会依据给出的数据类型推断出数据可能的数据类型,从而给出提示。
类中的类属性
python中的类属性属性就是对等于java中的静态属性,是属于类的,访问时默认先去访问类属性,没有再找实例属性(实例属性是每个实例对象都有的属性,即每个对象都有一份)类属性则是所有的实例对象共用一份
# 类属性定义在_ _init_ _方法外,实例属性定义在方法 _ _ init _ _内部
Class Car:
wheel = 4 # 类属性
def __init__(self,name,price):
self.name = name # 实例属性
self.price = price # 实例属性python中的模块
Python模块(module):
- 一个.py文件就是一个模块,模块是Python程序的基本组织单位。在模块中可以定义变量、函数、类,以及可执行的代码。
- 一个Python软件包也可以看作一个模块,主要用来把同属于一类的.py文件放到一起进行分类,软件包与普通包的区别在于软件包有一个__init__.py文件,里面保存当前包的信息或是一些初始化脚本文件。
- 默认情况下使用 from 模块名 import 功能名/from 包名.模块名 import 功能名,导入功能时,是以当前模块为起点的同级目录开始进行查找的,如果是在当前模块的上级目录,则会出现找不到所需模块的问题:
- 绝对路径导入模块: from 项目根目录.包名.模块名 import
- 相对路径导入(同级目录开始往下找):from 包名.模块名 import
因为开发项目时不可能所有的功能都写到一个.py文件中,当我们需要使用其他模块(模块指的是其他.py文件或者是软件包下的.py文件)时,就可以使用import结合from导入我们需要的功能,实现功能的复用
导入功能时的限制
- 使用
from 模块名 import *时默认是直接导入模块下的所有功能,可以使用变量__all__指定使用*时导入的功能 - 若是导入软件包下模块时使用
*,则必须在__init__.py文件中添加__all__=[]指定,否则会报错 - 导入模块时模块中若是有调用方法的语句则会在导入时自动执行,所以写模块时的测试代码,要用
if __name__ == "__main__"加以限制,原理如下:
# 模块my_fun内容如下
def fun():
print("这时测试模块的功能")
# 若是直接调用
fun()
# 另一模块test_module想导入my_fun模块功能时
# text_module导入my_fun方式如下
import my_fun
# 此时在模块text_module导入my_fun时,my_fun模块中的fun()代码会自动执行一遍解决方式是:每个模块都有一个内置变量__name__当在自己文件中调用自己定义的方法时,__name__得值为__main__,当被其他文件/模块调用定义的方法时,__name__的值为自己的模块名称__my_fun__,所以可以使用if语句进行避免导入模块时代码自动执行的情况出现
# 把my_fun模块中的内容进行限制就可以了
# 模块my_fun内容如下
def fun():
print("这时测试模块的功能")
if __name__ == "__main__":
fun()
# 这样当其他模块导入my_fun时,__name__的值就不为__main__,而是__my_fun__,if后的条件就不会成立这样就不会自动执行fun()了