Python3教程

Python基础

字符串格式化

python字符串格式化输出有三种方式: %、format、f

xxxxxxxxxx #!/usr/bin/env python#coding=utf-8#ansible version 4.10.0​import argparseimport jsonimport loggingfrom ansible.module_utils.common.collections import ImmutableDictfrom ansible.inventory.manager import InventoryManagerfrom ansible.parsing.dataloader import DataLoaderfrom ansible.vars.manager import VariableManagerfrom ansible import contextfrom ansible.executor.playbook_executor import PlaybookExecutorfrom ansible.plugins.callback import CallbackBase​logging.basicConfig(level=logging.DEBUG,format=”%(asctime)s %(levelname)s %(message)s”)​class ResultsCallback(CallbackBase):    def init(self,*args,**kwargs):        super(ResultsCallback,self).init(*args,**kwargs)        self.task_ok = {}        self.task_failed = {}        self.task_skipped = {}        self.task_status = {}        self.task_unreachable = {}​    def v2_runner_on_ok(self, result, *args, **kwargs):        self.task_ok[result.task_name] = result._result​    def v2_runner_on_failed(self, result, *args, **kwargs):        self.task_failed[result.task_name] = result._result​    def v2_runner_on_skipped(self, result, *args, **kwargs):        self.task_skipped[result.task_name] = result._result​    def v2_runner_on_status(self, result, *args, **kwargs):        self.task_status[result.task_name] = result._result​    def v2_runner_on_unreachable(self, result, *args, **kwargs):        self.task_unreachable[result.task_name] = result._result​class AnsibleApi:    def init(self):        self.parse_arguments()        self.init_context()​​    def init_context(self):        context.CLIARGS = ImmutableDict(            connection=’smart’,            module_path=[‘library’],            forks=10,            become=None,            become_method=None,            become_user=None,            check=False,            diff=False,            verbosity=0       )​    def parse_arguments(self):        parser = argparse.ArgumentParser(description=’Ansible API’)        parser.add_argument(‘–host’, type=str, help=’’)        parser.add_argument(‘–username’, type=str, help=’’)        parser.add_argument(‘–password’, type=str, help=’’)        parser.add_argument(‘–jdk_version’, type=str, help=’’)        parser.add_argument(‘–system_name’, type=str, help=’’)        parser.add_argument(‘–env_type’, type=str, help=’’)        parser.add_argument(‘–deploy_type’, type=str, help=’’)        parser.add_argument(‘–action’, type=str, help=’’)        self.args = vars(parser.parse_args())​    def get_variable(self):        loader = DataLoader()        inventory = InventoryManager(loader=loader, sources=’inventory/hosts’)        variable_manager = VariableManager(loader=loader, inventory=inventory)        host = self.args[‘host’]        inventory.add_host(host, group=’all’)        variable_manager.set_host_variable(host, ‘ansible_ssh_user’, self.args[‘username’])        variable_manager.set_host_variable(host, ‘ansible_ssh_pass’, self.args[‘password’])        variable_manager.set_host_variable(host, varname=’user’, value={‘username’: self.args[‘username’],                                                                        ‘home’: ‘/home/{}’.format(self.args[‘username’])})        for key,value in self.args.items():            variable_manager.set_host_variable(host,key,value)        return {“loader”: loader, “inventory”: inventory, “variable_manager”: variable_manager}​    def run_playbook(self):        data = self.get_variable()        loader, inventory, variable_manager = data[‘loader’], data[‘inventory’], data[‘variable_manager’]        pbex = PlaybookExecutor(            playbooks=[‘{}.yml’.format(self.args[‘action’])],            inventory=inventory,            variable_manager=variable_manager,            loader=loader,            passwords=None       )        return pbex​    def get_result(self):        pbex = self.run_playbook()        result_callback = ResultsCallback()        pbex._tqm._callback_plugins.append(result_callback)        pbex.run()        task_failed = result_callback.task_failed        task_unreachable = result_callback.task_unreachable        if task_unreachable:            raise Exception(‘task unreachable: {}’.format(json.dump(task_unreachable,indent=4)))        elif task_failed:            raise Exception(‘task failed: {}’.format(json.dump(task_failed, indent=4)))​if name == ‘main‘:    api = AnsibleApi()    api.get_result()python

name = 'acai'
age = 27
addr = 'BeiJing'
print("My name is %s, age: %s address: %s" %(name,age,addr))

format

res = "name: {0}, age: {1}, addr: {2}".format('acai',27,'beijing')
print(res)

f

name='acai'
age=27
res = f"name:{name}, age:{age}"
print(res)

字符编码

unicode作用

用来作为其他编码的中转,比如: utf-8 转 gbk:
转换顺序为:utf-8 > unicode >gbk

python文件指定编码

#coding:utf-8

编码转换

unicode转gbk:

s = '阿才的博客'
res = s.encode('gbk')
print(res,type(res))

gbk转unicode:

s = '阿才的博客'
res = s.encode('gbk')
print(res.decode('gbk'))

变量和常量

变量

变量是什么?

在Python中,变量是用于存储数据的名称。变量允许程序在执行过程中动态地存储和操作数据。每个变量都有一个名称和一个关联的值。

变量的命名规则

规则 示例
只能包含字母、数字和下划线 my_variable, num2, user_name
不能以数字开头 var1(有效),1var(无效)
区分大小写 my_varmy_Var是两个不同的变量
不能使用Python关键字 for, if, while
规避使用内置函数和模块名 print, list, math
建议使用小写字母 my_variableMyVariable更常见
对于常量,使用全大写字母和下划线 PI, MAX_VALUE, DEFAULT_NAME

变量的定义和使用

name = 'acaibog'
print(name)

常量

常量是什么?

在Python中,常量是指在程序运行期间不可更改的值。与变量不同,常量的值在定义后不能被修改或重新赋值。
没有严格的内置机制来定义常量。但是,为了标识一个值是常量,并且遵循一定的约定,通常会将常量的名称全部大写。这是一种常见的命名约定,用于告知其他开发人员这个值是一个常量,应该避免对其进行修改。

常量的使用

# 定义常量
PI = 3.14159
MAX_VALUE = 100

# 尝试修改常量(不推荐)
PI = 3.14 # 仍然可以修改,但违反了常量的约定

# 使用常量
radius = 5
circumference = 2 * PI * radius
print(circumference) # 输出: 31.4159

数据类型

字符串

字符串内置方法

方法名 描述
capitalize() 将字符串的第一个字符转换为大写字母,并将其余字符转换为小写字母
casefold() 将字符串转换为小写字母,用于比较字符串时忽略大小写
count(substring) 返回指定子字符串在字符串中出现的次数
endswith(suffix) 检查字符串是否以指定的后缀结束,返回布尔值
find(substring) 查找指定子字符串在字符串中的第一次出现的索引,找不到返回-1
index(substring) 查找指定子字符串在字符串中的第一次出现的索引,找不到会引发ValueError异常
isalnum() 检查字符串是否只包含字母和数字字符,返回布尔值
isalpha() 检查字符串是否只包含字母字符,返回布尔值
isdigit() 检查字符串是否只包含数字字符,返回布尔值
islower() 检查字符串中的字母是否都为小写,返回布尔值
isupper() 检查字符串中的字母是否都为大写,返回布尔值
join(iterable) 将可迭代对象中的元素连接成一个字符串,以当前字符串作为分隔符
lower() 将字符串中的所有字母转换为小写
upper() 将字符串中的所有字母转换为大写
replace(old, new) 将字符串中的指定子字符串替换为新的子字符串
split(separator) 使用指定的分隔符将字符串拆分为子字符串列表
strip() 去除字符串两端的空白字符(空格、制表符等)
startswith(prefix) 检查字符串是否以指定的前缀开始,返回布尔值
title() 将字符串中每个单词的首字母转换为大写
splitlines() 将字符串按行拆分为字符串列表

字符串根据索引截取

示例 描述
string = "Hello, World!" 原始字符串
string[0] 截取第一个字符 'H'
string[7] 截取第八个字符 'W'
string[-1] 截取最后一个字符 '!'
string[0:5] 截取索引0到4的子字符串 'Hello'
string[7:] 从索引7开始截取到字符串的末尾 'World!'
string[:5] 从字符串的开头截取到索引4的子字符串 'Hello'
string[-6:-1] 截取倒数第六个字符到倒数第二个字符的子字符串 'World'
string[::2] 从头到尾每隔一个字符截取 'HloWrd'
string[::-1] 反转字符串 '!dlroW ,olleH'

字符串的定义与使用

name = 'acaiblog.top'
print(name)

整型

内置方法

方法名 描述
int() 将一个对象转换为整数类型。可以接受字符串、浮点数、布尔值等不同类型的参数,并返回对应的整数值。
bit_length() 返回整数的二进制表示中所需的位数,不包括符号位。
to_bytes(length, byteorder, signed=False) 将整数转换为指定长度的字节数组,可以选择字节顺序和是否包含符号位。
from_bytes(bytes, byteorder, signed=False) 将字节数组转换为整数,可以选择字节顺序和是否包含符号位。
hex() 返回整数的十六进制表示,以字符串形式返回。
oct() 返回整数的八进制表示,以字符串形式返回。
bin() 返回整数的二进制表示,以字符串形式返回。
real 属性,表示整数的实部。对于整数类型来说,实部即整数本身。
imag 属性,表示整数的虚部。对于整数类型来说,虚部为0。

整型定义与使用

age = 27
print(age)

浮点型

浮点型内置方法

方法名 描述
float() 将一个对象转换为浮点数类型。可以接受整数、字符串等不同类型的参数,并返回对应的浮点数值。
is_integer() 检查浮点数是否表示一个整数。如果浮点数表示一个整数,则返回True,否则返回False
hex() 返回浮点数的十六进制表示,以字符串形式返回。
as_integer_ratio() 返回浮点数的分子和分母构成的元组,用于精确表示浮点数的分数形式。
is_integer() 检查浮点数是否表示一个整数。如果浮点数表示一个整数,则返回True,否则返回False
real 属性,表示浮点数的实部。对于浮点数类型来说,实部即浮点数本身。
imag 属性,表示浮点数的虚部。对于浮点数类型来说,虚部为0。

浮点型定义与使用

aaa = 3.1314926
print(float(aaa))

列表

列表内置方法

方法名 描述
append(element) 在列表末尾添加一个元素。
extend(iterable) 将可迭代对象中的元素添加到列表末尾。
insert(index, element) 在指定索引位置插入一个元素。
remove(element) 移除列表中第一个出现的指定元素。
pop([index]) 移除并返回列表中指定索引位置的元素,如果不指定索引,默认移除并返回最后一个元素。
index(element) 返回列表中第一个出现指定元素的索引。
count(element) 返回列表中指定元素出现的次数。
sort() 对列表进行排序,修改原列表。
reverse() 反转列表中的元素,修改原列表。
copy() 返回列表的浅拷贝副本。
clear() 移除列表中的所有元素。
len() 返回列表中元素的个数。
max() 返回列表中的最大值。
min() 返回列表中的最小值。
sum() 返回列表中所有元素的和。
index(element, start, end) 返回列表中指定元素在指定范围内的第一个出现的索引。
remove() 从列表中移除指定的值,如果值不存在则引发ValueError异常。

列表的定义与使用

list1 = [1,2,3,4]
print(list1)

元组

元组的特点

元组是一个不可变的列表,按照索引存放数据;只能读取不能修改

元组的内置方法

方法名 描述
count(element) 返回元组中指定元素出现的次数。
index(element) 返回元组中第一个出现指定元素的索引。
len() 返回元组中元素的个数。
max() 返回元组中的最大值。
min() 返回元组中的最小值。
sum() 返回元组中所有元素的和(仅适用于包含数字的元组)。

元组的定义和使用

tup1 = (1,2,3,4)
print(tup1)

字典

字典定义规范

规则 描述
使用花括号({})来定义字典 字典以花括号包围
键和值之间使用冒号(:)分隔 键和值之间使用冒号进行分隔
键必须是唯一的 字典中的键必须是唯一的
键可以是不可变的数据类型 键可以是字符串、数值、元组等不可变的数据类型
值可以是任意类型的对象 值可以是字符串、数值、列表、字典等任意类型的对象
键和值之间使用逗号(,)分隔 键-值对之间使用逗号进行分隔
元素是无序的 字典中的元素是无序的,顺序可能与定义时的顺序不同
可以使用索引访问元素 可以使用键作为索引来获取对应的值

字典内置方法

方法名 描述
clear() 移除字典中的所有元素。
copy() 返回字典的浅拷贝副本。
fromkeys(keys, value=None) 创建一个新字典,使用指定的键作为键,指定的值作为默认值。
get(key, default=None) 返回指定键的值,如果键不存在,则返回默认值。
items() 返回一个包含所有键值对的元组列表。
keys() 返回一个包含所有键的列表。
values() 返回一个包含所有值的列表。
pop(key, default=None) 移除并返回指定键的值,如果键不存在,则返回默认值。
popitem() 随机移除并返回一个键值对(通常是最后一个)。
setdefault(key, default=None) 返回指定键的值,如果键不存在,则设置默认值并返回默认值。
update(other) 使用其他字典或键值对更新当前字典。
len() 返回字典中键值对的数量。

字典定义与使用

dict1 = {"name":"acai.cloud", "age":27}

集合

什么是集合?

在Python中,集合(Set)是一种无序、不重复元素的数据结构。集合中的元素是唯一的,不会重复出现。集合是可变的,可以根据需要添加、删除和修改元素。

集合的命名规则

规则 描述
使用花括号({})来定义集合 集合以花括号包围
元素之间使用逗号(,)分隔 集合中的元素通过逗号进行分隔
可以使用set()函数创建空集合 使用set()函数可以创建一个空集合
可以将可迭代对象转换为集合 可以使用set()函数将其他可迭代对象(如列表、元组)转换为集合
元素无特定顺序 集合中的元素没有特定的顺序,每次迭代时元素的顺序可能会不同
元素必须是不可变的 集合中的元素必须是不可变的,不能包含可变的数据类型,如列表或其他集合
元素是唯一的 集合中的元素是唯一的,不会重复出现
可以使用len()函数获取元素数量 使用len()函数可以获取集合中元素的数量
支持常见的集合操作 集合支持常见的集合操作,如并集、交集、差集等
可以使用方法或运算符进行操作 可以使用方法或运算符对集合进行操作,如添加元素、删除元素、判断元素是否存在等

集合的内置方法

方法 描述
add(element) 向集合中添加一个元素
update(iterable) 将可迭代对象中的元素添加到集合中
remove(element) 从集合中删除指定的元素,如果元素不存在会引发KeyError错误
discard(element) 从集合中删除指定的元素,如果元素不存在不会引发错误
pop() 从集合中随机删除一个元素并返回该元素
clear() 清空集合中的所有元素
copy() 创建并返回集合的一个浅拷贝
union(set)
set.union(other_set)
返回两个集合的并集
intersection(set)
set.intersection(other_set)
返回两个集合的交集
difference(set)
set.difference(other_set)
返回两个集合的差集
symmetric_difference(set)
set.symmetric_difference(other_set)
返回两个集合的对称差集
issubset(set)
set.issubset(other_set)
判断当前集合是否为另一个集合的子集
issuperset(set)
set.issuperset(other_set)
判断当前集合是否为另一个集合的超集
isdisjoint(set)
set.isdisjoint(other_set)
判断当前集合是否与另一个集合没有交集

集合的定义

my_set = {1, 2, 3, 4, 5}

布尔

布尔的作用

通常用作判断条件

布尔的定义

True or False

运算符

算术运算符

算术运算符用于执行数值计算操作。可以使用这些运算符对整数、浮点数和其他支持算术运算的对象进行加减乘除、取余、取幂等操作。

运算符 描述
+ 加法运算符,用于相加两个操作数
- 减法运算符,用于从第一个操作数中减去第二个操作数
* 乘法运算符,用于将两个操作数相乘
/ 除法运算符,用于将第一个操作数除以第二个操作数
// 整除运算符,用于执行整数除法,结果向下取整
% 取模运算符,用于计算第一个操作数除以第二个操作数的余数
** 幂运算符,用于计算第一个操作数的指数次幂

比较运算符

比较运算符(或称为关系运算符)用于比较两个值或表达式,并返回布尔值(True或False),表示比较的结果。

运算符 描述
== 等于运算符,判断两个操作数是否相等
!= 不等于运算符,判断两个操作数是否不相等
> 大于运算符,判断第一个操作数是否大于第二个操作数
< 小于运算符,判断第一个操作数是否小于第二个操作数
>= 大于等于运算符,判断第一个操作数是否大于等于第二个操作数
<= 小于等于运算符,判断第一个操作数是否小于等于第二个操作数

逻辑运算符

逻辑运算符(Logical Operators)用于在布尔表达式中进行逻辑运算。Python提供了三个常用的逻辑运算符:and(与)、or(或)和not(非)。

运算符 描述
== 等于运算符,判断两个操作数是否相等
!= 不等于运算符,判断两个操作数是否不相等
> 大于运算符,判断第一个操作数是否大于第二个操作数
< 小于运算符,判断第一个操作数是否小于第二个操作数
>= 大于等于运算符,判断第一个操作数是否大于等于第二个操作数
<= 小于等于运算符,判断第一个操作数是否小于等于第二个操作数

