背景
由于最近要开发一些AI LLM(Large Language Model 大语言模型)应用程序,然后又想使用LangChain(LangChain 是一个用于构建和操作大语言模型(LLMs)的框架,旨在帮助开发者更方便地集成和使用各种 LLMs 和 AI 服务。),不过LangChain使用Python,因此需要先了解下Python。
之前我是Python零基础小白,不过在我看来任何一门语言的本质不过都是编译原理的表现,因此只要有了一门开发语言的基础后学习另外一门新的语言无非就是搭建开发环境和了解其基本特性和语法就可以快速入门上手干活了。之前干过这方面事情,参考:《编译原理抽象语法树生成及执行实例》https://blog.csdn.net/camelials/article/details/123415475
1. 开发环境
关于Python开发环境的搭建这里不详细介绍,一搜一大把的东西。至于开发IDE,我选择使用了PyCharm(还有很多人选择微软的VsCode),因为和Java用的集成开发环境Idea都是Jetbrains旗下的产品,界面和风格上几乎一样,因此更加符合Java程序员习惯。并且在实操的过程中直接支持将Idea的个人设置直接导入到PyCharm中。
PyCharm 和 IntelliJ IDEA 基本类似,需要说明的是:
1、pip install package_name 是 Python 社区中常用的方式,用于安装和管理第三方包,类似于 Java 中使用 Maven 或者 Gradle 管理依赖的方式。
2、建议安装save actions插件定义保存时的其他动作,例如:代码格式化,调整imports等;
3、venv目录不用管,这是虚拟环境目录。在使用 PyCharm 新建 Python 项目时,通常会创建一个虚拟环境(virtual environment,简称 venv)来隔离项目的依赖和环境。这是为了避免不同项目之间的依赖冲突,并确保项目的可移植性和可维护性。venv 是 Python 内置的模块,用于创建轻量级的“虚拟环境”。每个虚拟环境都有自己独立的 Python 解释器和一套独立的包安装目录。虚拟环境使得你可以在不干扰全局 Python 安装的情况下,为每个项目安装和管理依赖包。
2. Python与Java宏观对比
2.1 Python 是一门解释性语言
- Python 是一门解释性语言,而 Java 是一门编译型语言。Python的执行过程大致如下:解析源代码 -> 生成 AST -> 编译为字节码 -> 生成 .pyc 文件 -> 执行字节码。
- 由于Python 是解释型语言,Python 在某些情况下可能比 Java 运行速度慢。尤其是对于大量计算密集型任务或要求高性能的应用程序,Python 可能不如 Java 或者 C++ 等编译型语言。但是可以使用 py_compile 模块编译并生成 .pyc 文件,提前编译 Python 源文件并生成 .pyc 文件在部署时是推荐的做法。在实际的部署过程中,可以将编译步骤集成到 CI/CD 管道中。例如,在 Jenkins、GitLab CI 或 GitHub Actions 中添加编译步骤,以确保每次部署前都生成最新的 .pyc 文件。
Python 的字节码并不是简单的四元组数组(1 + 1 = 2),它是一种特定格式的指令序列,每个指令对应一个操作码(opcode),并可能伴随着操作数。字节码的执行是顺序执行的,但可以通过跳转指令(例如跳转、条件跳转)改变执行流程。
Python 函数:
def add(a, b):
return a + b
输出的字节码示例(对应的字节码可以使用 dis 模块来查看):
4 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
- LOAD_FAST、BINARY_ADD 等是操作码,指示加载变量和执行加法运算等操作。
- 0、1 是操作数,指示要加载的变量位置。
从上面的实例可以看出,把Python的运行机制认为是一个四元组数组的顺序+跳转执行也没有大的问题,虽然这里四元组的叫法不太好。
2.2 Python是一种强类型和动态类型语言
Python是一种强类型和动态类型语言,而Java是强类型和静态类型语言。
- 强类型
Python不会自动进行类型转换。举例来说,试图将字符串和整数相加会导致错误:
x = "hello"
y = 5
print(x + y) # TypeError: can only concatenate str (not "int") to str
- 动态类型
在Python中,变量的类型是运行时决定的,而不是编译时,而Java则是静态类型,一个变量的类型在编译阶段就已经确定。
x = 5
print(type(x)) # <class 'int'>
x = "hello"
print(type(x)) # <class 'str'>
3. Python基础
3.1 基础
3.1.1 代码注释
使用多行字符串作为注释时,这些字符串实际上会被解析器读入,只是没有赋给任何变量,不会影响程序运行。优先使用连续的单行注释。
# 这是一个单行注释
print("Hello, World!") # 这是在行尾的单行注释
# 这是一个多行注释的第一行
# 这是一个多行注释的第二行
# 这是一个多行注释的第三行
print("Hello, World!")
"""
这是一个多行注释的第一行
这是一个多行注释的第二行
这是一个多行注释的第三行
"""
print("Hello, World!")
3.1.2 变量申明
上面说了因为Python是一种动态类型语言,变量申明不需要也不能指定其类型,但是参考其他语言的var关键字也行啊,例如:var a = 1。反正目前只能是光秃秃的[argsName] = [argsValue]方式。
# 字符串(单/双引号都可以)
message = 'hello world'
print('字符串' + message)
print("字符串" + message)
# 数字,布尔
a, b, c = 1, True, 3.14
print(a, b, c)
3.2 函数
下面代码看一眼就懂,不过需要说明的是:
- 函数声明
在 Python 中,函数是通过 def 关键字来声明的,而不使用类似于 Java 中的 public、protected 或 private 这样的访问修饰符。 - 访问权限
在 Python 中,没有像 Java 那样的访问修饰符。函数默认是公开的(public),即可以被任何地方的代码调用。 - 函数的返回值
在 Python 中,函数可以有返回值,也可以没有。函数的返回值是通过 return 语句来实现的。因为 Python 是动态类型语言,函数可以返回任何类型的值,而且可以在运行时改变返回值的类型。
# 无参无返回值函数定义
def print_somthing():
print("Hello World")
# 有参有返回值函数定义
def multiply(a, b):
return f"{a} * {b} = {a * b}"
# 函数调用
print_somthing()
print(multiply(2, 3))
由于python的动态类型的本质,从变量申明,函数定义,我们可以看出来,啥玩意都是光秃秃的,导致可读性不好。我查了下:目前 Python 没有 var 关键字,而且在未来也不太可能引入这种关键字。Python 的设计哲学之一是简洁和明确,尽量减少不必要的语法冗余。
TMD的变量的申明你后续加个var 关键字能咋了?
3.3 控制结构
3.3.1 条件语句:if, elif, else
# if-else
age = 5
if age >= 18:
print("You are a adult.")
else:
print("You are not a adult.")
# if-elif
age = 12
if age >= 18:
print("You are a adult.")
elif age >= 16:
print("You are a teenager.")
elif age >= 4:
print("You are a child.")
else:
print("You are a baby.")
3.3.2 循环:for循环和while循环。
- for
list1 = [1, 2, 3, 4, 5]
for i in list1:
print(i)
- while
num = 0
while num < 5:
print(num)
num += 1
prompt = "Enter a city: "
while True:
city = input("Enter a city: ")
if city == "quit":
break
else:
print(city)
3.4 数据结构
3.4.1 List列表
list 是一种内置的数据结构,用于存储有序的元素集合。列表在许多方面类似于其他编程语言中的数组,但功能更强大,具有动态大小,并且可以包含不同类型的元素。Python 列表的特点:
- 有序:列表中的元素是按插入顺序存储的,可以通过索引访问。
- 动态大小:列表的大小是动态的,可以根据需要自动扩展和缩小。
- 可变:列表是可变的,可以在创建后进行修改(添加、删除或更改元素)。
- 多类型元素:列表可以包含不同类型的元素(如整数、字符串、对象等)。
创建列表
可以使用方括号 [] 或 list() 函数来创建列表:
# 使用方括号创建列表
my_list = [1, 2, 3, "hello", 4.5]
# 使用 list() 函数创建列表
another_list = list([1, 2, 3])
访问列表元素
使用索引访问列表中的元素,索引从 0 开始:
my_list = [1, 2, 3, "hello", 4.5]
# 访问第一个元素
print(my_list[0]) # 输出: 1
# 访问最后一个元素
print(my_list[-1]) # 输出: 4.5
# 切片访问多个元素
print(my_list[1:3]) # 输出: [2, 3]
修改列表
列表是可变的,可以修改其内容:
my_list = [1, 2, 3]
# 修改元素
my_list[1] = "two"
print(my_list) # 输出: [1, "two", 3]
# 添加元素
my_list.append(4)
print(my_list) # 输出: [1, "two", 3, 4]
# 插入元素
my_list.insert(1, "new")
print(my_list) # 输出: [1, "new", "two", 3, 4]
# 删除元素
my_list.remove("two")
print(my_list) # 输出: [1, "new", 3, 4]
# 删除指定位置的元素
del my_list[1]
print(my_list) # 输出: [1, 3, 4]
# 弹出元素
popped = my_list.pop()
print(popped) # 输出: 4
print(my_list) # 输出: [1, 3]
列表操作
Python 列表支持多种操作:
# 创建两个列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
# 合并列表
combined_list = list1 + list2
print(combined_list) # 输出: [1, 2, 3, 4, 5, 6]
# 重复列表元素
repeated_list = list1 * 2
print(repeated_list) # 输出: [1, 2, 3, 1, 2, 3]
# 列表长度
length = len(list1)
print(length) # 输出: 3
# 检查元素是否在列表中
is_in_list = 2 in list1
print(is_in_list) # 输出: True
遍历列表
可以使用循环来遍历列表中的元素:
my_list = [1, 2, 3, "hello", 4.5]
# 使用 for 循环遍历列表
for item in my_list:
print(item)
# 使用索引遍历列表
for i in range(len(my_list)):
print(my_list[i])
列表推导式
列表推导式是一种简洁的创建和操作列表的方法:
# 创建一个包含 0 到 9 的平方的列表
squares = [x ** 2 for x in range(10)]
print(squares) # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 过滤列表中的元素
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares) # 输出: [0, 4, 16, 36, 64]
3.4.2 Tuple元组
3.4.2.1 元组介绍
在 Python 中,tuple(元组)是一种内置的数据结构,用于存储有序的不可变元素集合。与列表(list)类似,元组可以包含不同类型的元素,但一旦创建,元组中的元素就不能修改。Python 元组的特点:
- 有序:元组中的元素是按插入顺序存储的,可以通过索引访问。
- 不可变:元组一旦创建,元素不能修改(即不能添加、删除或更改元素)。
- 多类型元素:元组可以包含不同类型的元素(如整数、字符串、对象等)。
- 更高性能:由于元组是不可变的,相对于列表,它们的性能(如遍历和访问速度)更好。
创建元组
可以使用圆括号 () 或 tuple() 函数来创建元组:
# 使用圆括号创建元组
my_tuple = (1, 2, 3, "hello", 4.5)
# 使用 tuple() 函数创建元组
another_tuple = tuple([1, 2, 3])
单元素元组:创建只有一个元素的元组时,需要在元素后加一个逗号,否则会被认为是普通的括号运算:
single_element_tuple = (1,)
print(type(single_element_tuple)) # 输出: <class 'tuple'>
not_a_tuple = (1)
print(type(not_a_tuple)) # 输出: <class 'int'>
访问元组元素
使用索引访问元组中的元素,索引从 0 开始:
my_tuple = (1, 2, 3, "hello", 4.5)
# 访问第一个元素
print(my_tuple[0]) # 输出: 1
# 访问最后一个元素
print(my_tuple[-1]) # 输出: 4.5
# 切片访问多个元素
print(my_tuple[1:3]) # 输出: (2, 3)
不可变性
元组是不可变的,一旦创建就不能修改:
my_tuple = (1, 2, 3)
# 尝试修改元组元素会导致错误
# my_tuple[1] = "two" # 会报错: TypeError: 'tuple' object does not support item assignment
元组操作
尽管元组是不可变的,但仍可以进行多种操作:
# 创建两个元组
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
# 合并元组
combined_tuple = tuple1 + tuple2
print(combined_tuple) # 输出: (1, 2, 3, 4, 5, 6)
# 重复元组元素
repeated_tuple = tuple1 * 2
print(repeated_tuple) # 输出: (1, 2, 3, 1, 2, 3)
# 元组长度
length = len(tuple1)
print(length) # 输出: 3
# 检查元素是否在元组中
is_in_tuple = 2 in tuple1
print(is_in_tuple) # 输出: True
遍历元组
可以使用循环来遍历元组中的元素:
my_tuple = (1, 2, 3, "hello", 4.5)
# 使用 for 循环遍历元组
for item in my_tuple:
print(item)
# 使用索引遍历元组
for i in range(len(my_tuple)):
print(my_tuple[i])
3.4.2.2 元组的使用场景
1、返回多个值:函数可以返回多个值作为元组。
def get_coordinates():
return (10, 20)
x, y = get_coordinates()
print(x, y) # 输出: 10 20
2、数据的不可变性:需要保证数据不被修改时使用元组。
3、字典键:由于元组是不可变的,可以用作字典的键(列表则不能)。
my_dict = {(1, 2): "value"}
print(my_dict[(1, 2)]) # 输出: value
3.4.3 Dictionary字典
在 Python 中,dictionary(字典)是一种内置的数据结构,用于存储键-值对。字典是无序的、可变的,并且可以存储任意类型的键和值。字典在其他编程语言中通常被称为哈希表或关联数组。Python中字典的特点:
- 无序:Python 3.7 之前,字典是无序的。从 Python 3.7 开始,字典保持插入顺序。
- 键唯一:字典中的键是唯一的,不能重复。
- 键和值可以是任意类型:键必须是不可变的类型(如字符串、数字或元组),而值可以是任意类型。
- 可变:字典是可变的,可以动态地添加、修改或删除键值对。
创建字典
可以使用花括号 {} 或 dict() 函数来创建字典:
# 使用花括号创建字典
my_dict = {"name": "Alice", "age": 25, "city": "New York"}
# 使用 dict() 函数创建字典
another_dict = dict(name="Bob", age=30, city="Los Angeles")
访问字典元素
通过键来访问字典中的值:
my_dict = {"name": "Alice", "age": 25, "city": "New York"}
# 访问元素
print(my_dict["name"]) # 输出: Alice
# 使用 get() 方法访问元素,可以避免键不存在时引发错误
print(my_dict.get("age")) # 输出: 25
print(my_dict.get("country", "USA")) # 输出: USA (默认值)
修改字典
字典是可变的,可以动态地添加、修改或删除键值对:
my_dict = {"name": "Alice", "age": 25, "city": "New York"}
# 修改值
my_dict["age"] = 26
# 添加新键值对
my_dict["country"] = "USA"
# 删除键值对
del my_dict["city"]
# 使用 pop() 方法删除键值对
age = my_dict.pop("age")
print(my_dict) # 输出: {'name': 'Alice', 'country': 'USA'}
print(age) # 输出: 26
遍历字典
可以使用各种方法遍历字典中的键、值或键值对:
my_dict = {"name": "Alice", "age": 25, "city": "New York"}
# 遍历键
for key in my_dict:
print(key)
# 遍历值
for value in my_dict.values():
print(value)
# 遍历键值对
for key, value in my_dict.items():
print(key, value)
字典的方法
Python 提供了多种字典方法,用于操作字典:
my_dict = {"name": "Alice", "age": 25, "city": "New York"}
# keys() 方法返回所有键
keys = my_dict.keys()
print(keys) # 输出: dict_keys(['name', 'age', 'city'])
# values() 方法返回所有值
values = my_dict.values()
print(values) # 输出: dict_values(['Alice', 25, 'New York'])
# items() 方法返回所有键值对
items = my_dict.items()
print(items) # 输出: dict_items([('name', 'Alice'), ('age', 25), ('city', 'New York')])
# clear() 方法清空字典
my_dict.clear()
print(my_dict) # 输出: {}
# copy() 方法返回字典的浅拷贝
new_dict = my_dict.copy()
print(new_dict) # 输出: {}
嵌套字典
字典可以嵌套,表示更复杂的数据结构:
nested_dict = {
"person1": {"name": "Alice", "age": 25},
"person2": {"name": "Bob", "age": 30}
}
print(nested_dict["person1"]["name"]) # 输出: Alice
字典推导式
字典推导式是一种创建字典的简洁方法:
# 创建一个键是数字,值是数字平方的字典
squares = {x: x**2 for x in range(6)}
print(squares) # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
3.4.4 Set集合
3.4.4.1 Set集合介绍
在 Python 中,set 是一种内置的数据结构,用于存储无序的、唯一的元素集合。集合用于快速检查元素是否存在、去重和数学集合操作(如交集、并集、差集等)。Python 集合的特点:
- 无序:集合中的元素没有特定的顺序,无法通过索引访问。
- 唯一性:集合中的元素是唯一的,重复的元素会被自动去除。
- 可变:集合是可变的,可以动态地添加、删除元素。
- 元素类型:集合中的元素必须是不可变的(如数字、字符串、元组),但是集合本身是可变的。
创建集合
可以使用花括号 {} 或 set() 函数来创建集合:
# 使用花括号创建集合
my_set = {1, 2, 3, 4, 5}
# 使用 set() 函数创建集合
another_set = set([1, 2, 3, 4, 5])
# 创建空集合
empty_set = set() # 不能使用 {} 创建空集合,因为 {} 创建的是空字典
添加和删除元素
可以动态地添加或删除集合中的元素:
my_set = {1, 2, 3}
# 添加元素
my_set.add(4)
print(my_set) # 输出: {1, 2, 3, 4}
# 删除元素,如果元素不存在会引发 KeyError
my_set.remove(2)
print(my_set) # 输出: {1, 3, 4}
# 使用 discard() 方法删除元素,如果元素不存在不会引发错误
my_set.discard(5) # 不会引发错误
# 使用 pop() 方法删除并返回一个随机元素
removed_element = my_set.pop()
print(removed_element)
print(my_set)
# 清空集合
my_set.clear()
print(my_set) # 输出: set()
集合操作
集合支持多种数学集合操作:
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 并集
union_set = set1 | set2
print(union_set) # 输出: {1, 2, 3, 4, 5}
# 交集
intersection_set = set1 & set2
print(intersection_set) # 输出: {3}
# 差集
difference_set = set1 - set2
print(difference_set) # 输出: {1, 2}
# 对称差集
symmetric_difference_set = set1 ^ set2
print(symmetric_difference_set) # 输出: {1, 2, 4, 5}
遍历集合
可以使用循环来遍历集合中的元素:
my_set = {1, 2, 3, 4, 5}
for item in my_set:
print(item)
集合推导式
集合推导式是一种创建集合的简洁方法:
# 创建一个包含 0 到 9 的平方的集合
squares = {x ** 2 for x in range(10)}
print(squares) # 输出: {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
不可变集合
Python 还提供了 frozenset,它是不可变的集合,创建后不能修改:
immutable_set = frozenset([1, 2, 3, 4, 5])
# 尝试添加或删除元素会引发错误
# immutable_set.add(6) # 会引发 AttributeError: 'frozenset' object has no attribute 'add'
3.4.2.2 Set集合使用场景
1、去重:集合可以自动去除重复的元素。
my_list = [1, 2, 2, 3, 4, 4, 5]
my_set = set(my_list)
print(my_set) # 输出: {1, 2, 3, 4, 5}
2、集合运算:可以方便地进行并集、交集、差集等运算。
3、快速成员检查:集合的成员检查操作速度非常快。
my_set = {1, 2, 3, 4, 5}
print(3 in my_set) # 输出: True
print(6 in my_set) # 输出: False
3.5 异常处理
在 Python 中,异常处理是通过 try、except、else 和 finally 这几个关键字来实现的。异常处理机制允许程序在运行过程中捕获和处理错误,而不是让程序直接崩溃。这样可以提高程序的健壮性和用户体验。Python 的异常处理结构主要包括以下几部分:
- try: 包含可能引发异常的代码。
- except: 捕获并处理异常。
- else: 如果没有引发异常,执行此代码块。
- finally: 无论是否引发异常,都执行此代码块。
基本示例
try:
# 可能引发异常的代码
result = 10 / 0
except ZeroDivisionError:
# 捕获并处理特定类型的异常
print("除数不能为零")
else:
# 如果没有引发异常,执行此代码块
print("计算成功")
finally:
# 无论是否引发异常,都执行此代码块
print("执行结束")
捕获特定异常
try:
num = int(input("请输入一个整数: "))
except ValueError:
print("输入的不是一个有效的整数")
捕获多个异常
try:
num = int(input("请输入一个整数: "))
result = 10 / num
except ValueError:
print("输入的不是一个有效的整数")
except ZeroDivisionError:
print("除数不能为零")
捕获所有异常
try:
num = int(input("请输入一个整数: "))
result = 10 / num
except Exception as e:
print(f"发生异常: {e}")
自定义异常
可以通过创建继承自 Exception 类的自定义异常类,来定义自己的异常类型:
class CustomError(Exception):
pass
def some_function():
raise CustomError("这是一个自定义异常")
try:
some_function()
except CustomError as e:
print(f"捕获自定义异常: {e}")
重新引发异常
try:
try:
num = int(input("请输入一个整数: "))
result = 10 / num
except ZeroDivisionError:
print("除数不能为零")
raise # 重新引发异常
except ValueError:
print("输入的不是一个有效的整数")
except Exception as e:
print(f"重新捕获异常: {e}")
3.6 面向对象
3.6.1 类
在 Python 中,类(class)是面向对象编程的基础。类用于定义对象的属性和行为,通过类可以创建多个具有相同属性和行为的实例(对象)。
3.6.1.1 类创建类
在 Python 中,使用 class 关键字来定义一个类。类名通常使用大写字母开头的驼峰命名法。
class MyClass:
pass # 占位符,表示什么都不做
3.6.1.2 类的属性和方法
类可以包含属性(变量)和方法(函数)。init 方法是类的构造函数,用于初始化对象的属性。
1、在 Python 中,self 和 Java 中的 this 在某种程度上类似,不过还是存在一定差异:
- 在 Java 中,this 是一个关键字,用于引用当前对象实例,不需要显式传递。
- 在 Python 中,self 是一个习惯用语,用于引用当前对象实例,需要在定义方法时显式传递。
2、str 和 repr: 用于定义对象的字符串表示。类似于 Java 中的 toString() 方法,如果只定义了 str 方法,而没有定义 repr 方法,则在调用 repr() 函数时会使用 str 方法的返回值作为默认值。
- __str__当使用 str() 函数或者 print() 函数打印对象时会调用。
- __repr__当使用 repr() 函数获取对象的字符串表示时会调用。
class Person:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age # 实例属性
def greet(self):
print(f"Hello, my name is {self.name} and I am {self.age} years old.")
def __str__(self):
return f"name: {self.name}, age: {self.age}"
# 创建类的实例
p = Person("Alice", 30)
# 访问属性和方法
print(p.name) # 输出: Alice
print(p.age) # 输出: 30
p.greet() # 输出: Hello, my name is Alice and I am 30 years old.
print(str(p)) # 输出:name: Alice, age: 30
3.6.1.3 继承
类可以继承另一个类,继承的类称为子类,被继承的类称为父类或基类。Python 支持多重继承,即一个类可以继承多个父类。
- 单继承
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Kitty")
print(dog.speak()) # 输出: Buddy says Woof!
print(cat.speak()) # 输出: Kitty says Meow!
- 多继承
class Base1:
def method_base1(self):
print("Base1 method")
class Base2:
def method_base2(self):
print("Base2 method")
class Derived(Base1, Base2):
pass
d = Derived()
d.method_base1() # 输出: Base1 method
d.method_base2() # 输出: Base2 method
3.6.1.4 多态
Python的多态是通过:不同类对象通过同一接口调用不同方法来实现的。这种方式称之为鸭子类型(Duck Typing),它依赖于对象的行为(是否具有特定的方法),而不是对象的具体类型,这就是建立在Python的动态类型基础之上的。
class Dog:
# sound
def sound(self):
print("Woof!")
class Duck:
# sound
def sound(self):
print("Quack!")
# 多态
def make_sound(obj):
obj.sound()
# Dog和Duck是两个对象,都有一个相同的方法:sound。
make_sound(Duck()) # 输出:Quack!
make_sound(Dog()) # 输出:Woof!
3.6.1.5 抽象
Python中使用 abc(Abstract Base Classes)模块来实现抽象类和接口,在Python中没有接口,接口可以使用抽象基类来模拟。
- abc 模块
abc 模块(Abstract Base Classes)是Python标准库的一部分,用于定义抽象基类。抽象基类提供了一种定义接口的方式,确保子类实现特定的方法。 - abstractmethod 装饰器
用于标记必须在子类中实现的方法
from abc import ABC, abstractmethod
# 定义抽象类
class Animal(ABC):
@abstractmethod
def sound(self):
pass # 抽象方法,没有具体实现
@abstractmethod
def move(self):
pass
def sleep(self):
print("sleep...")
# 定义具体子类,继承抽象类
class Dog(Animal):
def sound(self):
return "Woof!"
def move(self):
return "Runs"
class Cat(Animal):
def sound(self):
return "Meow!"
def move(self):
return "Jumps"
# 尝试实例化抽象类会引发错误
# animal = Animal() # TypeError: Can't instantiate abstract class Animal with abstract methods move, sound
# 实例化具体子类
dog = Dog()
cat = Cat()
print(dog.sound()) # 输出: Woof!
print(dog.move()) # 输出: Runs
print(dog.sleep()) # 输出: sleep...
print(cat.sound()) # 输出: Meow!
print(cat.move()) # 输出: Jumps
print(cat.sleep()) # 输出: sleep...
4. 总结
上述内容只是本人一个下午自学所得,也没有系统的去看什么教程,就是根据自己对于语言的认识去刻意去查相关东西做出的总结,因此说得不一定都对,仅仅是个人正儿八经的胡说八道式的总结。 不过最后还是想吐槽下一些python的写法:
1、Python 代码块的作用域和逻辑结构通过缩进来表示
对于这个我只能说唉……,用可见字符它不香吗?
2、Python 语法过于简洁
Python语法过于简洁,导致了一些另类的做法:
- 创建只有一个元素的元组时,需要在元素后加一个逗号,否则会被认为是普通的括号运算;
single_element_tuple = (1,)
print(type(single_element_tuple)) # 输出: <class 'tuple'>
not_a_tuple = (1)
print(type(not_a_tuple)) # 输出: <class 'int'>
- 申明空set 不能使用 empty_set = {} 来声明空Set,避免和空字典语法冲突;
# 创建空集合
empty_set = set() # 不能使用 {} 创建空集合,因为 {} 创建的是空字典
3、Python 动态类型的本质是把双刃剑
动态类型虽然可以让代码更加灵活和简洁,但是牺牲了代码可读性,例如Python的变量申明,函数定义,啥玩意都是光秃秃的,导致可读性不好。不过Python似乎也承认这样可读性不好的错误:从 Python 3.5 开始引入了类型注解(type hints),允许开发者在函数签名和变量上添加类型注解,增强代码可读性和可维护性。这些类型注解并不会改变 Python 动态类型的本质,只是作为开发和静态分析工具的参考。例如:可以这样去定义函数的参数/返回值类型。
def add(a: int, b: int) -> int:
return a + b
最后,个人感觉Java程序员按照上面的路子了解一遍后一小时Python快速上手没啥问题(下图为AI 生成)。