Java程序员Python一小时速成

背景

由于最近要开发一些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中。
在这里插入图片描述

PyCharmIntelliJ 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、strrepr: 用于定义对象的字符串表示。类似于 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 生成)。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/726888.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vant2组件库的基础应用

目录 一、Picker 选择器 1.1、数组对象处理 1.2、每个选项颜色设置 二、滚动分页加载列表 三、Calendar 日历(可选范围限制) 四、input值过滤 官网&#xff1a;Vant 2 - Mobile UI Components built on Vue 一、Picker 选择器 官网示例数据&#xff1a; columns: [杭州…

Anti-human IL-12/-23 (p40) mAb (MT3279H), azide free

Anti-human IL-12/-23 (p40) mAb (MT3279H), azide free该单克隆抗体用于中和人IL-12/-23的生物活性。 产品详情&#xff1a; 免疫原&#xff1a;人重组IL-23蛋白 Isotype: mouse IgG2b Specificity: 天然和重组人IL-12和 IL-23蛋白的p40 subunit 浓度&#xff1a;1mg/ml …

查看nginx安装/配置路径,一个服务器启动两个nginx

查看nginx安装/配置路径 查看nginx的pid&#xff1a; ps -ef | grep nginx查看pid对应服务的启动路径 ll /proc/2320/exe使用检查配置文件命令&#xff0c;查看配置文件位置 /usr/local/nginx/sbin/nginx -t一个服务启动两个nginx 拷贝一份程序&#xff0c;cpbin是我自己创…

【HarmonyOS NEXT】har 包的构建生成过程

Har模块文件结构 构建HAR 打包规则 开源HAR除了默认不需要打包的文件&#xff08;build、node_modules、oh_modules、.cxx、.previewer、.hvigor、.gitignore、.ohpmignore&#xff09;和.gitignore/.ohpmignore中配置的文件&#xff0c;cpp工程的CMakeLists.txt&#xff0c;…

[CR]厚云填补_基于相似像素的渐进式缺口填补方法PGFCTS

Progressive gap-filling in optical remote sensing imagery through a cascade of temporal and spatial reconstruction models--Remote Sensing of Environment--2024.06.01 Abstract 在光学遥感图像的预处理中&#xff0c;由于云层较厚或传感器发生故障而产生的空白填充一…

群晖NAS本地部署并运行一个基于大语言模型Llama2的个人本地聊天机器人

前言 本文主要分享如何在群晖 NAS 本地部署并运行一个基于大语言模型 Llama 2 的个人本地聊天机器人并结合内网穿透工具发布到公网远程访问。本地部署对设备配置要求高一些,如果想要拥有比较好的体验,可以使用高配置的服务器设备. 目前大部分大语言模型的产品都是基于网络线上…

律所优选管理软件排名:Alpha法律智能操作系统领先行业

面对庞大复杂的管理体量&#xff0c;律所一体化建设面临的首要问题便是信息化系统的建设与应用&#xff0c;即统一管理平台的问题。Alpha法律智能操作系统集法律大数据、律所管理、人工智能于一体&#xff0c;从业务、人员、信息三个板块最大限度支持律所数字化建设&#xff0c…

电脑桌面文件夹删除不了怎么办?6种方法快速解决,建议收藏!

桌面文件夹删不掉怎么办&#xff1f;有时会遇到桌面上的文件夹无法删除的问题&#xff0c;这是由于文件夹被系统进程或某些应用占用&#xff0c;或者是由于权限设置等原因造成的。以下是解决桌面文件夹无法删除问题的方法&#xff0c;帮助你有效地清理桌面环境。 桌面文件夹删不…

南京邮电大学计算机网络实验二(网络路由器配置RIP协议)

文章目录 一、 实验目的和要求二、 实验环境(实验设备)三、 实验步骤四、实验小结&#xff08;包括问题和解决方法、心得体会、意见与建议等&#xff09;五、报告资源 一、 实验目的和要求 掌握思科路由器的运行过程&#xff0c;掌握思科路由器的硬件连线与接口&#xff0c;掌…

HNU-计算机系统(CSAPP)实验一 原型机vspm1.0