成员运算符

成员运算符用于判断一个值是否存在于指定的集合(如字符串、列表、元组、集合和字典)中。Python提供了两个成员运算符:in和not in。

运算符 描述
in 如果值存在于集合中,返回True;否则返回False
not in 如果值不存在于集合中,返回True;否则返回False

身份运算符

身份运算符(Identity Operators)用于比较对象的身份(内存地址)是否相同。在Python中,身份运算符包括isis not

运算符 描述
is 如果两个操作数引用同一个对象,则返回True;否则返回False
is not 如果两个操作数引用的不是同一个对象,则返回True;否则返回False

增量运算符

增量运算符(Increment Operators)用于对变量进行增量操作,即对变量的值进行递增或递减。Python中提供了一组增量运算符,用于简化对变量进行加、减、乘、除等操作的语法。

运算符 描述 示例
+= 加法赋值运算符,将右侧操作数的值加到左侧操作数上,并将结果赋值给左侧操作数 a += 1 等价于 a = a + 1
-= 减法赋值运算符,将右侧操作数的值从左侧操作数中减去,并将结果赋值给左侧操作数 a -= 1 等价于 a = a - 1
*= 乘法赋值运算符,将右侧操作数的值乘以左侧操作数,并将结果赋值给左侧操作数 a *= 2 等价于 a = a * 2
/= 除法赋值运算符,将左侧操作数的值除以右侧操作数,并将结果赋值给左侧操作数 a /= 2 等价于 a = a / 2
//= 整除赋值运算符,将左侧操作数的值整除以右侧操作数,并将结果赋值给左侧操作数 a //= 2 等价于 a = a // 2
%= 取模赋值运算符,将左侧操作数的值取模右侧操作数,并将结果赋值给左侧操作数 a %= 2 等价于 a = a % 2
**= 幂赋值运算符,将左侧操作数的值求幂右侧操作数,并将结果赋值给左侧操作数 a **= 2 等价于 a = a ** 2

链式运算符

Python中没有专门的”链式运算符”一词。但是,可以使用多个比较运算符来构建链式比较表达式,从而实现链式比较的效果。这种方式也被称为”链式比较”。
链式比较允许在单个表达式中对多个值进行连续比较。比如,我们可以使用<><=>=等比较运算符来构建一个链式比较表达式。

x = 5
y = 10
z = 15

if x < y < z:
print("x < y < z")

交叉赋值

交叉赋值(Cross Assignment)是一种同时交换多个变量值的操作。在Python中,可以使用交叉赋值语法来实现这种操作,通过将多个变量放在一起进行赋值。

a = 1
b = 2

a, b = b, a

print(a) # 输出: 2
print(b) # 输出: 1

解压赋值

解压赋值(Unpacking Assignment)是一种将序列(如列表、元组)或可迭代对象的元素解压并赋值给多个变量的操作。在Python中,可以使用解压赋值语法来实现这种操作,通过将变量放在一对括号中,用逗号分隔开,与序列或可迭代对象对应的元素个数相同,可以将其解压赋值给多个变量。
解压列表:

my_list = [1, 2, 3]
a, b, c = my_list

print(a) # 输出: 1
print(b) # 输出: 2
print(c) # 输出: 3

解压元组:

my_tuple = (4, 5, 6)
x, y, z = my_tuple

print(x) # 输出: 4
print(y) # 输出: 5
print(z) # 输出: 6

解压可迭代对象:

my_string = "abc"
m, n, o = my_string

print(m) # 输出: 'a'
print(n) # 输出: 'b'
print(o) # 输出: 'c'

解压赋值还可以结合使用*操作符,将多余的元素赋值给一个列表变量。例如:

my_list = [1, 2, 3, 4, 5]
a, b, *rest = my_list

print(a) # 输出: 1
print(b) # 输出: 2
print(rest) # 输出: [3, 4, 5]

流程控制

条件语句

if语句:用于根据条件执行不同的代码块。
if-else语句:在if条件为真时执行一个代码块,否则执行另一个代码块。
if-elif-else语句:用于处理多个条件,根据不同条件执行相应的代码块。

循环语句

for循环:用于遍历一个可迭代对象中的元素。
while循环:在条件为真时重复执行一个代码块,直到条件为假或循环被中断。

控制语句

break语句:用于终止循环并跳出当前循环块。
continue语句:跳过当前循环的剩余代码,并继续下一次循环迭代。
pass语句:用作占位符,表示一个空的代码块。

文件操作

python提供open()函数来操作文件读写操作

open函数支持的文件操作模式

模式 描述 示例
“r” 只读模式 file = open("filename.txt", "r")
“w” 写入模式 file = open("filename.txt", "w")
“a” 追加模式 file = open("filename.txt", "a")
“x” 独占创建模式 file = open("filename.txt", "x")
“b” 二进制模式 file = open("filename.txt", "rb")
“t” 文本模式 file = open("filename.txt", "rt")
“+” 读写模式 file = open("filename.txt", "r+")

open函数支持的内置方法

方法 描述
file.read(size=-1) 读取文件内容
file.readline(size=-1) 读取文件的一行内容
file.readlines(sizehint=-1) 读取所有行的内容,并返回列表
file.write(string) 将字符串写入文件
file.writelines(lines) 将字符串列表逐行写入文件
file.seek(offset[, whence]) 在文件中移动文件指针的位置
file.tell() 返回当前文件指针的位置
file.close() 关闭文件
file.flush() 将缓冲区内容立即写入文件
file.fileno() 返回文件的文件描述符
file.isatty() 检查文件是否连接到终端设备
file.truncate(size=None) 截断文件到指定的字节数
file.__enter__() and file.__exit__() 上下文管理器协议方法,用于支持 with 语句的文件自动管理

Python函数

什么是python函数

在Python中,函数是一段可重复使用的代码块,用于执行特定的任务或计算,并可以接受参数和返回值。函数可以封装一系列的操作,通过给定的输入(参数)来产生输出(返回值),从而提高代码的可重用性和可维护性。

函数的特点

特点 描述
可重复使用 函数是可重复使用的代码块,可以在程序中的多个位置调用执行。
参数传递 函数可以接受参数作为输入,并在函数体中使用这些参数进行计算和操作。
返回值 函数可以返回一个值作为结果,供调用方使用。
函数定义 使用 def 关键字定义函数,并指定函数名称、参数和函数体的代码块。
函数调用 使用函数名和参数调用函数,以执行函数体中的代码。
代码组织和模块化 函数可以将代码组织为更小的可管理单元,提高程序结构的清晰性和可维护性。
可读性和重用性 使用函数可以提高代码的可读性,并允许在需要时重复使用相同的功能。

python函数的命名规则

规则 描述
由字母、数字和下划线组成 函数名可以包含字母(大小写敏感)、数字和下划线字符。
以字母或下划线开头 函数名可以以字母或下划线开头,但不能以数字开头。
区分大小写 Python 是大小写敏感的,因此函数名中的大小写字母被视为不同的字符。
具有描述性 函数名应该具有描述性,能够清晰地表达函数的功能或目的。
采用小写字母和下划线 通常使用小写字母和下划线的组合作为函数名,以提高可读性。
使用下划线或驼峰命名法 对于多个单词组成的函数名,可以使用下划线或驼峰命名法(Camel Case)。
避免使用保留字 避免使用Python的保留字(关键字)作为函数名。
选择描述性的函数名 尽量选择具有描述性和准确意义的函数名,以提高代码的可读性和可维护性。

定义函数的语法

函数的定义使用 def 关键字。以下是函数定义的语法:

def function_name(parameter1, parameter2, ...):
# 函数体代码块
# 可以执行一系列操作
# 可以使用参数进行计算
# 可以使用 return 语句返回结果(可选)
语法 描述
def function_name(parameter1, parameter2, ...): 使用 def 关键字定义函数,后跟函数名和参数列表。
缩进的函数体代码块 在冒号后面缩进的代码块,包含了函数的操作和逻辑。
return 语句(可选) 使用 return 语句指定函数的返回值。

函数的调用

函数名() or 函数名(参数1,参数2,…..)

函数的参数

形参和实参

形参:在定义函数阶段定义的参数称为形式参数,简称形参
实参:在调用函数阶段传入的参数称之为实际参数,简称实参

实参传值的三种方式

方式一

func(1,2)

方式二

a=1
b=2
func(a,b)

方式三

func(int('1'),2)

位置参数

在函数定义阶段按照从左到右的顺序,依次定义的参数称之为位置参数。
形参特点:每个形参必须被传值
实参特点:按照从左到右的顺序依次传值,实参和形参位置一一对应
示例:

def func(x,y,z):
return x,y,z
print(func(1,2,3))

示例:实参带*,可以将列表元素拆分作为位置实参然后传递给形参,结果返回元组类型数据

def func(x,y,z):
return x,y,z
print(func(*[1,2,3]))

关键字参数

在函数调用阶段,按照key=value的形式传值,称之为关键字参数
实参特点:可以给指定形参传值
示例:

def func(x,y,z):
return x,y,z
print(func(x=1,y=2,z=10))

位置参数和关键字参数混合使用,位置实参必须放在关键字前面,不能为同一个形参重复传值

def func(x,y,z):
return x,y,z
print(func(1,2,z=10))

默认参数

在定义函数阶段,就已经被赋值的形参,称之为默认参数。
默认参数特点: 在调用函数阶段可以不用给默认形参传值 。
示例:

def func(x,y,z=20):
return x,y,z
print(func(x=10,y=20))
#如果在调用函数时给默认参数传值,则默认参数的值为新值
print(func(x=10,y=20,z=30))

位置参数和默认参数混合使用,位置形参必需在默认形参的左边;默认参数的形参实在函数定义阶段被赋值的,准确来说被赋予的是值的内存地址;虽然默认参数可以被指定为任意数据类型,但是不推荐使用可变数据类型。
实例:形参默认值不推荐使用可变数据类型,可以定义为None;然后通过判断传入值为None时,设置形参为可变数据类型。

def func(x,y,z,l=None):
if not l: #相当于 if l == None:
l = []
for i in x,y,z:
l.append(i)
return l

print(func(1,2,3))

可变长度参数

可变长度参数:在调用函数阶段,实参传入的值的个数不固定;比如:args、**kargs
可变长度的位置参数,用来接收溢出的位置实参;溢出的位置参数
参数名传入的值返回元组数据类型;语法约定可变长度位置参数用*args表示
示例:

def func(x,y,*args):
return x,y,args

print(func(1,2,3,4,5,6))

可变长度的关键字参数:用来接收溢出的关键字实参;形参名会将溢出的关键字实参保存成字典格式返回;语法约定可变长度的关键字参数用**kargs表示。
示例:

def func(x,y,**kwargs):
return x,y,kwargs

print(func(x=1,y=2,b=10,c=30))

args和**kargs组合使用:args必需在**kargs之前
示例:

def func(*args,**kwargs):
return args,kwargs

print(func(1,2,aa=10,bb=20))

命名关键字参数

在定义函数阶段,*后面定义的参数称之为命名关键字参数;命名关键字实参必需按照key=value的形式为其传值
示例:

def func(x,y,*,a,b): #a,b称之为命名关键字参数
return x,y,a,b

print(func(1,2,a=3,b=4))

名称空间与作用域

什么是名称空间?

名称空间:存放变量名,是对栈区的划分;简单来说,名称空间就是存储变量名与变量内存地址对应关系的一张表。

名称空间的分类

名称空间 描述
内置名称空间 Python 内置函数,如 abs()
全局名称空间 顶级代码定义的变量,在调用时创建全局名称空间
局部名称空间 函数体内定义的变量

名称空间的加载顺序

内置名称空间、全局名称空间、局部名称空间

什么是作用域

变量的作用范围就叫做作用域

global与nonlocal

在函数体内使用global关键字声明的变量称之为全局变量,例如:

def func1():
x = 11
def func2():
global x
x = 22
func2()
print("func1内x:",x)
func1()

修改函数外层函数包含的变量使用nonlocal关键字,例如:

def func1():
x = 11
def func2():
nonlocal x
x = 22
func2()
print("func1内x:",x)
func1()

闭包函数

什么是闭包函数

内层函数引用了外层函数,叫做闭包函数

定义闭包函数

def func1():
x = 'acai.cloud'
def func2():
print(x)
return func2

func = func1()
func()

闭包函数传递参数

def func1(x):
def func2():
print(x)
return func2

res = func1('acai.cloud')
res()

函数装饰器

函数装饰器的定义

在不修改被装饰器对象源代码以及调用方式的情况下为装饰对象添加新功能。
案例:检查函数执行时间

import time

def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"函数 {func.__name__} 执行时间: {execution_time} 秒")
return result
return wrapper

@timer_decorator #相当于timer_decorator(my_function)
def my_function():
# 函数逻辑
pass

my_function() # 当调用my_function函数时,实际上是执行了timer_decorator(my_function)

多个装饰器运行顺序

当一个函数被装饰器装饰存在多个时,装饰器加载顺序为从下往上,运行顺序从上往下

函数迭代器

迭代器的定义

在Python中,函数迭代器是通过在函数中使用生成器(Generator)来实现的。生成器是一种特殊的函数,它使用 yield 语句来生成一个值,并且可以暂停和恢复函数的执行状态。

迭代器对象

在 Python 中,可迭代对象(iterable)是指具有迭代功能的对象,它可以被用于迭代操作,例如在循环中逐个访问元素。可迭代对象提供了一种统一的方式来处理序列、集合和其他可迭代的数据结构。
可迭代对象具有以下特点:

  • 实现了 iter() 方法:可迭代对象必须实现 iter() 方法,该方法返回一个迭代器对象。迭代器对象用于实际进行迭代操作。
  • 可以使用 iter() 函数进行迭代:可迭代对象可以通过调用内置函数 iter() 来获取一个迭代器对象。这样,我们可以使用迭代器对象逐个访问可迭代对象的元素。
  • 支持使用 for 循环进行迭代:可迭代对象可以使用 for 循环进行迭代,也可以使用其他迭代相关的操作,如 next() 函数和迭代器协议的方法。

Python 中的许多数据类型和数据结构都是可迭代对象,例如列表(list)、元组(tuple)、字符串(string)、字典(dict)、集合(set)等。此外,用户也可以自定义类来实现可迭代对象的功能,只需实现 iter() 方法返回一个迭代器对象即可。自定义迭代器示例:

class MyIterable:
def __init__(self, data):
self.data = data

def __iter__(self):
return MyIterator(self.data)


class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0

def __iter__(self):
return self

def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value


# 使用自定义的可迭代对象进行迭代
my_iterable = MyIterable([1, 2, 3, 4, 5])
for item in my_iterable:
print(item)

函数生成器

什么是生成器

在函数体中存在yield关键字,调用函数并不会执行函数体代码;会返回一个生成器对象,生成器即迭代器。 一句话概括:生成器就是自定义的迭代器。

定义生成器

def func():
print('第一次')
yield 1
print('第二次')
yield 2
print('第三次')
yield 3

res = func()
res.__next__() #此处__next__()方法可以简写成next(res)
res.__next__()
res.__next__()

从上面的代码示例可以看出,每调用一次__next__()方法遇到yield关键字,会把结果返回给yield

函数递归

什么是递归函数

函数递归的本质就是函数循环。

调用递归函数

函数递归的调用是函数嵌套调用的一种形式,在调用一个函数的过程中又直接或间接地调用本身。 例如:

def func():
print('func')
func()

func()

生成式

什么是生成式

在Python中,生成式(Comprehension)是一种简洁的语法结构,用于创建新的可迭代对象(例如列表、集合、字典)或生成器对象。生成式允许我们使用一种紧凑的方式从现有的可迭代对象中快速构建新的对象。

列表生成式

列表生成式(List Comprehension):列表生成式用于创建新的列表对象。它使用一种简洁的语法结构,允许在一个表达式中定义循环,并可选地应用条件筛选。 示例:

numbers = [1, 2, 3, 4, 5]
squares = [x ** 2 for x in numbers]
# 输出: [1, 4, 9, 16, 25]

集合生成式

集合生成式(Set Comprehension):集合生成式用于创建新的集合对象。它的语法类似于列表生成式,但结果是一个集合,因此不会包含重复的元素。例如:

numbers = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
unique_squares = {x ** 2 for x in numbers}
# 输出: {1, 4, 9, 16, 25}

字典生成式

字典生成式(Dictionary Comprehension):字典生成式用于创建新的字典对象。它使用一种类似于列表生成式的语法结构,允许在表达式中定义键-值对,并可选地应用条件筛选。例如:

numbers = [1, 2, 3, 4, 5]
square_dict = {x: x ** 2 for x in numbers}
# 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

三元表达式

什么是三元表达式

使用一行代码实现条件判断或循环

示例

例如:

def func(a,b):
if a > b:
print(a)
else:
print(b)

使用三元表达式效果:

def func(a,b):
res = a if a > b else b
print(res)

回调函数

什么是回调函数

把函数当成参数传递给另外一个函数,再当前函数执行完毕之后。最后再调用该参数(函数),这个函数就是回调函数。

回调函数的特点

特点 描述
可定制性 允许开发者编写自定义的逻辑和操作,根据需求执行特定的代码。
扩展性 在不修改原有代码的情况下,通过回调函数作为参数传递,增加新的逻辑和功能。
异步编程 在异步编程中常用,通过将回调函数注册到事件处理机制中,及时响应和处理事件。
事件驱动性 与事件驱动编程模型相关联,当特定事件发生时,调用相应的回调函数进行响应和处理。
可重用性 独立的代码逻辑可以在不同的上下文和场景中重复使用,提高代码的复用性和维护性。
解耦合性 通过定义接口规范和回调函数,实现模块之间的松耦合交互和通信。

回调函数的基本语法

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import currentThread as cthread
import os
import time

def func1(i):
print("Process start ……",i,os.getpid())
time.sleep(1)
print("Process end ……",i,os.getpid())
return "*" * i

def call_back(obj):
print("回调函数callback进程号:",os.getpid())
obj.result()

if __name__ == '__main__':
p = ProcessPoolExecutor(3)
for i in range(1,3):
res = p.submit(func1,i)
#进程对象.add_done_callback(回调函数);可以把res本对象和回调函数自动传递到函数里来
res.add_done_callback(call_back)

p.shutdown()
print("主进程执行结束……",os.getpid())

Python模块

使用模块

什么是python模块

模块可以理解为代码的集合体,当我们要使用的时候通过调用模块实现代码功能。而不用每次使用代码功能的时候都要写一遍,使用模块可以大量节约开发成本和时间。

导入模块

导入单个模块

import 模块名.方法

from 模块 import 方法

导入多个模块

import module1,module2,...   #不建议这么些,建议一行导入一个模块

导入模块的顺序

内置模块 > 第三方模块 > 自定义模块

python模块命名规范

规范 描述
使用小写字母 模块名应该使用小写字母。
使用下划线分隔单词 如果模块名由多个单词组成,应使用下划线 _ 分隔单词。
避免使用特殊字符 模块名应避免使用特殊字符,例如空格、连字符 -、点 . 等。
避免与Python关键字冲突 模块名应避免与Python的关键字冲突。
使用有意义的名称 模块名应该具有描述性,并能够清楚地表示其功能或目的。
遵循命名约定 遵循Python的命名约定,例如采用小写字母和下划线的snake_case风格。

sys

用于与Python解释器交互的模块,提供访问命令行参数、标准输入输出、系统信息等功能。

方法名 描述
sys.argv 命令行参数的列表。其中,sys.argv[0] 表示脚本名称,后续元素为传递给脚本的参数。
sys.exit([arg]) 退出当前程序。如果提供了arg参数,它会作为退出状态码返回给调用者。
sys.modules 返回当前已加载的模块字典。
sys.platform 返回当前系统平台的标识符。
sys.stdin, sys.stdout, sys.stderr 分别表示标准输入、标准输出和标准错误流。
sys.version 返回Python解释器的版本信息。
sys.path 包含要在模块搜索路径中查找模块的目录列表。
sys.getsizeof(object[, default]) 返回对象占用的内存字节数。
sys.getdefaultencoding() 返回系统默认的字符串编码。
sys.setrecursionlimit(limit) 设置递归调用的最大深度限制。

os

提供与操作系统交互的函数,例如文件和目录操作、环境变量、进程管理等。

方法名 描述
os.name 返回操作系统的名称。Windows系统返回'nt',Linux系统返回'posix'
os.getcwd() 返回当前工作目录的路径。
os.chdir(path) 修改当前工作目录到指定路径。
os.listdir(path='.') 返回指定目录中的文件和文件夹列表。
os.mkdir(path) 创建一个目录。
os.makedirs(path) 递归创建多层目录。
os.remove(path) 删除指定的文件。
os.rmdir(path) 删除指定的目录。
os.removedirs(path) 递归删除目录树。
os.rename(src, dst) 重命名文件或目录。
os.path.exists(path) 判断指定路径是否存在。
os.path.isfile(path) 判断指定路径是否为文件。
os.path.isdir(path) 判断指定路径是否为目录。
os.path.join(path1[, path2[, ...]]) 拼接多个路径组件。
os.path.abspath(path) 返回指定路径的绝对路径。
os.path.dirname(path) 返回指定路径的父目录路径。
os.path.basename(path) 返回指定路径的文件名或目录名。
os.path.splitext(path) 分离文件路径和扩展名。
os.path.getsize(path) 返回指定文件的大小(字节数)。
os.path.isabs(path) 判断指定路径是否为绝对路径。
os.path.islink(path) 判断指定路径是否为符号链接。
os.path.realpath(path) 返回指定路径的规范化绝对路径。
os.path.dirname(os.path.realpath(__file__)) 获取当前脚本所在的目录路径。

time

提供与时间相关的函数和时间戳操作,例如获取当前时间、时间延迟等。

方法名 描述
time.time() 返回当前时间的时间戳(从1970年1月1日开始的秒数)。
time.sleep(secs) 暂停程序执行指定的秒数。
time.localtime([secs]) 将时间戳转换为本地时间。如果未提供时间戳,则使用当前时间。
time.gmtime([secs]) 将时间戳转换为UTC时间。如果未提供时间戳,则使用当前时间。
time.strftime(format[, t]) 格式化时间为指定的字符串格式。
time.strptime(string[, format]) 将字符串解析为时间。
time.ctime([secs]) 将时间戳转换为可读的字符串形式(默认为当前时间)。
time.asctime([t]) 将时间元组或struct_time转换为可读的字符串形式。
time.mktime(t) 将时间元组或struct_time转换为时间戳。
time.process_time() 返回当前进程的CPU时间(以秒为单位)。
time.perf_counter() 返回计算机系统的高精度计时器的值(以秒为单位)。
time.monotonic() 返回计算机系统的单调时钟的值(以秒为单位)。
time.time_ns() 返回当前时间的时间戳(以纳秒为单位)。
time.sleep_ms(ms) 暂停程序执行指定的毫秒数。
time.sleep_us(us) 暂停程序执行指定的微秒数。
time.thread_time() 返回当前线程的CPU时间(以秒为单位)。

datetime

提供日期和时间操作的模块,包括日期时间的创建、格式化、运算等。

方法名 描述
datetime.datetime(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]) 创建一个表示日期和时间的datetime对象。
datetime.datetime.now([tz]) 返回当前本地日期和时间的datetime对象。
datetime.datetime.utcnow() 返回当前UTC日期和时间的datetime对象。
datetime.datetime.fromtimestamp(timestamp[, tz]) 根据给定的时间戳创建一个datetime对象。
datetime.datetime.strptime(date_string, format) 将字符串解析为datetime对象,按照指定的格式进行解析。
datetime.datetime.strftime(format) 返回格式化后的日期和时间字符串。
datetime.timedelta(days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]) 表示时间间隔或持续时间的timedelta对象。
datetime.date(year, month, day) 创建一个表示日期的date对象。
datetime.date.today() 返回当前本地日期的date对象。
datetime.date.fromtimestamp(timestamp) 根据给定的时间戳创建一个date对象。
datetime.date.strftime(format) 返回格式化后的日期字符串。
datetime.time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) 创建一个表示时间的time对象。
datetime.time.strftime(format) 返回格式化后的时间字符串。
datetime.timedelta.total_seconds() 返回timedelta对象表示的时间间隔总秒数。
datetime.timedelta.days 返回timedelta对象表示的天数。
datetime.timedelta.seconds 返回timedelta对象表示的总秒数(不包括天数)。
datetime.timedelta.microseconds 返回timedelta对象表示的总微秒数。

random

random模块提供了生成随机数的函数和方法。

方法名 描述
random.random() 返回一个0到1之间的随机浮点数。
random.randint(a, b) 返回一个指定范围内的随机整数,包括两个边界值。
random.uniform(a, b) 返回一个指定范围内的随机浮点数,包括两个边界值。
random.choice(seq) 从序列中随机选择一个元素。
random.shuffle(seq) 将序列中的元素随机打乱顺序。
random.sample(population, k) 从指定的总体中随机选择k个元素,返回一个新列表。
random.seed(a=None, version=2) 初始化随机数生成器的种子。如果不提供种子,则使用系统时间作为种子。
random.getstate() 返回当前随机数生成器的内部状态。
random.setstate(state) 恢复随机数生成器的内部状态。
random.choices(population, weights=None, *, cum_weights=None, k=1) 从指定的总体中根据权重随机选择k个元素。
random.gauss(mu, sigma) 返回一个符合高斯分布的随机浮点数。
random.randrange(stop) 返回一个从0开始的随机整数,范围为0到指定的停止值(不包括停止值)。
random.randrange(start, stop[, step]) 返回一个指定范围的随机整数,可指定起始值、停止值和步长。
random.expovariate(lambd) 返回一个符合指数分布的随机浮点数。
random.gammavariate(alpha, beta) 返回一个符合伽马分布的随机浮点数。
random.uniform(a, b) 返回一个指定范围内的随机浮点数,包括两个边界值。
random.triangular(low, high, mode) 返回一个符合三角分布的随机浮点数。
random.betavariate(alpha, beta) 返回一个符合beta分布的随机浮点数。

shutil

shutil模块提供了许多用于文件和目录操作的方法。

方法名 描述
shutil.copy(src, dst) 复制文件或目录。
shutil.copy2(src, dst) 复制文件或目录,并保留原始文件的元数据。
shutil.move(src, dst) 移动文件或目录。
shutil.copytree(src, dst[, symlinks=False]) 递归复制整个目录树。
shutil.rmtree(path[, ignore_errors=False]) 递归删除整个目录树。
shutil.make_archive(base_name, format[, root_dir[, base_dir]]) 创建压缩归档文件(例如ZIP文件)或者解压缩归档文件。
shutil.unpack_archive(filename[, extract_dir]) 解压缩归档文件。
shutil.disk_usage(path) 返回指定路径所在磁盘分区的使用情况。
`shutil.which(cmd[, mode=os.F_OK os.X_OK, path=None])`
shutil.get_terminal_size(fallback=(columns, lines)) 返回当前终端窗口的大小。
shutil.chown(path, user=None, group=None) 更改指定路径的所有者和所属组。
shutil.copyfile(src, dst) 复制文件。
shutil.copymode(src, dst) 将源文件的权限模式复制到目标文件。
shutil.copystat(src, dst) 复制源文件的状态信息到目标文件。
shutil.copy2(src, dst) 复制文件或目录,并保留原始文件的元数据。
shutil.copytree(src, dst[, symlinks=False]) 递归复制整个目录树。

json

json模块提供了用于 JSON 数据编码和解码的方法。可以将 Python 对象转换为 JSON 字符串,以及将 JSON 字符串解码为 Python 对象。

方法名 描述
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) 将 Python 对象转换为 JSON 字符串。
json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) 将 JSON 字符串转换为 Python 对象。
json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) 将 Python 对象写入文件中以 JSON 格式编码。
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) 从文件中读取 JSON 数据并将其解码为 Python 对象。
json.JSONEncoder.default(self, o) 当遇到无法序列化的对象时,提供默认的处理方法。
json.JSONEncoder.encode(self, o) 将 Python 对象编码为 JSON 字符串。
json.JSONDecoder.object_hook(self, dct) 在解码 JSON 字符串时,自定义处理 JSON 对象的方法。
json.JSONDecoder.decode(self, s, _w=<function _whitespace_matcher>, _PY3=False) 将 JSON 字符串解码为 Python 对象。

pickle

pickle模块提供了用于序列化和反序列化Python对象的方法。

方法名 描述
pickle.dump(obj, file, protocol=None, *, fix_imports=True) 将 Python 对象序列化并写入文件中。
pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict") 从文件中加载序列化的 Python 对象并进行反序列化。
pickle.dumps(obj, protocol=None, *, fix_imports=True) 将 Python 对象序列化为字符串。
pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict") 将序列化的字节对象反序列化为 Python 对象。
pickle.Pickler(file, protocol=None, *, fix_imports=True) 创建一个 Pickler 对象,用于将 Python 对象序列化为文件。
pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict") 创建一个 Unpickler 对象,用于从文件中加载并反序列化 Python 对象。

hashlib

hashlib模块提供了多种哈希算法的实现,包括MD5、SHA1、SHA256等。用于计算给定数据的哈希值,并生成摘要值。可以通过选择不同的哈希算法来调用相应的方法生成相应的哈希对象,再使用update()方法更新数据,最后使用digest()或hexdigest()方法生成摘要值。

方法名 描述
hashlib.md5([data]) 返回一个用于MD5算法的hash对象。
hashlib.sha1([data]) 返回一个用于SHA1算法的hash对象。
hashlib.sha256([data]) 返回一个用于SHA256算法的hash对象。
hashlib.sha512([data]) 返回一个用于SHA512算法的hash对象。
hashlib.blake2b([data], *, digest_size=64) 返回一个用于BLAKE2B算法的hash对象。
hashlib.blake2s([data], *, digest_size=32) 返回一个用于BLAKE2S算法的hash对象。
hashlib.sha3_224([data]) 返回一个用于SHA3_224算法的hash对象。
hashlib.sha3_256([data]) 返回一个用于SHA3_256算法的hash对象。
hashlib.sha3_384([data]) 返回一个用于SHA3_384算法的hash对象。
hashlib.sha3_512([data]) 返回一个用于SHA3_512算法的hash对象。
hashlib.new(name[, data]) 返回指定算法的hash对象,支持的算法名称包括md5、sha1、sha256、sha512、blake2b、blake2s、sha3_224、sha3_256、sha3_384、sha3_512等。
hash.update(data) 使用给定的字节串更新hash对象。
hash.digest() 返回hash对象的二进制形式的摘要值。
hash.hexdigest() 返回hash对象的十六进制形式的摘要值。

subprocess

subprocess模块是用于在Python中创建新的进程并与其进行交互的模块。它提供了多种方法和函数来执行外部命令并处理子进程的输入、输出和错误。

方法名 描述
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs) 运行命令行命令或可执行文件,并等待其完成执行。
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None) 执行命令行命令或可执行文件,返回一个Popen对象,用于与子进程进行交互。
Popen.communicate(input=None, timeout=None) 向子进程发送输入,并等待子进程终止。
Popen.wait(timeout=None) 等待子进程终止,并返回退出码。
Popen.poll() 检查子进程是否已经终止。
Popen.terminate() 终止子进程的执行。
Popen.kill() 强制终止子进程的执行。

面向对象

面向对象基础

什么是面向对象?

Python是一种面向对象的编程语言,它支持面向对象编程(Object-Oriented Programming,简称OOP)。面向对象编程是一种程序设计范型,它以对象作为程序的基本单元,通过封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)等概念来组织和管理代码。
在Python中,一切都是对象,包括数字、字符串、列表、函数等。对象是具有属性(属性是描述对象特征的变量)和方法(方法是对象能够执行的操作)的实体。面向对象编程将代码组织成对象的集合,这些对象之间通过交互来完成任务。

面向对象概念

概念 描述
类(Class) 对象的蓝图或模板,用于定义对象的属性和方法
对象(Object) 类的实例化结果,具有自己的属性和方法
属性(Attribute) 对象的特征,用于描述对象的状态
方法(Method) 对象能够执行的操作或行为,定义了对象的行为
封装(Encapsulation) 将相关的属性和方法组合在一起,并对外部隐藏对象内部的细节
继承(Inheritance) 允许一个类继承另一个类的属性和方法,子类可以重用父类的代码,并添加或修改其功能
多态(Polymorphism) 允许不同的对象对相同的方法做出不同的响应,提供了代码的灵活性和可扩展性

类的定义

在Python中,可以使用关键字class来定义一个类。类的定义通常包括类名、属性和方法。例如:

class MyClass:
# 属性
attribute = "some value"

# 方法
def my_method(self):
print("Hello, I'm a method in MyClass!")

定义类常见的几种形式

最基本的方式:使用class关键字直接定义类。例如:

class MyClass:
# 类的定义
pass

继承其他类:可以通过在类定义时在括号中指定要继承的父类,以创建一个派生类。

class ChildClass(ParentClass):
# 类的定义
pass

使用type()函数动态创建类:type()函数可以用于动态地创建类。它需要三个参数:类名、继承的父类元组(可选)和包含属性和方法的字典。例如:

MyClass = type('MyClass', (ParentClass,), {'attribute': 42, 'my_method': lambda self: print("Hello!")})

类的示例化

示例:

class MyClass:
def __init__(self, name):
self.name = name

def greet(self):
print("Hello, " + self.name + "!")

# 创建类的实例
my_obj = MyClass("John")

# 调用实例方法
my_obj.greet()

类的基本结构

示例:

class MyClass:
# 属性(变量)
my_variable = 123

# 方法(函数)
def my_method(self):
print("This is my method.")

# 创建类的实例
my_obj = MyClass()

# 调用实例方法
my_obj.my_method()

类的命名规则

规则 示例
使用大写字母开头 MyClass
使用驼峰命名法 MyAwesomeClass
使用描述性名称 Car, Person, Student
避免与Python关键字冲突 Classroom,而不是class
遵循命名约定 my_variable, my_method

类的三大特征

特征 描述
封装(Encapsulation) 将数据和操作封装在类中,通过公共接口控制对内部数据和方法的访问。
继承(Inheritance) 通过创建子类继承父类的属性和方法,子类可以扩展或重写父类的功能,实现代码的重用和扩展。
多态(Polymorphism) 同一个方法可以在不同的对象上表现出不同的行为,通过父类引用指向子类对象,实现统一的接口调用,具体执行方法取决于实际对象的类型。