一、题目 【实验目的】 &#xff08;1&#xff09;了解冯诺伊曼体系结构&#xff1b; &#xff08;2&#xff09;理解指令集结构及其作用&#xff1b; &#xff08;3&#xff09;理解计算机的运行过程&#xff0c;就是指令的执行过程&#xff0c;并初步掌握调试方法。 【实…

黑马苍穹外卖4 店铺营业状态设置+Redis基础

店铺营业状态设置 Redis MySQL Java并发 JavaMVC 计算机网络 操作系统 算法&#xff0c;后端面试主要是这些&#xff0c;外加项目 Redis 数据库&#xff0c;基于内存存储的key-value结构。 mysql是磁盘存储&#xff0c;通过二维表存储。 在文件夹目录打开cmd 服务端&#xf…

HNU操作系统2023期中考试试卷及参考答案

本试题参考 甘晴void 的CSDN博客【2.2】操作系统OS_甘晴void的博客-CSDN博客&#xff0c;本意为期中复习自用&#xff0c;答案在其基础上进行进一步完善&#xff0c;若有错误还请指正&#xff01; 第一题&#xff1a;基础题&#xff08;20分&#xff09; 1.1&#xff08;4分&a…

Windows反截屏开发实现

文章目录 Windows反截屏开发实现1. SetWindowDisplayAffinity2. 反截屏系统3. 总结 Windows反截屏开发实现 最近在我们云桌面中需要做到反截屏能力&#xff0c;所谓反截屏就是我们无法通过截图软件&#xff08;微信&#xff0c;QQ&#xff0c;截图等程序&#xff09;截取桌面的…

lock-锁的概念

锁的简介 锁是计算机协调多个进程或线程并发访问某一资源的机制&#xff08;避免发生资源争抢&#xff09; 在并发环境下&#xff0c;多个线程会对同一个资源进行争抢&#xff0c;可能会导致数据不一致的问题。为了解决这一问题&#xff0c;需要通过一种抽象的锁来对资源进行…

springboot + Vue前后端项目(第十八记)

项目实战第十八记 写在前面1. 前台页面搭建&#xff08;Front.vue&#xff09;2. 路由3.改动登录页面Login.vue4. 前台主页面搭建Home.vue总结写在最后 写在前面 本篇主要讲解系统前台搭建&#xff0c;通常较大的项目都会搭建前台 1. 普通用户登录成功后前台页面效果&#xf…

RestTemple请求GET接口403

问题描述 使用oss接口获取资源的时候&#xff0c;通过浏览器可以直接下载&#xff0c;在代码中使用RestTemplate的get方式访问的时候&#xff0c;出现403错误 问题排查 因为返回状态码是403&#xff0c;就想着是不是授权问题&#xff0c;因为有的接口是有防抓取规则的&…

Java实现自动定时任务配置并判断当天是否需要执行示例

最近接到一个需求&#xff0c;用户要能配置一个定时循环的任务&#xff0c;就是和手机闹钟循环差不多&#xff0c;设置好比如周一、周二、周三&#xff0c;那么在这几天内这个任务就需要自动执行 需求不复杂&#xff0c;首先我需要判断当前是周几&#xff0c;当然用户说了让我…

【廉颇老矣,尚能饭否】传统的数据仓库是否还能发挥作用?

引言&#xff1a;随着数字化转型的深入和大数据技术的发展&#xff0c;大数据平台、数据中台和和数据湖技术不断涌现&#xff0c;给人感觉传统的数据仓库技术已经过时&#xff0c;廉颇老矣&#xff0c;不能应对新的挑战&#xff0c;在数字化转型中&#xff0c;不能发挥重要作用…

第4章 工程经济评价指标 作业

第4章 工程经济评价指标 作业 一单选题&#xff08;共27题&#xff0c;100分&#xff09; (单选题)利息备付率是指( )与应付利息费用的比值。 A. 息税前利润 B. 利润总额 C. 净利润 D. 营业收入 正确答案: A:息税前利润; (单选题)当净现值( )0时,该项目不可行。 A. < B. …

界面构件开发之RC文件

代码; #include <gtk-2.0/gtk/gtk.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <glib-2.0/glib.h> #include <stdio.h>int main(int argc, char *argv[]) {gtk_init(&argc, &argv);gtk_rc_parse("./mainrc");GtkWidget *winN…