类的私有属性和方法

在Python中,类的私有属性和方法是指以双下划线(__)开头的属性和方法。私有属性和方法在类的内部可见,外部无法直接访问。它们用于封装类的实现细节,防止外部直接修改或调用。示例:

class MyClass:
def __init__(self):
self.__private_attr = 42 # 私有属性

def __private_method(self): # 私有方法
print("This is a private method.")

def public_method(self):
print("This is a public method.")
self.__private_method() # 在类的内部可以访问私有方法
print(self.__private_attr) # 在类的内部可以访问私有属性


obj = MyClass()
obj.public_method() # 在类的外部可以调用公共方法
# obj.__private_method() # 无法在类的外部直接调用私有方法,会引发 AttributeError
# print(obj.__private_attr) # 无法在类的外部直接访问私有属性,会引发 AttributeError

动态添加类的属性和方法

在Python中,可以使用setattr()函数动态地添加类的属性,使用setattr()或直接通过点语法动态地添加类的方法。示例:

class MyClass:
pass

# 动态添加属性
obj = MyClass()
setattr(obj, 'name', 'John')
print(obj.name) # 输出: John

# 动态添加方法
def greet(self):
print("Hello, " + self.name + "!")

setattr(MyClass, 'greet', greet)

obj.greet() # 输出: Hello, John!

删除类的属性和方法

在Python中,可以使用 del 关键字或 delattr() 函数来删除类的属性,使用 del 关键字或 delattr() 函数来删除类的方法。示例:

class MyClass:
name = "John"

def greet(self):
print("Hello, " + self.name + "!")

# 删除属性
obj = MyClass()
print(obj.name) # 输出: John

del obj.name
# 或者使用 delattr(obj, 'name')

print(obj.name) # 引发 AttributeError

# 删除方法
del MyClass.greet
# 或者使用 delattr(MyClass, 'greet')

obj.greet() # 引发 AttributeError

类的属性

Python类提供的内置属性

属性名称 描述 使用场景
__name__ 类的名称 用于获取类的名称,在类的定义中进行检查或记录日志
__module__ 类所属的模块名称 用于获取类所属的模块名称,进行模块级别的操作或动态处理类
__doc__ 类的文档字符串 类的描述和文档,可以通过访问该属性来获取类的帮助信息
__dict__ 包含类或实例的命名空间的字典 动态查看、修改或添加类的属性和方法,实现元编程或动态属性的处理
__class__ 对象所属的类 获取对象所属的类信息,进行类型检查或基于类的操作

类的方法

类方法的分类

类的方法可以分为三种类型:实例方法(Instance Methods)、类方法(Class Methods)和静态方法(Static Methods)。以下是每种方法的描述:

方法类型 描述 使用场景
实例方法(Instance Methods) 在类中定义的普通方法,第一个参数通常为 self,表示当前实例对象 访问和操作实例的属性和方法,处理和修改实例的状态,执行实例特定的操作
类方法(Class Methods) 使用 @classmethod 装饰器修饰的方法,第一个参数通常为 cls,表示当前类本身 操作和访问类级别的属性和方法,创建、操作或处理与整个类相关的数据,或提供与类相关的实用方法
静态方法(Static Methods) 使用 @staticmethod 装饰器修饰的方法,与类和实例无关 执行与类相关但与实例无关的操作,或作为工具函数提供实用方法

实例方法(Instance Methods)

class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print(f"Hello, my name is {self.name}.")

# 创建实例并调用实例方法
person = Person("Alice")
person.say_hello() # Output: Hello, my name is Alice.

类方法(Class Methods)

class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print(f"Hello, my name is {self.name}.")

# 创建实例并调用实例方法
person = Person("Alice")
person.say_hello() # Output: Hello, my name is Alice.

静态方法(Static Methods)

class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print(f"Hello, my name is {self.name}.")

# 创建实例并调用实例方法
person = Person("Alice")
person.say_hello() # Output: Hello, my name is Alice.

类的内置方法

Python类支持以下几种内置方法:

方法名称 描述
__init__ 构造方法,在创建对象时进行初始化
__str__ 返回对象的字符串表示形式
__repr__ 返回对象的可打印的字符串表示形式
__len__ 返回对象的长度
__getitem__ 获取对象的指定索引的元素
__setitem__ 设置对象的指定索引的元素
__delitem__ 删除对象的指定索引的元素
__iter__ 返回对象的迭代器
__next__ 返回迭代器的下一个元素
__contains__ 检查对象是否包含指定的元素
__eq__ 比较对象是否相等
__ne__ 比较对象是否不相等
__lt__ 比较对象是否小于
__gt__ 比较对象是否大于
__le__ 比较对象是否小于或等于
__ge__ 比较对象是否大于或等于
__call__ 使对象可调用,类似于函数调用
__enter__ 进入上下文管理器时的操作
__exit__ 退出上下文管理器时的操作
__getattr__ 获取对象不存在的属性时的处理逻辑
__setattr__ 设置对象属性时的处理逻辑
__delattr__ 删除对象属性时的处理逻辑
__getattribute__ 获取对象属性时的处理逻辑

__init__

构造方法是一种特殊的方法,在创建对象时被调用,用于对对象进行初始化。在 Python 中,构造方法的名称固定为 __init__,它在类实例化时自动调用,并且是类中的必要方法之一。简单来说就是类在实例化的时候给类初始化的参数或方法都会在构造方法中执行

构造方法的特点

特点 描述
名称 __init__
调用时机 在创建对象时自动调用
参数 可以接受参数,用于初始化对象的属性
对象初始化 可以在构造方法中执行必要的初始化操作,如设置对象的初始状态、初始化属性等
self 参数 构造方法的第一个参数必须是 self,表示当前对象的引用
返回值 不需要返回任何值,会自动返回新创建的对象

示例:构造方法的用法

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def introduce(self):
print(f"My name is {self.name} and I am {self.age} years old.")


# 创建 Person 对象并初始化
person = Person("Alice", 25)

# 调用对象的方法
person.introduce() # 输出:My name is Alice and I am 25 years old.

__new__

new 方法是一个特殊的方法,在创建对象时被调用,用于创建并返回对象的实例。它是类的元方法(metamethod),用于控制对象的创建过程。与构造方法 init 不同,new 方法在对象创建之前被调用,负责创建对象,并返回一个新的实例。

特点 描述
名称 __new__
调用时机 在创建对象时自动调用,早于构造方法 __init__ 被调用
参数 第一个参数是类本身,之后可以接收额外的参数,用于控制对象创建过程
对象创建 __new__ 方法负责创建对象,并返回一个新的实例
返回值 必须返回一个对象实例

示例:__new__方法的使用

class Singleton:
instance = None

def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = super().__new__(cls)
return cls.instance


# 创建 Singleton 对象
obj1 = Singleton()
obj2 = Singleton()

# 判断两个对象是否相同
print(obj1 is obj2) # 输出: True

__init____new__方法的区别

特点 __new__ 方法 __init__ 方法
调用时机 在对象创建之前被调用 在对象创建之后被调用
参数 第一个参数是类本身,之后可以接收额外的参数 第一个参数是新创建的对象实例 self,之后可以接收其他参数
返回值 必须返回一个对象实例 无需返回值,作为初始化方法使用
功能 控制对象的创建过程,负责创建并返回一个新的实例 初始化对象的属性,对新创建的对象进行必要的设置和初始化操作
调用顺序 __new__ 方法创建并返回对象实例后,__init__ 方法被调用 __new__ 方法创建对象实例,然后调用 __init__ 方法进行初始化
总结 在对象创建之前被调用的方法,用于控制对象的创建过程,返回一个对象实例。 在对象创建之后被调用的方法,用于对新创建的对象进行初始化操作,设置对象的属性等。

__del__

在Python中,__del__是一个特殊方法(也称为魔术方法或魔术函数),用于定义类的析构函数。析构函数在对象被销毁(即没有任何引用指向该对象时)时自动被调用。
__del__方法的作用是在对象被销毁前执行一些清理操作,例如释放资源、关闭文件、断开网络连接等。它可以用来处理对象的收尾工作,以确保在对象不再被使用时执行必要的清理操作。

特殊方法 描述
__del__ 定义类的析构函数。在对象被销毁时自动调用。
触发时机 当对象的引用计数归零时,即没有任何引用指向该对象时,垃圾回收机制可能会触发调用 __del__ 方法。注意,垃圾回收的具体时机可能会有差异,因为它依赖于Python解释器的实现。
功能 执行对象的清理操作,例如释放资源、关闭文件、断开网络连接等。
参数 self(隐式参数):指向当前对象的引用。没有其他参数。
返回值 __del__ 方法没有定义返回值,因此通常不需要显式返回任何内容。

案例:读取文件时,使用析构函数自动关闭文件

import os

class ReadFile():
def __new__(cls, read_file):
#判断文件是否存在
if os.path.exists(read_file):
#文件存在创建一个对象
return object.__new__(cls)

else:
return print("{} 文件不存在" .format(read_file))

def __init__(self,read_file):
#接收传入的读取的文件
self.read_file = read_file
#打开文件操作
self.f = open(read_file,mode='r',encoding='utf-8')

#使用析构方法,析构放在在程序执行返程之后执行;所以通过析构方法将文件关闭
def __del__(self):
self.f.close()

def readcontent(self):
content = self.f.read()
return content

obj = ReadFile('test.txt')
res = obj.readcontent()
print(res)

__str__

__str__是一个特殊方法(也称为魔术方法或魔术函数)在Python中的类中定义。它用于返回一个对象的字符串表示形式,以便可以方便地打印、显示或转换为字符串。
当我们对一个对象使用内置的str()函数或者在打印时需要将对象转换为字符串时,Python会自动调用对象的__str__方法来获取其字符串表示形式。
__str__方法的作用是提供一个可读性良好且人类可理解的对象描述。通常,它会返回一个字符串,其中包含有关对象的信息,以便更好地理解对象的状态或特征。

特殊方法 描述
__str__ 定义对象的字符串表示形式。在需要将对象转换为字符串或打印对象时自动调用。
触发时机 当我们使用内置的 str() 函数将对象转换为字符串,或直接对对象进行打印操作时,Python会自动调用对象的 __str__ 方法来获取其字符串表示形式。
功能 返回对象的字符串表示形式,用于提供可读性良好且人类可理解的对象描述。
参数 self(隐式参数):指向当前对象的引用。没有其他参数。
返回值 __str__ 方法应返回一个字符串,其中包含了对象的描述信息。这个字符串将用于打印或转换对象为字符串。

案例

class Cat():
gift = "猫有九条命"
def __init__(self,name):
self.name = name

def __str__(self):
return self.info()

def info(self):
return "{} 小猫有故事".format(self.name)

obj = Cat("tom")
res = str(obj)
print(res)

__repr

__repr__是一个特殊方法(也称为魔术方法或魔术函数)在Python中的类中定义。它用于返回一个对象的规范字符串表示形式,以便可以准确地重新创建该对象。
当我们在交互式环境中直接输入对象名并按回车键时,Python会自动调用对象的__repr__方法来获取其规范字符串表示形式。
__repr__方法的作用是提供一个准确而详细的对象描述,以便可以重新创建该对象。它通常返回一个字符串,其中包含有关对象的重要属性和状态的信息。

案例:如何使用

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __repr__(self):
return f"Point({self.x}, {self.y})"

# 创建一个Point对象
point = Point(3, 4)

# 在交互式环境中直接输入对象名并按回车键
point # 输出: Point(3, 4)

__call__

__call__是一个特殊方法(也称为魔术方法或魔术函数)在Python中的类中定义。它使得一个对象可以像函数一样被调用。
当我们使用类的实例作为函数并提供参数时,Python会自动调用该对象的__call__方法来执行对象的行为。这样,我们可以通过在类中定义__call__方法来使对象变得可调用。
__call__方法的作用是定义一个对象的可调用行为,使其可以像函数一样被调用。可以在__call__方法中编写希望对象被调用时执行的逻辑和操作。

特殊方法 描述
__call__ 定义对象的可调用行为,使其可以像函数一样被调用。
触发时机 当将一个对象作为函数调用时,即通过在对象后面加上括号并提供相应的参数时,Python会自动调用该对象的 __call__ 方法。
功能 执行对象的行为和逻辑,实现对象的可调用行为,使其具备函数的功能。
参数 self(隐式参数):指向当前对象的引用。此外,可以根据需要接受其他参数,这些参数取决于对象的具体实现。
返回值 __call__ 方法可以返回任何适合的值,取决于对象的行为和逻辑。返回值的类型和含义由对象的实现决定。

案例:如何使用__call__方法

class Adder:
def __call__(self, a, b):
return a + b

# 创建一个Adder对象
adder = Adder()

# 调用对象
result = adder(3, 4)
print(result) # 输出: 7

__bool__

bool()是Python的内置函数,用于返回给定对象的布尔值。同时,在类中,可以通过定义__bool__()__len__()方法来实现自定义对象的布尔值。
当我们使用bool()函数来检查对象的布尔值时,它会尝试调用对象的__bool__()方法(如果存在),或者__len__()方法(如果__bool__()方法不存在)来获取布尔值。如果__bool__()__len__()方法都不存在,那么对象将被认为是真值(True)。

方法/函数 描述
bool()函数 返回给定对象的布尔值。根据对象的类型和实现方式,它可能会调用对象的__bool__()__len__()方法来获取布尔值。
触发时机 当调用bool()函数并传递一个对象作为参数时,Python会尝试调用对象的__bool__()方法(如果存在),或者__len__()方法(如果__bool__()方法不存在)来获取布尔值。如果这两个方法都不存在,那么对象将被认为是真值(True)。
功能 返回给定对象的布尔值,用于判断对象是否为真或假。
参数 对象(作为bool()函数的参数)或者隐式的self参数(__bool__()方法)。
返回值 bool()函数返回一个布尔值,即TrueFalse__bool__()方法返回一个布尔值,表示对象的布尔值。

案例:如何使用__bool__方法

class CustomObject:
def __bool__(self):
return False

obj = CustomObject()

print(bool(obj)) # 输出: False

__add__

__add__是一个特殊方法(也称为魔术方法或魔术函数)在Python中的类中定义。它用于定义对象之间的加法操作。
当我们使用加法运算符(+)将两个对象相加时,Python会自动调用第一个对象的__add__方法,并将第二个对象作为参数传递给该方法。
__add__方法的作用是定义对象之间的加法操作。它可以根据对象的类型和实现方式来执行相应的加法操作,并返回结果。

特殊方法 描述
__add__ 定义对象之间的加法操作。当使用加法运算符(+)将两个对象相加时,Python会自动调用第一个对象的__add__方法,并将第二个对象作为参数传递给该方法。
触发时机 当使用加法运算符(+)将两个对象相加时,Python会自动调用第一个对象的__add__方法。
功能 执行对象之间的加法操作,并返回结果。
参数 self(隐式参数):指向当前对象的引用。other:第二个对象,作为参数传递给__add__方法。
返回值 __add__ 方法应返回一个新的对象,代表两个对象相加的结果。返回值的类型和内容根据对象的具体实现方式决定。

案例:使用add方法

class Vector:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector(new_x, new_y)

# 创建两个Vector对象
v1 = Vector(1, 2)
v2 = Vector(3, 4)

# 使用加法运算符相加
result = v1 + v2

print(result.x) # 输出: 4
print(result.y) # 输出: 6

类的继承

什么是类的继承

类的继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类、基类或超类)继承属性和方法。
在继承关系中,子类继承了父类的属性和方法,并可以在此基础上添加自己的属性和方法,或者重写父类的方法以改变其行为。
继承的主要目的是实现代码重用和建立类之间的关系。通过继承,子类可以获得父类的特征和行为,并且可以根据需要进行修改、扩展或特殊化。这样可以提高代码的可维护性、可扩展性和可重用性。
子类可以继承父类的公有属性和方法,这些属性和方法可以直接在子类中使用。如果在子类中定义了与父类同名的方法,则子类的方法将覆盖(重写)父类的方法。
继承还可以形成多层次的继承关系,即一个类可以作为多个子类的父类,这样就形成了一个继承层次结构。
通过继承,可以更好地组织和管理类的层次结构,并促进代码的复用和模块化。它是面向对象编程的一个重要特性,提供了灵活而强大的代码组织和扩展机制。

类继承的基本语法

class ParentClass:
# 父类的定义

class ChildClass(ParentClass):
# 子类的定义

类继承的分类

类的继承可以分为子类和父类

名词 描述
子类(衍生类) 一个类通过继承其他类而创建的新类,它继承了父类的属性和方法,并可以添加自己的属性和方法。子类也被称为衍生类,因为它是从父类衍生出来的。
父类(基类、超类) 被子类继承的类,它是子类的源头,提供了子类所继承的属性和方法。父类也被称为基类或超类,因为它是衍生其他类的基础。

类继承的方式

类继承的方式分为两种:单继承、多继承、多层继承、菱形继承
单继承:单继承是指一个子类只继承自一个父类。在Python中,一个类可以直接继承自另一个类,即单继承的关系。

class ParentClass:
pass

class ChildClass(ParentClass):
pass

多继承:多继承是指一个子类可以继承自多个父类。在Python中,可以通过在类定义时指定多个父类来实现多继承。

class ParentClass1:
pass

class ParentClass2:
pass

class ChildClass(ParentClass1, ParentClass2):
pass

多层继承:多层继承是指一个子类继承自另一个子类,而该子类又继承自另一个父类的情况。通过多层继承,可以形成一个继承层次结构。

class GrandParentClass:
pass

class ParentClass(GrandParentClass):
pass

class ChildClass(ParentClass):
pass

菱形继承:菱形继承是多继承中的一种特殊情况,其中一个子类继承自两个直接父类,而这两个直接父类又共同继承自同一个父类。

class GrandParentClass:
pass

class ParentClass1(GrandParentClass):
pass

class ParentClass2(GrandParentClass):
pass

class ChildClass(ParentClass1, ParentClass2):
pass

多继承成员属性、方法的优先级

在多继承中,当一个类继承自多个父类时,如果这些父类中存在同名的成员属性或方法,就会涉及到成员的优先级问题。Python使用方法解析顺序(Method Resolution Order,简称MRO)来确定成员的访问顺序。MRO 定义了在多继承中查找成员的顺序。
在 Python 3 中,可以使用 class.mro() 方法来查看类的方法解析顺序。MRO 的顺序是根据 C3 线性化算法计算得出的。
以下是多继承中成员属性和方法的优先级顺序的原则:

从左到右:在类定义时,继承的父类列表中的顺序决定了方法解析顺序。首先会从左边的父类开始查找成员,然后依次向右查找。
深度优先:如果多个父类都具有相同的方法或属性,会按照深度优先的原则进行查找。即先在当前类中查找,然后再查找第一个父类的方法,接着查找第二个父类的方法,依此类推。
遵循MRO顺序:Python使用C3线性化算法计算方法解析顺序,以确定继承树中的成员查找顺序。这个算法保证了方法解析顺序的一致性和可预测性。
下面是一个示例来说明多继承中成员属性和方法的优先级:

class A:
def foo(self):
print("A's foo")

class B:
def foo(self):
print("B's foo")

class C(A, B):
pass

c = C()
c.foo() # 输出: A's foo

super的用法

在Python中,super() 是一个内置函数,用于调用父类的方法。它提供了一种方便的方式来访问父类的属性和方法,以实现继承中的方法重写和扩展。
super() 函数的一般用法是在子类中的方法中调用父类的同名方法。通过使用 super(),可以避免直接使用父类的类名,并且可以确保调用正确的父类方法,特别是在多继承的情况下。
super() 函数的基本语法如下:

super().method_name(arguments)

在上述语法中,method_name 是要调用的父类方法的名称,arguments 是要传递给父类方法的参数。调用 super().method_name() 会自动找到当前子类所继承的父类,并调用父类中对应的方法。
以下是 super() 的常见用法示例:

class ParentClass:
def method(self):
print("ParentClass method")

class ChildClass(ParentClass):
def method(self):
super().method()
print("ChildClass method")

# 创建子类对象
child = ChildClass()
child.method()

issubclass判断类继承关系

issubclass() 是Python中的一个内置函数,用于判断类之间的继承关系。它可以确定一个类是否是另一个类的子类。语法:

issubclass(类, 类信息)

案例:

class 车辆:
pass

class 小汽车(车辆):
pass

class 摩托车(车辆):
pass

print(issubclass(小汽车, 车辆)) # 输出: True
print(issubclass(摩托车, 车辆)) # 输出: True
print(issubclass(车辆, 小汽车)) # 输出: False

isinstances判断类的类型

isinstance() 是 Python 中的一个内置函数,用于检查一个对象是否属于指定的类或类型。语法:

isinstance(对象, 类或类型)

案例:

class 动物:
pass

class (动物):
pass

class (动物):
pass

猫咪 = 猫()
小狗 = 狗()

print(isinstance(猫咪, 猫)) # 输出: True
print(isinstance(猫咪, 动物)) # 输出: True
print(isinstance(小狗, 猫)) # 输出: False
print(isinstance(小狗, 动物)) # 输出: True

类的多态

什么是类的多态

不通的子类对象,调用相同的父类方法,产生不同的结果叫做类的多态;通俗的讲,多态就是类的继承和子类方法的改写。案例:

class Animal:
def sound(self):
pass

class Cat(Animal):
def sound(self):
print("Meow")

class Dog(Animal):
def sound(self):
print("Woof")

def make_sound(animal):
animal.sound()

cat = Cat()
dog = Dog()

make_sound(cat) # 输出: Meow
make_sound(dog) # 输出: Woof

类的反射

什么是类的反射

在面向对象编程中,类的反射(Reflection)是指在运行时动态地获取、检查和操作类的信息和属性。通过反射,可以在程序运行时查询类的属性、方法和其他元数据,并且可以动态地调用方法、访问属性、创建对象等操作,而无需提前知道类的具体信息。
类的反射提供了一种动态地操作类的方式,使得代码更加灵活和可扩展。它可以在运行时根据需要查看和修改类的结构和行为,从而实现一些高级的编程技巧和模式。
常见的类反射操作包括:

操作 描述
getattr(obj, name) 获取对象 obj 的属性 name 的值。
hasattr(obj, name) 检查对象 obj 是否具有属性 name,返回布尔值。
setattr(obj, name, value) 设置对象 obj 的属性 name 的值为 value
delattr(obj, name) 删除对象 obj 的属性 name
globals() 获取当前的全局命名空间,返回一个字典,其中包含全局作用域中定义的变量和函数。
locals() 获取当前的局部命名空间,返回一个字典,其中包含局部作用域中定义的变量和函数。
type(name, bases, dict) 动态创建类,name 是类的名称,bases 是父类的元组,dict 是包含类的属性和方法的字典。
issubclass(class, classinfo) 检查 class 是否是 classinfo 的子类,返回布尔值。
__doc__ 类的文档字符串属性,包含类的说明和用法等文档信息。
__name__ 类的名称属性,包含类的名称。

类反射案例

class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print("Hello, I'm", self.name)

# 动态创建类的实例对象
obj = globals()['Person']("John")

# 获取属性值和调用方法
print(getattr(obj, "name")) # 输出: John
getattr(obj, "say_hello")() # 输出: Hello, I'm John

类的装饰器

什么是类的装饰器

在不修改被装饰函数函数体及函数调用的情况下,使用装饰器函数为被装饰器函数扩展功能。

类内置装饰器

装饰器 描述
@property 将方法转换为只读属性,可以通过访问属性的方式调用方法。
@classmethod 将方法转换为类方法,可以通过类调用方法而不需要创建类的实例。
@staticmethod 将方法转换为静态方法,可以通过类调用方法而不需要传递实例或类参数。
@abstractmethod 声明一个抽象方法,子类必须实现该方法。
@final 声明一个方法或类为最终的,不能被子类覆盖或继承。
@cached_property 缓存属性的结果,减少重复计算的开销。
@wraps 用于装饰装饰器函数,使被装饰的函数保留原始函数的元数据(名称、文档字符串等)。

装饰器的符号

类装饰器使用 @ 符号来应用装饰器。在定义类时,可以在类的上方使用 @装饰器名 的语法来应用类装饰器。 下面是一个示例,展示类装饰器使用 @ 符号的语法:

@decorator
class MyClass:
pass

装饰器@的作用

可以自动把@符号下面的函数当成参数传递给装饰器
把新函数返回,让新函数去替换旧函数,实现功能扩展

装饰器基本语法

def decorator(cls):
# 对类进行操作或修改
# ...
return cls

@decorator
class MyClass:
# 类定义
pass

有参类装饰器

def decorator_factory(arg1, arg2, ...):
def decorator(cls):
# 对类进行操作或修改,可以使用参数 arg1, arg2, ...
# ...
return cls
return decorator

@decorator_factory(arg1, arg2, ...)
class MyClass:
# 类定义
pass

案例:如果参数为1,添加类属性和方法;如果参数为2将run方法变为属性

class Wapper():
ad = "装饰器广告"

def __init__(self,num):
self.num = num

#把对象当作函数使用,需要用call方法
def __call__(self, cls):
if self.num == 1:
return self.func1(cls)

elif self.num == 2:
return self.func2(cls)

def money(self):
print("测试")

#参数为1的情况
def func1(self,cls):
def func():
#为当前cls这个类,添加属性
cls.ad = Wapper.ad
#为当前cls这个类,添加方法
cls.money = Wapper.money
return cls() #返回对象
return func
#参数为2的情况
def func2(self,cls):
def func():
#判断类中有run的方法
if "run" in cls.__dict__:
#调用类中的方法,拿到返回值
res = cls.run()
#把返回值重新赋值给run属性,后者覆盖了前者,方法变成了属性
cls.run = res
return cls()
return func


@Wapper(2)
class MyClass():
def run():
return "亢龙有悔"

obj = MyClass()
print(obj.run)

property

property 装饰器是一个内置装饰器,用于创建属性(property)的 getter、setter 和 deleter 方法,使其能够以属性的方式访问和修改。语法:

class MyClass:
def __init__(self):
self._my_property = None

@property
def my_property(self):
return self._my_property

@my_property.setter
def my_property(self, value):
self._my_property = value

@my_property.deleter
def my_property(self):
del self._my_property

classmethod

声明一个方法为类方法,可以通过类直接调用而不需要实例化类。例如:

class MyClass:
@classmethod
def my_class_method(cls, arg1, arg2, ...):
# 类方法的逻辑
# 可以使用 cls 访问类本身
# ...
MyClass.my_class_method(arg1, arg2, ...)

staticmethod

将方法转换为静态方法,可以通过类调用方法而不需要传递实例或类参数。例如:

class MyClass:
@staticmethod
def my_static_method(arg1, arg2, ...):
# 静态方法的逻辑
# 不依赖于实例,也无法访问实例属性
# ...
MyClass.my_static_method(arg1, arg2, ...)

类方法和静态方法的区别

classmethod staticmethod
访问类属性和方法 无法直接访问类属性和方法
第一个参数是类本身 (cls) 没有隐含的类参数
可以操作实例和类 不依赖于实例,无法访问实例属性和方法
用于操作类级别的数据和行为 用于定义与类相关但与实例无关的功能
子类继承时 cls 指向子类 子类继承时行为与父类相同,仅限于父类本身的属性和方法

网络编程

Python常见网络编程的模块:

库/模块 功能描述
socket 创建网络套接字,实现底层的网络通信,支持TCP和UDP等协议
requests 发送HTTP请求和处理HTTP响应的简洁API
urllib 进行URL解析、构建和发送HTTP请求
http.server 提供简单的HTTP服务器功能,用于搭建基本的Web服务器
http.client 提供低级别的HTTP客户端功能,发送HTTP请求和处理HTTP响应
socketserver 提供基于TCP和UDP的高级别网络服务器框架
asyncio 异步I/O库,提供基于事件循环的异步编程模型
paramiko SSH协议的实现,实现远程命令执行、文件传输等功能
ftplib 提供FTP协议的客户端功能,实现文件上传、下载等操作
smtplib 提供SMTP协议的客户端功能,用于发送电子邮件
imaplib 提供IMAP协议的客户端功能,用于接收和管理电子邮件
poplib 提供POP3协议的客户端功能,用于接收电子邮件

socket

socket模块内置方法

方法 描述
socket.socket() 创建一个新的套接字对象
socket.bind(address) 绑定套接字到指定的地址和端口
socket.listen(backlog) 开始监听传入连接请求,参数指定最大等待连接数
socket.accept() 接受客户端的连接请求,并返回新的套接字对象和客户端地址
socket.connect(address) 连接到指定的远程地址和端口
socket.send(bytes) 发送字节数据到连接的套接字
socket.recv(bufsize) 接收并返回最多指定字节数的数据
socket.sendall(bytes) 完整地发送所有字节数据,直到全部发送完成
socket.recvfrom(bufsize) 接收数据并返回数据和发送方的地址
socket.sendto(bytes, address) 发送数据到指定的目标地址
socket.close() 关闭套接字连接

TCP和UDP

# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

案例:同步服务端指定目录下文件或目录到客户端

服务端代码:

import socket
import os

class Server:
def __init__(self, server_dir):
self.server_dir = server_dir

def start(self):
# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定地址和端口
server_address = ('localhost', 12345)
server_socket.bind(server_address)

# 开始监听连接请求
server_socket.listen(5)

while True:
# 接受客户端连接
client_socket, client_address = server_socket.accept()
print('Received connection from:', client_address)

# 扫描服务端目录
file_list = os.listdir(self.server_dir)

# 同步文件和目录到客户端
for file_name in file_list:
file_path = os.path.join(self.server_dir, file_name)
if os.path.isfile(file_path):
# 同步文件
with open(file_path, 'rb') as file:
file_data = file.read()
client_socket.sendall(b'file:' + file_name.encode() + b':' + file_data)
elif os.path.isdir(file_path):
# 同步目录
client_socket.sendall(b'dir:' + file_name.encode())

# 发送同步完成的标识
client_socket.sendall(b'sync_complete')

# 关闭连接
client_socket.close()

# 创建Server对象并启动服务端
server_dir = '/path/to/server/directory/'
server = Server(server_dir)
server.start()

客户端代码:

import socket
import os

class Client:
def __init__(self, client_dir):
self.client_dir = client_dir

def connect(self, server_address):
# 创建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务端
client_socket.connect(server_address)

# 接收同步数据
while True:
data = client_socket.recv(1024)
if not data:
break

# 解析同步数据
data_parts = data.split(b':')
if data_parts[0] == b'file':
# 同步文件
file_name = data_parts[1].decode()
file_data = data_parts[2]

file_path = os.path.join(self.client_dir, file_name)
with open(file_path, 'wb') as file:
file.write(file_data)
print('Received file:', file_name)
elif data_parts[0] == b'dir':
# 同步目录
dir_name = data_parts[1].decode()

dir_path = os.path.join(self.client_dir, dir_name)
os.makedirs(dir_path, exist_ok=True)
print('Created directory:', dir_name)

# 接收同步完成的标识
complete_data = client_socket.recv(1024)
if complete_data == b'sync_complete':
print('Synchronization completed')

# 关闭连接
client_socket.close()

# 创建Client对象并连接服务端
client_dir = '/path/to/client/directory/'
client = Client(client_dir)
client.connect(('localhost', 12345))

进程

进程基础

什么是进程?

在Python中,进程(Process)是操作系统中正在执行的一个独立单位。它是程序执行的一个实例,具有独立的内存空间、资源和执行流。每个进程都有自己的地址空间,可以包含多个线程。

进程间如何通讯?

IPC方式 描述
队列(Queue) 队列是一种线程安全的数据结构,可以在进程之间传递数据。
管道(Pipe) 管道是一种单向的连接,可以在进程之间传递数据。
共享内存(Shared Memory) 共享内存是一种高效的进程间通信方式,可以让多个进程访问同一块内存区域。
套接字(Socket) 套接字是一种网络编程的通用接口,也可以在同一台机器上的不同进程之间进行通信。

并行和并发

并行:指的是同时执行多个任务,它要求系统具有多个处理单元或处理器,以便可以同时处理多个任务。在并行执行中,每个任务都有自己的执行线程或进程,并且可以在不同的处理器上同时执行。并行执行可以显著提高任务的执行速度,特别是在涉及大量计算的场景中。
并发:指的是任务在时间上重叠执行,尽管不一定是同时进行的。在并发执行中,多个任务交替执行,每个任务都可能分配一定的执行时间片段,并且通过时间片轮转或者调度算法来切换执行。并发执行的目标是增加系统资源的利用率,提高任务的响应性和吞吐量。
并行和并发的区别:

并行 并发
定义 多个任务在同一时刻同时执行,利用多个处理单元或处理器,以提高执行速度。 多个任务在时间上重叠执行,任务可能交替执行,以提高系统资源利用率和响应性。
执行方式 同时执行多个任务 任务交替执行
目标 提高执行速度 提高系统资源利用率和响应性

CPU调度算法

调度算法 描述
先来先服务(FCFS) 按照任务到达的顺序进行调度,先到达的任务先执行。
最短作业优先(SJF) 选择剩余执行时间最短的任务进行调度,以减少平均等待时间。
优先级调度 每个任务分配一个优先级,优先级高的任务优先执行。
时间片轮转(RR) 每个任务被分配一个固定的时间片,任务按照时间片轮流执行,直到完成或时间片用完。
最高响应比优先(HRRN) 根据任务等待时间和服务时间的比值选择优先级,等待时间越长且服务时间短的任务优先执行。
最短剩余时间优先(SRTF) 在每个时间片内选择剩余执行时间最短的任务进行调度,以减少等待时间和响应时间。
多级反馈队列调度(MFQ) 将任务分为多个队列,每个队列具有不同的优先级和时间片大小,任务根据优先级和时间片进行调度。
最佳优先权调度 根据任务的优先权选择最高优先权的任务进行调度。

进程的基本语法

在Python中,可以使用multiprocessing模块来创建和管理进程。下面是使用multiprocessing模块的基本语法:

import multiprocessing

def worker():
# 进程要执行的任务
print("Worker process")

if __name__ == "__main__":
# 创建进程对象
process = multiprocessing.Process(target=worker)

# 启动进程
process.start()

# 等待进程执行结束
process.join()

进程带参数的基本语法

import multiprocessing

def worker(name):
# 进程要执行的任务
print(f"Worker process with name: {name}")

if __name__ == "__main__":
# 创建进程对象,并传递参数
process = multiprocessing.Process(target=worker, args=("Alice",))

# 启动进程
process.start()

# 等待进程执行结束
process.join()

什么是进程的join

在多进程编程中,join()是一个方法,用于等待进程执行完毕并回收资源。当调用join()方法时,主线程(或者父进程)会阻塞,直到所关联的进程执行完毕。
具体而言,join()方法会使主线程等待调用该方法的进程执行完毕。在等待期间,主线程会被阻塞,直到进程执行完毕才会继续执行后面的代码。
使用join()方法可以确保在主线程继续执行之前,所有创建的子进程都已经执行完毕。这对于需要在子进程执行完毕后获取结果或者进行清理操作的情况非常有用。示例:

import multiprocessing
import time

def worker():
print("Worker process")
time.sleep(2)
print("Worker process completed")

if __name__ == "__main__":
process = multiprocessing.Process(target=worker)
process.start()

print("Main process")

# 等待进程执行完毕
process.join()

print("Main process completed")

使用自定义类创建进程

要使用自定义类创建进程,需要继承multiprocessing.Process类并重写run()方法。run()方法中定义了进程要执行的任务。示例:

import multiprocessing

class MyProcess(multiprocessing.Process):
def run(self):
# 进程要执行的任务
print("Worker process")

if __name__ == "__main__":
# 创建自定义类的进程对象
process = MyProcess()

# 启动进程
process.start()

# 等待进程执行结束
process.join()

守护进程

什么是守护进程

守护进程就是守护主进程,如果主进程执行结束了,立刻终止程序。

守护进程的特点

特点 描述
后台运行 守护进程在后台运行,不与任何用户交互或关联。
系统启动时启动 守护进程通常在系统启动时启动,可以作为系统服务或后台任务。
无控制终端 守护进程没有控制终端,无法接收用户输入或输出到终端。
用于系统级任务 守护进程通常用于执行系统级任务、服务或常驻程序,如网络服务、定时任务、日志记录等。
随系统关闭而终止 守护进程会在系统关闭时被自动终止,或可以通过其他方式手动停止。
独立于用户会话 守护进程独立于用户会话,与用户登录状态无关。
提供特定的服务 守护进程在后台默默地运行并提供特定的服务,无需用户干预或注意。

守护进程基本语法

from multiprocessing import Process

def func():
print("开始当前子进程……")
print("结束当前子进程……")

if __name__ == '__main__':
p = Process(target=func)
p.daemon = True
p.start()

print("主程序执行结束……")

守护进程应用场景

应用场景 描述
网络服务 后台运行的守护进程,用于提供Web服务器、FTP服务器、数据库服务器等网络服务。
定时任务 守护进程执行预定的定时任务,如备份数据、生成报表、发送电子邮件等。
日志记录 守护进程实时或定期记录系统日志或应用程序日志,用于监视关键事件、错误信息和系统状态。
后台处理 处理后台任务,如数据处理、图像处理、批处理任务等。守护进程在后台运行,避免阻塞用户界面或其他重要任务。
消息队列处理 从消息队列中获取消息并进行处理、转发、存储等操作的守护进程。
守护监控程序 监控系统状态、资源利用率、服务可用性等的守护进程。周期性收集和分析系统数据,触发警报或采取相应的措施。
硬件控制 与硬件设备交互和控制的守护进程,如传感器数据采集、机器人控制、自动化系统等。

进程阻塞

进程阻塞的作用

进程阻塞是指一个进程在执行过程中被暂停,等待某个条件满足或某个事件发生。进程阻塞的作用是为了合理利用系统资源,提高系统的效率和性能。下面是一些进程阻塞的常见作用:

作用 描述
同步操作 进程阻塞用于实现多个进程或线程之间的同步操作,确保任务按照特定的顺序执行。
资源限制和调度 进程阻塞帮助操作系统进行资源限制和调度,根据资源的可用性和优先级合理分配资源,提高系统性能和公平性。
输入/输出操作 进程阻塞等待输入/输出操作完成,如读取文件、网络传输数据或等待用户输入。
进程间通信 进程阻塞用于进程间通信,等待其他进程发送消息或共享内存中的数据。
避免忙等待 进程阻塞避免忙等待,即不断轮询某个条件是否满足的情况,释放CPU资源,等待条件满足后继续执行,避免不必要的资源消耗。
事件驱动 进程阻塞等待特定事件的发生,当事件触发时才继续执行,充分利用系统资源,避免不必要的计算或处理。
超时处理 进程阻塞可以设置超时时间,如果在指定时间内未满足条件或事件未发生,进程可以执行相应的超时处理逻辑,避免无限等待导致系统资源浪费。

什么是信号量

信号量(Semaphore)是一种用于控制对共享资源的访问的同步机制。它是一种计数器,用于记录可用的资源数量。信号量主要用于多进程或多线程环境中,用于解决并发访问共享资源时可能出现的竞争条件和互斥问题。
信号量维护了一个整数值,通常为非负数。该值表示当前可用的资源数量。它支持两种基本操作:原子地增加信号量的值(通常称为”V”操作或”释放”操作)和原子地减少信号量的值(通常称为”P”操作或”获取”操作)。
当一个进程或线程需要访问一个共享资源时,它首先执行”获取”操作,如果信号量值大于0,表示有可用的资源,该进程或线程可以访问共享资源,并将信号量的值减1。如果信号量值为0,表示没有可用的资源,那么进程或线程会被阻塞,等待信号量的值变为大于0。当另一个进程或线程完成对共享资源的访问后,执行”释放”操作,将信号量的值加1,这样等待的进程或线程可以继续执行。
信号量提供了一种有效的机制来控制并发访问共享资源,避免了竞争条件和互斥问题。它可以用于限制资源的访问数量、实现进程间的同步和互斥,以及解决其他与并发访问相关的问题。在实际应用中,信号量是多线程编程和并发控制中常用的同步原语之一。
在 Python 中,可以使用 Semaphore 类来创建和管理信号量。Semaphore 类是 threading 模块提供的一种同步原语,用于控制对共享资源的访问。以下是Semaphore常用的方法:

方法 描述
acquire() 获取资源。如果信号量的值大于 0,表示有可用资源,进程可以继续执行。如果信号量的值为 0,表示没有可用资源,进程会被阻塞,直到有资源可用为止。
release() 释放资源。每次调用该方法,信号量的值会增加 1,表示有一个资源可用。如果有其他进程正在等待资源,其中一个进程将被唤醒并获得资源。
__enter__() acquire() 方法一起使用,支持上下文管理器的语法,可使用 with 语句自动获取和释放资源。
__exit__() release() 方法一起使用,支持上下文管理器的语法,在 with 语句结束时自动释放资源。
locked() 返回一个布尔值,表示当前信号量是否被锁定。
get_value() 返回当前信号量的值,即可用资源的数量。

使用案例:

import threading

# 创建信号量对象
semaphore = threading.Semaphore(2)

def worker():
# 获取资源
semaphore.acquire()
print("Worker acquired resource.")

# 模拟工作
print("Worker is working...")

# 释放资源
semaphore.release()
print("Worker released resource.")

# 创建多个工作线程
for _ in range(5):
t = threading.Thread(target=worker)
t.start()

进程事件

什么是进程事件

进程事件(Process Event)是指在操作系统中,用于通知和同步进程之间发生的特定事件的机制。进程事件可以是进程状态的变化、资源的可用性、其他进程的操作等。
进程事件用于进程间的通信和同步,使得进程能够相互感知和响应特定的事件。当某个事件发生时,操作系统可以通知等待该事件的进程,使其能够及时作出相应的处理。进程事件的实现可以基于操作系统提供的各种机制,如信号、消息队列、管道等。

进程事件的主要作用

主要作用 描述
进程间通信 进程事件用于不同进程之间的通信,实现进程之间的信息交换和同步操作。
进程同步 进程事件可以作为同步的标志,使得多个进程可以按照预期的顺序执行,确保特定的事件按照特定的顺序发生。
进程调度 进程事件可以用于操作系统的进程调度,根据不同进程的优先级和事件的发生情况,合理分配系统资源和切换进程。
任务协调 进程事件可以协调多个任务的执行顺序,确保任务按照预期的方式执行,避免竞争条件和互斥访问共享资源的问题。
异常处理 进程事件可以用于处理异常情况,当某个特定的异常事件发生时,进程可以及时捕获并进行相应的异常处理操作。
系统通知 进程事件可以用于操作系统向进程发送通知,例如系统状态变化、资源可用性改变等,以便进程及时作出相应的处理。

进程事件实现进程阻塞的步骤

步骤 描述
创建进程事件 创建一个进程事件,用于通知和同步进程之间的特定事件。
设置进程事件 设置进程事件的初始状态,通常为非触发状态。
进程等待事件 进程调用等待事件的方法,阻塞进程的执行,等待特定事件的发生。
触发进程事件 当某个特定条件满足时,触发进程事件,改变其状态为触发状态。
唤醒等待的进程 一旦进程事件触发,操作系统会唤醒等待该事件的进程,使其从阻塞状态返回到就绪状态,可以继续执行后续操作。
处理事件 唤醒的进程根据事件的发生进行相应的处理操作,例如读取数据、执行任务等。
重置进程事件状态 处理完事件后,可以选择重置进程事件的状态,使其再次处于非触发状态,为下一次事件的发生做准备。

进程事件Event的基本语法

在 Python 中,可以使用 Event 类来创建和管理进程事件。Event 类是 multiprocessing 模块提供的一种同步原语,用于在多进程环境下实现进程间的事件通知和同步。以下是event内置方法:

方法 描述
set() 设置事件状态为触发状态。
clear() 清除事件状态为非触发状态。
is_set() 返回事件状态是否为触发状态的布尔值。
wait(timeout=None) 等待事件的触发。如果事件已触发,则立即返回;否则,进程将被阻塞,直到事件被设置为触发状态或超时。

案例:

import multiprocessing
import time

def worker(event):
print("Worker is waiting for the event.")
event.wait()
print("Worker received the event and continues working.")

if __name__ == "__main__":
event = multiprocessing.Event()

p = multiprocessing.Process(target=worker, args=(event,))
p.start()

time.sleep(2)

print("Main process sets the event.")
event.set()

p.join()

进程队列

什么是进程队列

进程队列(Process Queue)是在多进程编程中用于进程间通信的一种数据结构。它提供了线程安全的数据交换机制,允许进程之间安全地发送和接收数据。
进程队列基于先进先出(FIFO)的原则,类似于普通队列,但具备了线程安全的特性。多个进程可以同时操作进程队列,而不会导致数据竞争或不一致的问题。

进程队列的特点

主要特点 描述
线程安全 进程队列具备线程安全的机制,可以被多个进程同时访问和操作,无需开发者显式处理线程同步问题。
支持多种数据类型 进程队列可以传输各种类型的数据,包括整数、浮点数、字符串、自定义对象等。
阻塞操作 当进程尝试从空队列获取数据时,进程队列会自动阻塞,直到有数据可用。类似地,当进程尝试向已满队列放入数据时,也会阻塞。
共享内存机制 进程队列使用共享内存机制进行数据传输,无需进行数据的序列化和反序列化,提高了数据交换的效率。

队列queen的特点

主要特点 描述
先进先出 队列采用先进先出(FIFO)的原则,保证数据按照顺序进行插入和删除操作。
线程安全 队列具备线程安全的机制,多线程环境下可以安全地进行数据操作,避免数据竞争和不一致性问题。
动态大小 队列的大小可以根据需要进行动态调整,可以自动扩容或收缩,适应不同场景下的数据存储需求。
多种数据类型 队列可以存储和处理各种类型的数据,包括整数、浮点数、字符串、自定义对象等。
阻塞操作 当队列为空时,从队列获取数据的操作会被阻塞,直到队列中有数据可用;当队列满时,插入数据的操作也会被阻塞。
高效的插入和删除 队列的插入和删除操作具有高效性能,时间复杂度通常为 O(1)。

队列queen的基本语法

在 Python 中,可以使用 Queue 类来创建和操作队列(Queue)数据结构。Queue 类位于 queue 模块中,提供了实现队列的基本功能。以下是queue内置方法:

方法 描述
put(item, block=True, timeout=None) 向队列中插入元素 item。如果队列已满且 block=True,则会阻塞直到有空间可用或超时。
get(block=True, timeout=None) 从队列中获取并移除一个元素,并返回该元素。如果队列为空且 block=True,则会阻塞直到有元素可用或超时。
empty() 返回布尔值,表示队列是否为空。
full() 返回布尔值,表示队列是否已满。
qsize() 返回队列中元素的个数。
put_nowait(item) 向队列中插入元素 item,不会阻塞。
get_nowait() 从队列中获取并移除一个元素,并返回该元素。如果队列为空,则会立即引发 queue.Empty 异常。

案例:如何使用queue

import queue

q = queue.Queue()

q.put(1)
q.put(2)
q.put(3)

while not q.empty():
item = q.get()
print(item)

限制queue队列长度

限定Queen队列三个消息,如果队列超出限定队列长度会发生阻塞,示例:

from multiprocessing import Process,Queue

q = Queue(3)
q.put(1)
q.put(2)
q.put(3)
q.put(4)

案例:主进程添加数据子进程取数据

from multiprocessing import  Process,Queue

def func1(q):
#子进程取数据
res = q.get()
print("func1:",res)

def func2(q):
res = q.get()
print("func2:",res)

if __name__ == '__main__':
q = Queue()
p1 = Process(target=func1,args=(q,))

p1.start()

#主进程向队列添加数据
q.put("acai.cloud1")

#为了等待子进程把数据添加到队列再取,否则会发生消息阻塞
p1.join()

print("主进程:{}".format(q.get()))

进程Manager

进程管理器(Process Manager)是一种操作系统或计算机系统中的软件组件,用于管理和控制进程的创建、执行和终止。

进程管理器的作用

作用 描述
执行程序 进程是计算机系统中运行的程序的实例。每个进程都有自己的内存空间和执行上下文,可以独立运行,并在操作系统的管理下协作工作。
资源分配和管理 进程管理器负责分配和管理系统资源,如内存、CPU时间片、文件句柄、网络连接等。它确保不同进程之间的资源分配公平合理,并避免资源争用。
并发执行 多进程系统可以同时执行多个进程,实现并发执行。进程管理器负责协调进程的执行顺序和分配系统资源,以实现高效的并发计算和任务调度。
进程间通信 进程之间可以通过进程间通信(IPC)机制进行数据传输、共享资源和相互协作。进程管理器提供不同的IPC机制,如管道、共享内存、消息队列等。
故障隔离和容错 进程的独立性使得系统能够实现故障隔离和容错。如果一个进程出现问题或崩溃,其他进程仍然可以继续执行,不会影响整个系统的稳定性和可靠性。
安全性和保护 进程之间的隔离性保证了系统的安全性和保护机制。不同进程之间无法直接访问彼此的内存空间和资源,从而有效防止非法访问和数据泄露的风险。
并行处理和任务分配 多进程系统可以实现并行处理和任务分配。进程管理器可以将大型任务分解为多个子任务,并将它们分配给不同的进程进行并行处理,提高系统的效率。
进程监控和调试 进程管理器可以监控进程的运行状态和执行情况,收集日志信息并处理异常情况。它提供调试工具和错误处理机制,以确保进程的正确运行和稳定性。
系统资源的合理利用和调度 进程管理器确保系统中的进程和资源得到合理利用和调度,提高系统的整体性能和效率。它根据不同进程的优先级、资源需求和调度算法,进行资源分配。

进程管理器基本语法

from multiprocessing import Process,Manager,Lock

def work(data,lock):
#上锁,等执行完成之后放行
# lock.acquire()
# data["count"] -= 1
# lock.release()

#使用with语法简化上锁解锁操作
with lock:
data["count"] -= 1

if __name__ == '__main__':
mgr = Manager()
data = mgr.dict({"count":200})

lst = []
lock = Lock()
for i in range(100):
p = Process(target=work,args=(data,lock))
p.start()
lst.append(p)

for i in lst:
i.join()

print(data)

生产者-消费者模型

生产者-消费者模型是一种常见的并发编程模型,用于解决生产者生成数据并将其传递给消费者进行处理的问题。在 Python 中,可以使用多线程或多进程来实现生产者-消费者模型。示例:

import time
from multiprocessing import Process,Queue

#消费者模型
def consumer(q,name):
while True:
time.sleep(1)
food = q.get()
print("{name} 吃了{food}个".format(name=name,food=food))




#生产者模型
def producer(q,name,food):
for i in range(1,6):
time.sleep(1)
print("{name} 生产了{food} {num}个".format(name=name, food=food, num=i))
q.put(food+str(i))



if __name__ == '__main__':
#创建队列q
q = Queue()
#创建消费者进程
p1 = Process(target=consumer,args=(q,"acai"))
p1.start()

#创建生产者进程
p2 = Process(target=producer, args=(q,"acai","大白菜"))
p2.start()

print("程序执行结束……")

JoinableQueue队列

JoinableQueue 是 Python 中 multiprocessing 模块提供的一种特殊队列,用于多进程间的数据传递和协作。与普通的队列不同,JoinableQueue 具有一些额外的功能,使得它更适合在多进程场景下使用。

JoinableQueue的作用

主要作用 描述
数据传递和共享 JoinableQueue 可以在多个进程之间传递数据,生产者进程将数据放入队列,消费者进程从队列中获取数据进行处理。
进程间同步 JoinableQueue 支持放置特殊的占位符,用于表示任务的结束。当消费者进程获取到占位符时,意味着所有任务已完成。
阻塞和超时控制 JoinableQueue 具有阻塞特性,可以在队列为空时阻塞消费者进程。通过设置超时时间,可以控制消费者进程等待数据的时间。
进程间任务分配和完成通知 JoinableQueue 用于任务分配和完成的通知,生产者进程将任务放入队列,消费者进程获取任务并进行处理。

JoinableQueue内置方法

方法 描述
put(item) 将数据项 item 放入队列中。
put_nowait(item) put(item) 类似,但如果队列已满,则立即引发 queue.Full 异常。
get() 从队列中获取一个数据项,并将其从队列中移除。如果队列为空,则阻塞,直到有数据可用。
get_nowait() get() 类似,但如果队列为空,则立即引发 queue.Empty 异常。
task_done() 标记队列中的一个任务项已完成。
join() 阻塞调用线程,直到队列中的所有任务项都已完成。
empty() 返回队列是否为空,如果队列为空,则返回 True,否则返回 False
full() 返回队列是否已满,如果队列已满,则返回 True,否则返回 False
qsize() 返回队列中当前的任务项数量。
close() 关闭队列,表示不再接受新的任务项。此后,尝试将任务项放入队列将引发 ValueError 异常。
cancel_join_thread() 取消与队列关联的线程的隐式 join。

JoinableQueue基本语法

from multiprocessing import Process,JoinableQueue

jq = JoinableQueue()
#对了计数器加1
jq.put("a")
print(jq.get())
#通过task_done()让对了计数器减1
jq.task_done()
#只有队列计数器为0时,才会放行阻塞
jq.join()

print("finish")

使用JoinableQueue优化生产者消费者模型:

import time
from multiprocessing import Process,JoinableQueue

#消费者模型
def consumer(jq,name):
while True:
time.sleep(1)
food = jq.get()
print("{name} 吃了{food}个".format(name=name,food=food))

#生产者模型
def producer(jq,name,food):
for i in range(1,4):
time.sleep(1)
print("{name} 生产了{food} {num}个".format(name=name, food=food, num=i))
jq.put(food+str(i))
jq.task_done()

if __name__ == '__main__':
#创建队列q
jq = JoinableQueue()
#创建消费者进程
p1 = Process(target=consumer,args=(jq,"acai"))
#设置消费者进程为守护进程,当主进程结束后杀死消费者进程
p1.daemon = True
p1.start()

#创建生产者进程
p2 = Process(target=producer, args=(jq,"acai","大白菜"))
p2.start()

#设置队列阻塞,如果队列长度为0时执行后面的代码;如果队列长度不为0时阻塞
jq.join()
#设置进程阻塞
p2.join()

print("程序执行结束……")

进程池

进程池可以提供指定数量的进程供用户调用,当有新的请求提交到进程池中时;如果进程池还没有满,那么就会创建一个新的进程用来执行该请求;但如果进程池中的进程数已经达到规定最大值,那么该请求就会等待。直到进程池中有进程结束,才会创建新的进程。

进程池的作用

主要作用 描述
提高性能和资源利用率 进程池允许多个任务并发执行,重复使用已创建的进程,提高性能和资源利用率。
任务调度和分配 进程池负责管理进程的创建和销毁,并根据任务的到达情况将任务分配给空闲的进程。
控制并发度 进程池通过设置最大进程数来限制并发执行的任务数量,控制系统资源的消耗和竞争。
异步任务执行和结果获取 进程池可以将任务提交给进程进行异步执行,并提供机制来获取任务的执行结果。
错误处理和异常隔离 进程池可以捕获并处理子进程中发生的异常和错误,防止其传播到主进程并影响应用程序的稳定性。

进程池的基本语法

from concurrent.futures import ProcessPoolExecutor
import os

def func(i):
print("任务执行中……",os.getpid())
print("任务执行结束",i)
return i

if __name__ == '__main__':
#创建进程池对象,进程池最多创建os.count()个进程;
p_pool = ProcessPoolExecutor()
#异步提交任务
for i in range(10):
p_pool.submit(func,i)



#获取当前进程池的返回值
lst = []
for i in range(10):
res = p_pool.submit(func,i)
lst.append(res)

for i in lst:
print(i.result)

#等待所有子进程结束之后执行主进程
p_pool.shutdown()

print("主进程结束")

总结

阻塞机制 描述
Semaphore 用于控制对共享资源的访问,可以设置初始的信号量值,通过 acquire() 和 release() 控制进程的访问。
join() 用于等待指定进程结束,阻塞当前进程直到目标进程执行完毕,可以通过设置超时时间来控制阻塞的时间。
进程数据交换 描述
进程 Manager 用于创建和管理共享对象,可以通过生成 Proxy 对象来访问和操作共享对象,实现进程间的数据共享和通信。
Queue 进程安全的队列,用于在多个进程之间传递数据,支持多个进程同时读写队列,实现进程间的数据交换。
JoinableQueue 基于 Queue 的队列,额外提供了 task_done() 和 join() 方法,用于跟踪已完成的任务和等待所有任务完成。

线程

线程基础

Python线程是指在Python中创建和管理的轻量级执行单元。线程是进程内的一个独立执行流,与进程内的其他线程共享同一进程的资源。与进程相比,线程的创建和销毁开销较小,且线程间的切换速度更快。

线程的特点

线程特点 描述
轻量级 线程的创建和销毁开销较小,相比于进程更轻量级。
共享资源 线程在同一进程内运行,共享进程的内存空间和其他资源,可以直接访问和修改相同的变量和数据结构。
并发执行 多个线程可以同时执行不同的任务,从而提高程序的执行效率。
GIL限制 Python解释器中的全局解释器锁(GIL)限制了同一时间只能有一个线程执行Python字节码。
非真正并行 由于GIL的存在,Python线程在CPU密集型任务中无法实现真正的并行执行。
适用于I/O密集型任务 在I/O密集型任务中,线程可以并发执行,通过等待I/O操作时让其他线程工作,提高执行效率。
线程同步和通信 线程之间共享资源,可能发生竞争条件和数据不一致问题,需要使用线程同步和通信机制进行管理。

进程和线程的关系

进程是程序的执行实例,是操作系统进行资源分配和调度的基本单位;线程是进程内的一个独立执行流,多个线程可以共享同一进程的资源。

进程和线程的区别

总结:进程用来分配资源,不干活;线程是程序的最小单位,是负责干活的

线程基本语法

import threading

# 目标函数,打印线程名称
def print_thread_name():
thread_name = threading.current_thread().name
print("当前线程名称为:", thread_name)

# 创建线程对象
thread = threading.Thread(target=print_thread_name)

# 启动线程
thread.start()

# 等待线程结束
thread.join()

print("主线程结束")

使用类定义线程

import threading

class MyThread(threading.Thread):
def run(self):
thread_name = threading.current_thread().name
print("当前线程名称为:", thread_name)

# 创建线程对象
thread = MyThread()

# 启动线程
thread.start()

# 等待线程结束
thread.join()

print("主线程结束")

线程内置方法

方法 描述
start() 启动线程,并执行线程的run()方法
run() 定义线程的执行逻辑
join([timeout]) 等待线程执行完毕
is_alive() 检查线程是否仍在运行
name 线程的名称
ident 线程的标识符
daemon 设置线程是否为守护线程
current_thread() 返回当前线程对象

守护线程

什么是守护线程

在Python中,线程可以分为守护线程(daemon thread)和非守护线程(non-daemon thread);
守护线程是指在程序运行过程中在后台运行的线程,它的存在不会阻止程序的退出。当所有非守护线程结束时,守护线程会自动退出,无论它们是否执行完毕。
非守护线程是指在程序运行过程中执行的线程,它会阻止程序退出,直到所有非守护线程都执行完毕或被手动终止。
守护线程通常被用于执行一些后台任务或提供支持功能。它们在后台默默地执行,不会影响主要的程序流程。一旦所有的非守护线程执行完毕,无论守护线程是否执行完毕,程序就会退出。
在Python中,可以使用线程对象的daemon属性来设置线程是否为守护线程。默认情况下,线程对象是非守护线程。你可以通过将daemon属性设置为True将线程设置为守护线程,或将其设置为False将线程设置为非守护线程。

示例

在示例中,我们创建了一个名为daemon_thread_func的目标函数,并使用threading.Thread创建了一个线程对象daemon_thread。将daemon_thread的daemon属性设置为True,将其设置为守护线程。然后,启动守护线程,并让主线程休眠3秒钟。在这个示例中,当主线程结束时,守护线程也会随之结束。

import threading
import time

# 守护线程的目标函数
def daemon_thread_func():
while True:
print("守护线程执行中...")
time.sleep(1)

# 创建守护线程
daemon_thread = threading.Thread(target=daemon_thread_func)
daemon_thread.daemon = True

# 启动守护线程
daemon_thread.start()

# 主线程休眠3秒钟
time.sleep(3)

print("主线程结束")

线程锁

什么是线程锁

线程锁(Thread lock)是一种同步机制,用于在多线程环境中控制对共享资源的访问。它可以帮助确保在某个线程修改共享资源时,其他线程无法同时进行修改,从而避免数据竞争和一致性问题。

线程锁内置方法

方法 描述
acquire([blocking]) 请求获取线程锁
release() 释放线程锁
locked() 检查线程锁的状态

线程锁基本语法

from threading import Lock,Thread
import time

n = 0

def func1(lock):

global n
for i in range(10000):
# lock方式一
lock.acquire()
n += 1
lock.release()
print("子线程func1 n:",n)


def func2(lock):
global n
for i in range(10000):
# lock方式二
with lock:
n -= 1
print("子线程func2 n:",n)

if __name__ == '__main__':
lock = Lock()
t1 = Thread(target=func1,args=(lock,))
t2 = Thread(target=func2,args=(lock,))

t1.start()
t2.start()

print("主线程n:",n)

线程信号量

什么是线程信号量

线程信号量(Thread Semaphore)是一种同步机制,用于在多线程环境中控制对共享资源的访问。与线程锁不同,线程信号量可以允许多个线程同时访问共享资源,而不是一次只有一个线程。

线程信号量的作用

作用 描述
控制对共享资源的并发访问 限制对共享资源的并发访问数量
控制线程的执行顺序 阻塞一组线程,直到满足特定条件后才允许其继续执行
解决生产者-消费者问题 协调生产者线程和消费者线程,确保缓冲区的正确使用

线程信号量的基本语法

import threading

# 创建信号量,初始计数为2
semaphore = threading.Semaphore(2)

# 任务函数
def task():
semaphore.acquire() # 请求获取信号量
try:
# 执行任务
print("执行任务")
finally:
semaphore.release() # 释放信号量

# 创建多个线程并启动
threads = []
for _ in range(5):
thread = threading.Thread(target=task)
threads.append(thread)
thread.start()

# 等待所有线程执行完毕
for thread in threads:
thread.join()

线程死锁

什么是线程死锁

线程死锁(Thread deadlock)是指在多线程编程中,两个或多个线程被无限地阻塞,互相等待对方释放资源而无法继续执行的情况。当发生线程死锁时,程序将无法继续正常执行,可能导致系统停止响应或出现异常情况。

解决线程死锁的方法

方法 描述
避免循环等待 合理设计资源申请和释放的顺序,避免形成循环等待条件
使用超时机制 在获取资源时设置合理的超时时间,避免线程一直等待资源的情况,防止死锁的发生
按需分配资源 只有在确实需要资源时再进行申请,而不是一次性申请所有可能需要的资源
使用锁的层次性和有序性 按照统一的顺序获取和释放锁,确保在获取锁时不破坏资源的层次性和有序性,避免死锁的发生
死锁检测和恢复 使用死锁检测算法和工具检测死锁的发生,并采取相应的恢复策略解除死锁和恢复正常执行
合理的资源管理和分配 合理规划和管理资源的分配,避免资源过度分配和浪费,确保资源的合理使用和释放

线程死锁基本语法

示例:当两个线程同时抢同一个资源导致死锁

import time
from threading import Lock,Thread


def eat1(name):
noodle_lock.acquire()
print("{name}拿到面条啦".format(name=name))
kuaizi_lock.acquire()
print("{name}拿到筷子啦".format(name=name))

print("{name}开始吃面条".format(name=name))
time.sleep(3)
kuaizi_lock.release()
print("{name}放下筷子啦".format(name=name))
noodle_lock.release()
print("{name}放下面条啦".format(name=name))

def eat2(name):
kuaizi_lock.acquire()
print("{name}拿到筷子啦".format(name=name))
noodle_lock.acquire()
print("{name}拿到面条啦".format(name=name))

print("{name}开始吃面条啦".format(name=name))
time.sleep(3)

noodle_lock.release()
print("{name}放下面条啦".format(name=name))
kuaizi_lock.release()
print("{name}放下筷子啦".format(name=name))

if __name__ == '__main__':
noodle_lock = Lock()
kuaizi_lock = Lock()
name_list1 = ["zhangsan","lisi","wangmazi","zhaosi"]
name_list2 = ["aaa","bbb","ccc","ddd"]
for i in name_list1:
Thread(target=eat1,args=(i,)).start()

for i in name_list2:
Thread(target=eat2,args=(i,)).start()

线程递归锁

什么是线程递归锁

递归锁可以连续acquire多次,每次acquire计数器加1;只有计数器为0时,才能被其他线程抢到资源;递归锁每解锁一次,计数器减1

线程递归锁的作用

作用 描述
避免死锁 允许同一个线程在多个函数调用层次中重复获取锁,避免死锁的发生
代码灵活性 在递归调用或嵌套函数调用的情况下,允许同一个线程在不同层次中获取和释放锁,保证数据访问的一致性和正确性
简化编程模型 简化编程模型,减少编程错误和调试困难,提高代码的可读性和可维护性

递归锁内置方法

方法 描述
acquire([blocking]) 获取递归锁。可选参数blocking指定是否使用阻塞模式获取锁,默认为True。如果blockingTrue且锁已被其他线程持有,则当前线程将被阻塞,直到获取到锁为止。如果blockingFalse,则尝试获取锁,如果锁已被其他线程持有,则立即返回,不会阻塞。
release() 释放递归锁。如果当前线程多次获取了同一个递归锁,则每次释放锁时,锁的计数器减1。只有当锁的计数器减到零时,锁才能完全释放,其他等待该锁的线程才能继续执行。
locked() 判断递归锁是否被锁定。如果锁被任何线程持有,返回True;否则返回False

递归锁基本语法

from threading import Thread,RLock

def func(rlock,num):
rlock.acquire()
rlock.acquire()
print("{num} bbb".format(num=num))
print("{num} aaa".format(num=num))
rlock.release()
rlock.release()


if __name__ == '__main__':
rlock = RLock()
for i in range(10):
Thread(target=func,args=(rlock,i)).start()

线程互斥锁

什么是线程互斥锁

线程互斥锁(Thread Mutex Lock)是一种用于线程同步的锁机制,用于保护共享资源,确保在任何给定时刻只有一个线程可以访问该资源。它是一种最基本和常见的线程同步原语。
互斥锁的原理是通过在共享资源访问前获取锁,并在使用完共享资源后释放锁,以确保同一时刻只有一个线程能够访问共享资源,其他线程需要等待锁的释放。

互斥锁的特点

特点 描述
互斥性 同一时刻只有一个线程可以持有互斥锁,其他线程必须等待。
阻塞性 当一个线程尝试获取已经被其他线程持有的互斥锁时,它将被阻塞,直到互斥锁被释放。
一次只能被一个线程持有 互斥锁一次只能被一个线程获取,其他线程必须等待前一个线程释放锁。

互斥锁基本语法

导入threading模块:

import threading

创建互斥锁对象:

lock = threading.Lock()

在需要进行同步的代码块中,使用互斥锁的acquire()release()方法:

lock.acquire()  # 获取互斥锁
try:
# 执行需要同步的代码
# ...
finally:
lock.release() # 释放互斥锁

示例:模拟12306抢票

在示例中,我们假设总共有10张票,每个用户线程尝试不断地抢票,直到票数为0。为了保护票数的访问,我们使用了互斥锁lock,在每个用户线程抢票前先获取锁,然后进行抢票操作,最后释放锁。这样可以保证同一时刻只有一个线程可以修改票数,避免并发访问的问题。

import threading

ticket_count = 10 # 总票数
lock = threading.Lock() # 创建互斥锁

def buy_ticket(user_name):
global ticket_count

# 模拟用户抢票过程
while ticket_count > 0:
lock.acquire() # 获取互斥锁
if ticket_count > 0:
print(f"{user_name} 抢到了第 {ticket_count} 张票")
ticket_count -= 1
lock.release() # 释放互斥锁

# 创建多个用户线程进行抢票
threads = []
for i in range(1, 6):
user_name = f"User{i}"
thread = threading.Thread(target=buy_ticket, args=(user_name,))
threads.append(thread)
thread.start()

# 等待所有线程执行完毕
for thread in threads:
thread.join()

线程事件

什么是线程事件

线程事件(Thread Event)是一种线程同步原语,用于线程间的协调和通信。它是一种线程间的信号标志,用于线程之间的通知和等待。
线程事件有两种状态:已触发(set)和未触发(clear)。当线程事件处于已触发状态时,等待该事件的线程可以继续执行;当线程事件处于未触发状态时,等待该事件的线程将被阻塞,直到事件被触发。
线程事件通常用于多个线程之间的协调,例如一个线程等待另一个线程完成某个任务后再继续执行。一个线程可以等待一个或多个线程事件,当所有事件都处于已触发状态时,线程才能继续执行。

线程事件基本语法

导入threading模块:

import threading

创建线程事件对象:

event = threading.Event()

在等待线程中使用wait()方法等待事件触发:

event.wait()  # 等待事件触发

在触发线程中使用set()方法设置事件触发:

event.set()  # 设置事件触发

在其他线程中使用clear()方法清除事件触发:

event.clear()  # 清除事件触发

示例:模拟连接远程数据库

在上述示例中,我们创建了一个线程事件对象event,然后创建了一个连接线程connect_thread,并将线程事件对象作为参数传递给连接函数connect_remote_database。在连接函数中,我们可以根据实际情况模拟连接远程数据库的过程,并在连接成功时设置线程事件event.set()。
在主程序中,我们启动连接线程并等待一段时间(这里设置为5秒)来判断连接是否成功。如果线程事件在指定的超时时间内被设置,即连接成功,我们可以执行连接成功后的操作。否则,我们可以执行连接超时或失败后的操作。

import threading

# 模拟连接远程数据库
def connect_remote_database(event):
# 模拟连接过程
# ...

# 假设连接成功
event.set()

# 主程序
def main():
# 创建线程事件对象
event = threading.Event()

# 创建连接线程
connect_thread = threading.Thread(target=connect_remote_database, args=(event,))
connect_thread.start()

# 等待连接结果
if event.wait(timeout=5):
print("连接成功")
# 连接成功后的操作
# ...
else:
print("连接超时或失败")
# 连接超时或失败后的操作
# ...

# 等待连接线程结束
connect_thread.join()

# 运行主程序
if __name__ == '__main__':
main()

线程队列

什么是线程队列

线程队列(Thread Queue)是一种用于在线程间安全地传递数据的数据结构。它提供了线程安全的数据交换机制,允许一个线程将数据放入队列的一端,而另一个线程从队列的另一端取出数据。
线程队列常用于多线程编程中,用于实现线程间的数据共享和通信。它可以有效地解耦生产者线程和消费者线程,让它们在并发执行的情况下安全地进行数据交换。

线程队列的特点

特点 描述
线程安全 线程队列提供线程安全的操作,可以在多个线程中并发地对队列进行读取和写入操作。
先进先出 线程队列按照先进先出(FIFO)的原则,最早放入队列的数据最先被取出。
阻塞操作 阻塞操作会在队列为空时阻塞线程,直到队列中有新的数据可用。
非阻塞操作 非阻塞操作会立即返回,如果队列为空则返回空值或抛出异常。
数据共享 线程队列用于在线程间共享数据,可以方便地进行数据传递和通信。
解耦 生产者线程和消费者线程可以独立运行,通过线程队列进行数据交换,实现解耦。
同步 线程队列提供了同步机制,可以通过等待队列中的数据完成来同步线程的执行。
数据一致性 线程队列的操作保证了数据的一致性,避免了多线程操作导致的数据竞争和不一致性。

Queue队列

Queue队列的特点

特点 描述
先进先出 队列中的元素按照先进先出(FIFO)的顺序进行处理。
阻塞操作 阻塞操作会在队列为空或已满时阻塞线程,直到有新的元素可用或有空闲空间。
线程安全 队列提供了线程安全的操作,可以在多个线程中并发地对队列进行读取和写入操作。
最大长度 队列可以设置最大长度,限制队列中元素的数量,超过最大长度时继续插入会阻塞或引发异常。
生产者消费者 队列常用于生产者-消费者模型中,生产者线程将数据放入队列,消费者线程从队列中取出并处理数据。
同步机制 队列提供了同步机制,可以通过等待队列中的数据完成来同步线程的执行。

Queue队列的内置方法

方法 描述
put(item) 将元素 item 放入队列。默认情况下,该方法会阻塞直到队列中有空闲空间。可以通过设置 block=False 来进行非阻塞放入操作。
get() 从队列中取出并返回一个元素。默认情况下,该方法会阻塞直到队列中有可用元素。可以通过设置 block=False 来进行非阻塞取出操作。
empty() 返回队列是否为空的布尔值。
full() 返回队列是否已满的布尔值。
qsize() 返回队列中的元素个数。
task_done() 在完成一项工作后调用此方法,表示该项工作已经被处理完毕。通常与 join() 方法配合使用,用于等待队列中的所有工作被处理完毕。
join() 阻塞调用线程,直到队列中的所有工作被处理完毕。

Queue队列的基本语法

import queue

使用queue.Queue()类创建一个先进先出(FIFO)的队列:

q = queue.Queue()

使用put(item)方法将元素放入队列:

q.put(item)

使用get()方法从队列中取出并返回一个元素:

item = q.get()

使用empty()方法检查队列是否为空:

if q.empty():
print("队列为空")

使用full()方法检查队列是否已满:

if q.full():
print("队列已满")

使用qsize()方法获取队列中的元素个数:

size = q.qsize()

在完成一项工作后,可以使用task_done()方法通知队列一项工作已经被处理完毕

q.task_done()

使用join()方法阻塞调用线程,直到队列中的所有工作被处理完毕:

q.join()

lifoQueue队列

lifoQueue队列的特点

特点 描述
后进先出 LifoQueue 是一种后进先出(LIFO)的队列,即最后放入队列的元素最先被取出。
阻塞操作 阻塞操作会在队列为空时阻塞线程,直到队列中有新的元素可用。
非阻塞操作 非阻塞操作会立即返回,如果队列为空则返回空值或抛出异常。
线程安全 LifoQueue 提供线程安全的操作,可以在多个线程中并发地对队列进行读取和写入操作。
可设置最大长度 LifoQueue 可以设置最大长度,限制队列中元素的数量,超过最大长度时继续插入会阻塞或引发异常。
适用于生产者消费者模型 LifoQueue 常用于生产者-消费者模型中,生产者线程将数据放入队列,消费者线程从队列中取出并处理数据。

lifoQueue队列的内置方法

方法 描述
put(item) 将元素 item 放入队列。默认情况下,该方法会阻塞直到队列中有空闲空间。可以通过设置 block=False 来进行非阻塞放入操作。
get() 从队列中取出并返回一个元素。默认情况下,该方法会阻塞直到队列中有可用元素。可以通过设置 block=False 来进行非阻塞取出操作。
empty() 返回队列是否为空的布尔值。
full() 返回队列是否已满的布尔值。
qsize() 返回队列中的元素个数。
task_done() 在完成一项工作后调用此方法,表示该项工作已经被处理完毕。通常与 join() 方法配合使用,用于等待队列中的所有工作被处理完毕。
join() 阻塞调用线程,直到队列中的所有工作被处理完毕。

lifoQueue队列的基本语法

首先,需要导入 queue 模块:

import queue

然后,可以使用 LifoQueue() 类创建一个后进先出队列:

q = queue.LifoQueue()

使用 put(item) 方法将元素放入队列:

q.put(item)

使用 get() 方法从队列中取出并返回一个元素:

item = q.get()

可以使用 empty() 方法检查队列是否为空:

if q.empty():
print("队列为空")

使用 full() 方法检查队列是否已满:

if q.full():
print("队列已满")

使用 qsize() 方法获取队列中的元素个数:

size = q.qsize()

在完成一项工作后,可以使用 task_done() 方法通知队列一项工作已经被处理完毕:

q.task_done()

使用 join() 方法阻塞调用线程,直到队列中的所有工作被处理完毕:

q.join()

PriorityQueue队列

PriorityQueue队列的特点

特点 描述
优先级排序 PriorityQueue 是一种根据元素优先级排序的队列,优先级高的元素先出队列。
非阻塞操作 非阻塞操作会立即返回,即使队列为空也可以执行放入和取出操作。
线程安全 PriorityQueue 提供线程安全的操作,多个线程可以并发地对队列进行读取和写入操作。
可设置最大长度 PriorityQueue 可以设置最大长度,限制队列中元素的数量,超过最大长度时继续插入会阻塞或引发异常。
适用于优先级调度 PriorityQueue 常用于任务调度和优先级处理的场景,根据元素的优先级确定处理顺序。

PriorityQueue队列的内置方法

方法 描述
put(item, priority) 将元素 item 放入队列,并指定优先级 priority。默认情况下,较小的优先级值表示较高的优先级。
get() 从队列中取出并返回一个具有最高优先级的元素。如果多个元素具有相同的最高优先级,则按照它们被放入队列的顺序进行比较和返回。
empty() 返回队列是否为空的布尔值。
full() 返回队列是否已满的布尔值。
qsize() 返回队列中的元素个数。
task_done() 在完成一项工作后调用此方法,表示该项工作已经被处理完毕。通常与 join() 方法配合使用,用于等待队列中的所有工作被处理完毕。
join() 阻塞调用线程,直到队列中的所有工作被处理完毕。

PriorityQueue队列的基本语法

from queue import PriorityQueue
#如果队列数据都是数字,默认从小到大排序
pq = PriorityQueue()
pq.put(13)
pq.put(20)
pq.put(10)
print(pq.get())
print(pq.get())
print(pq.get())

#如果队列数据都是字符串,按照ASCII编码排序
pq.put("china")
pq.put("america")
pq.put("atinos")
print(pq.get())
print(pq.get())
print(pq.get())

#使用元组,队列存字符串和数字
pq.put(("acai.cloud",27))
print(pq.get())

线程池

什么是线程池

线程池(Thread Pool)是一种管理和复用线程的机制,它可以提供一组预先创建的线程,用于执行多个任务。线程池维护着一个线程队列,任务到达时,从队列中取出一个线程来处理任务,任务执行完毕后,线程并不被销毁,而是返回到线程池中等待下一个任务。
线程池的主要目的是减少线程的创建和销毁的开销,通过复用线程来提高执行效率,减少系统资源的消耗。在任务量较大的情况下,线程池可以有效地控制并发线程的数量,防止系统资源被耗尽。

线程池的组件

组件 描述
线程池管理器 (Thread Pool Manager) 负责线程池的创建、初始化和销毁,以及任务的提交和分配。
线程池 (Thread Pool) 由一组线程构成,用于执行任务。
任务队列 (Task Queue) 用于存放待执行的任务,通常是一个先进先出的队列。
工作线程 (Worker Thread) 线程池中的线程,用于执行任务。
任务 (Task) 要执行的具体工作单元。

线程池的特点

特点 描述
重用线程 线程池通过复用预先创建的线程,避免了频繁创建和销毁线程的开销,提高了系统性能。
提高性能 通过减少线程创建和销毁的开销,线程池可以更有效地管理并发任务的执行,提高系统的性能。
控制并发度 线程池允许限制并发线程的数量,通过设置线程池大小和任务队列长度,可以控制并发度,避免系统资源被过度占用。
提供任务管理和调度 线程池提供了对任务的排队、分配和调度机制,可以灵活管理任务的执行顺序和优先级,提供更好的任务处理方式。
异步执行任务 线程池能够以异步方式执行任务,即任务的提交和执行可以并行进行,不会阻塞主线程,提高程序的响应性。
线程安全 线程池在设计上考虑了线程安全性,能够在多线程环境下正常运行,并提供了对共享资源的合理访问机制,避免竞态条件和数据不一致性问题。

线程池的基本语法

from threading import currentThread
from concurrent.futures import ThreadPoolExecutor

def func(i):
print("任务开始{} 线程号:{}".format(i,currentThread().ident))

if __name__ == '__main__':
#创建一个线程池对象,线程池最多限制os.cpu_count()*5个线程数
tp = ThreadPoolExecutor()
#异步提交任务
for i in range(10):
tp.submit(func,i)

#等待子线程结束后再执行主线程
tp.shutdown()

print("主线程结束")

协程

什么是协程

协程(Coroutine)是一种轻量级的并发编程模型,用于实现协作式多任务处理。协程可以理解为一种特殊的函数,它可以在执行过程中暂停和恢复,允许多个协程在同一个线程中交替执行,实现非抢占式的任务切换和协作式的多任务处理。
与传统的线程或进程不同,协程没有操作系统的参与,它是由程序员通过代码控制的。协程之间可以自主地决定在何时暂停、恢复和切换,以及共享何种数据。这种特性使得协程能够更高效地利用系统资源,并避免了线程切换的开销和同步原语的复杂性。

协程的特点

特点 描述
轻量级 协程是轻量级的执行单位,可以创建大量的协程而不会带来过多的系统开销。
高效性 协程之间的切换成本低,不涉及线程上下文切换和内核调度,因此执行效率较高。
非阻塞式 协程通常是非阻塞式的,可以主动释放控制权,执行其他协程,充分利用CPU资源。
协作式多任务 协程通过协作式的方式实现多任务处理,各个协程之间需要合作、协调,确保适时释放控制权。
状态保存和恢复 协程在暂停时能够保存当前的执行状态,包括函数栈、局部变量等,在恢复时继续执行,方便处理复杂的逻辑和长时间运行的任务。
可扩展性 协程支持并发编程,适用于编写高效的异步代码、处理IO密集型任务和实现事件驱动编程,具有良好的可扩展性。
编程模型灵活性 协程的编程模型灵活,可以根据需要选择适合的调度算法和协程间通信方式,满足不同场景下的需求。
没有竞态条件和死锁问题 协程的并发控制由程序员显式地进行管理,避免了传统并发编程中的竞态条件和死锁问题,简化了并发编程的难度。
可移植性 协程在不同的编程语言和平台中都有广泛的支持,具有良好的可移植性,方便在不同环境中进行开发和部署。

定义使用协程的python模块

模块名称 描述
asyncio Python 提供的用于异步编程的标准库,提供协程的基本支持。
functools Python 内置模块,提供函数操作的工具函数,可用于定义协程的装饰器。
types Python 内置模块,用于操作类型和动态创建类,可用于创建自定义的协程对象。
gevent 第三方库,提供协程和并发编程的支持,通过独立的事件循环实现协程切换和任务调度。

gevent模块内置方法

函数 描述
spawn(func, *args, **kwargs) 启动一个协程任务。
join() 阻塞当前协程,直到该协程任务完成。
joinall(jobs) 等待给定的协程任务列表中的所有任务完成后才继续执行。
value() 获取协程任务的返回值。

gevent实现协程的基本语法

import gevent

def eat():
print("eat 1")
#使用gevent.sleep设置阻塞;默认gevent不识别time阻塞
gevent.sleep(3)
print("eat 2")

def play():
print("play 1")
gevent.sleep(3)
print("play 2")

if __name__ == '__main__':
#创建协程对象
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
#阻塞,必需g1协程执行完毕为止
g1.join()
# 阻塞,必需g2协程执行完毕为止
g2.join()

print("主程序执行完毕……")

总结

进程、线程、协程的区别

特点 进程 线程 协程
调度方式 由操作系统进行调度 由操作系统进行调度 由程序代码显式控制调度
并发性 进程之间相互独立,可并行执行 线程在同一进程内共享资源,可并行执行 协程在同一线程内执行,通过协程切换实现并行执行
切换开销 进程切换开销较大,涉及上下文切换和内核态与用户态切换 线程切换开销较小,涉及上下文切换和用户态切换 协程切换开销较小,通过用户态切换实现,无需内核态切换
内存消耗 每个进程有独立的内存空间 多个线程共享进程的内存空间 协程共享线程的内存空间,协程间数据隔离
编程模型 多进程编程相对较复杂 多线程编程相对较复杂 协程编程相对简单,但需要显式的调度控制
错误隔离 进程之间相互隔离,一个进程崩溃不影响其他进程 线程之间共享进程资源,一个线程崩溃可能导致整个进程崩溃 协程之间共享线程资源,一个协程出错可能导致整个线程崩溃
并发编程模型 进程间通信方式多样,如管道、套接字、共享内存等 线程间通信方式多样,如锁、条件变量、队列等 协程间通信通过消息传递或共享变量等方式

Web开发

Python常用web开发框架

框架名 描述
Django 全功能的Web开发框架,适用于构建中大型和复杂的Web应用
Flask 轻量级的Web开发框架,适用于快速构建小型到中型的Web应用或API
Pyramid 通用的、灵活的Web开发框架,适用于构建各种规模的Web应用
Bottle 简单而快速的Web开发框架,适用于小型项目或API开发
Tornado 异步、高性能的Web开发框架,适用于处理大量并发连接的Web应用
CherryPy 轻量级的Web开发框架,适用于构建中小型的Web应用
TurboGears 全栈式的Web开发框架,适用于构建中大型的Web应用
web2py 全功能的Web开发框架,适用于快速开发可扩展的Web应用
文章作者: 慕容峻才
文章链接: https://www.acaiblog.top/Python教程/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿才的博客
微信打赏
支付宝打赏