1.Python并发编程之Futures
在Python中,concurrent.futures
模块提供了一种简单而强大的并发编程方式,称为"Futures"。它基于线程池(ThreadPoolExecutor
)和进程池(ProcessPoolExecutor
)的概念,使并发任务的管理和执行更加方便。
使用concurrent.futures
模块,你可以将任务提交给执行器(Executor),然后通过Future
对象来跟踪任务的状态和结果。
下面是一个简单的示例,演示了如何使用concurrent.futures
模块创建一个线程池,并使用submit()
方法提交任务给执行器,然后使用result()
方法获取任务的结果:
pythonCopy code
from concurrent import futures
def task(name):
print(f"Executing task {name}")
return f"Task {name} completed"
# 创建线程池
with futures.ThreadPoolExecutor() as executor:
# 提交任务给执行器
future1 = executor.submit(task, "A")
future2 = executor.submit(task, "B")
# 获取任务结果
result1 = future1.result()
result2 = future2.result()
print(result1)
print(result2)
在上面的示例中,task()
函数是一个简单的任务函数,接受一个参数name
,并返回一个字符串表示任务完成。我们使用ThreadPoolExecutor()
创建了一个线程池执行器,并使用submit()
方法提交了两个任务(“A"和"B”)给执行器。
通过result()
方法,我们可以获取任务的结果。注意,result()
方法是一个阻塞调用,直到任务完成并返回结果。
concurrent.futures
模块还提供了其他一些方法和功能,例如map()
方法用于批量提交任务、as_completed()
方法用于迭代已完成的任务、wait()
方法用于等待多个任务的完成等。这些功能可以根据具体需求进行使用。
总结来说,Python的concurrent.futures
模块提供了一种便捷的方式来实现并发编程,利用线程池和进程池来管理和执行任务。这可以帮助提高程序的性能和响应性,特别是在需要并行处理多个任务的场景下。
1 import concurrent.futures
2 import requests
3 import threading
4 import time
5
6 def download_one(url):
7 resp = requests.get(url)
8 print('Read {} from {}'.format(len(resp.content), url))
9
10
11 def download_all(sites):
12 with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
13 executor.map(download_one, sites)
14
15 def main():
16 sites = [
17 'https://en.wikipedia.org/wiki/Portal:Arts',
18 'https://en.wikipedia.org/wiki/Portal:History',
19 'https://en.wikipedia.org/wiki/Portal:Society',
20 'https://en.wikipedia.org/wiki/Portal:Biography',
21 'https://en.wikipedia.org/wiki/Portal:Mathematics',
22 'https://en.wikipedia.org/wiki/Portal:Technology',
23 'https://en.wikipedia.org/wiki/Portal:Geography',
24 'https://en.wikipedia.org/wiki/Portal:Science',
25 'https://en.wikipedia.org/wiki/Computer_science',
26 'https://en.wikipedia.org/wiki/Python_(programming_language)',
29 'https://en.wikipedia.org/wiki/PHP',
30 'https://en.wikipedia.org/wiki/Node.js',
31 'https://en.wikipedia.org/wiki/The_C_Programming_Language',
32 'https://en.wikipedia.org/wiki/Go_(programming_language)'
33 ]
34 start_time = time.perf_counter()
35 download_all(sites)
36 end_time = time.perf_counter()
37 print('Download {} sites in {} seconds'.format(len(sites), end_time - start_t
38
39 if __name__ == '__main__':
40 main()
41
42 ## 输出
43 Read 151021 from https://en.wikipedia.org/wiki/Portal:Mathematics
44 Read 129886 from https://en.wikipedia.org/wiki/Portal:Arts
45 Read 107637 from https://en.wikipedia.org/wiki/Portal:Biography
46 Read 224118 from https://en.wikipedia.org/wiki/Portal:Society
47 Read 184343 from https://en.wikipedia.org/wiki/Portal:History
48 Read 167923 from https://en.wikipedia.org/wiki/Portal:Geography
49 Read 157811 from https://en.wikipedia.org/wiki/Portal:Technology
50 Read 91533 from https://en.wikipedia.org/wiki/Portal:Science
51 Read 321352 from https://en.wikipedia.org/wiki/Computer_science
52 Read 391905 from https://en.wikipedia.org/wiki/Python_(programming_language)
53 Read 180298 from https://en.wikipedia.org/wiki/Node.js
54 Read 56765 from https://en.wikipedia.org/wiki/The_C_Programming_Language
55 Read 468461 from https://en.wikipedia.org/wiki/PHP
56 Read 321417 from https://en.wikipedia.org/wiki/Java_(programming_language)
57 Read 324039 from https://en.wikipedia.org/wiki/Go_(programming_language)
上面代码我们创建了一个线程池,总共有 5 个线程可以分配使用。executer.map() 与前面所讲的 Python 内置的 map() 函数类似,表示对 sites 中的每一个元素,并发地调用函数download_one()
2.Python 垃圾回收机制
在Python中,计数引用是一种内存管理技术,通过跟踪对象的引用数量来确定何时释放对象所占用的内存空间。Python使用垃圾回收机制来处理不再被引用的对象,以确保内存的有效使用。
计数引用的基本原理是每个对象都有一个计数器,用于记录当前对象被引用的次数。当一个对象被引用时,计数器加1;当一个对象的引用被删除时,计数器减1。当对象的计数器为0时,即没有任何引用指向该对象,垃圾回收机制会将该对象标记为垃圾并释放其占用的内存。
Python的计数引用机制是自动的,开发者无需显式地处理计数器。Python解释器会在合适的时机自动执行垃圾回收操作。这意味着当对象的引用计数归零时,它会被垃圾回收器自动释放,无需手动释放内存。
然而,有时候在Python中会出现循环引用的情况,即多个对象之间形成了相互引用,而没有外部引用指向它们。这种情况下,对象的引用计数永远不会归零,导致垃圾回收机制无法回收这些对象。为了解决循环引用带来的内存泄漏问题,Python引入了更高级的垃圾回收机制,称为"分代垃圾回收"(generational garbage collection)。
分代垃圾回收将对象根据其存活时间划分为不同的代(generation),并在不同的时机执行垃圾回收操作。通常,新创建的对象会被放入第0代,随着时间的推移,存活下来的对象会被转移到更高的代。垃圾回收机制会优先处理年轻代的对象,只有当某个代中的对象存活时间足够长时,才会触发垃圾回收操作。
Python的计数引用是一种基于引用计数的内存管理技术,通过追踪对象的引用数量来决定何时释放对象的内存空间。垃圾回收机制负责处理不再被引用的对象,并确保内存的有效使用。对于循环引用的情况,Python引入了分代垃圾回收机制来解决内存泄漏问题。
当一个对象不再被引用时,Python的计数引用机制会自动回收该对象的内存空间。下面是一些示例,说明Python的计数引用是如何工作的:
示例 1:
pythonCopy code
# 创建一个对象
x = [1, 2, 3]
# 创建一个引用指向同一个对象
y = x
# 删除一个引用
del x
# 此时,只有y引用指向对象
# 当y也被删除时,对象的引用计数归零,内存被回收
del y
在上面的示例中,我们创建了一个列表对象 [1, 2, 3]
并将变量 x
指向它。然后,我们又创建了变量 y
并将其指向同一个对象。当我们删除变量 x
时,引用计数减1,但对象仍然存在,因为变量 y
仍然指向它。最后,当我们删除变量 y
时,对象的引用计数归零,垃圾回收器将回收该对象的内存空间。
示例 2:
pythonCopy code
# 创建一个循环引用
a = [1, 2]
b = [a]
# 将循环引用构成闭环
a.append(b)
# 删除所有引用
del a
del b
在上面的示例中,我们创建了两个列表对象 a
和 b
。然后,我们将列表 a
作为元素添加到列表 b
中,并将列表 b
设置为列表 a
的最后一个元素,形成了循环引用。即使我们删除变量 a
和 b
,对象之间的循环引用使得它们的引用计数永远不为零,垃圾回收器无法回收它们。为了解决这个问题,Python的分代垃圾回收机制会定期检查这些循环引用,识别并回收无法访问的对象。
事实上,Python 本身能够处理这种情况,我们刚刚讲过的,可以显式调用 gc.collect() ,来 启动垃圾回收。
Python 使用标记清除(mark-sweep)算法和分代收集(generational),来启用针对循环 引用的自动垃圾回收。你可能不太熟悉这两个词,这里我简单介绍一下。
先来看标记清除算法。我们先用图论来理解不可达的概念。对于一个有向图,如果从一个节点
出发进行遍历,并标记其经过的所有节点;那么,在遍历结束后,所有没有被标记的节点,我
们就称之为不可达节点。显而易见,这些节点的存在是没有任何意义的,自然的,我们就需要
对它们进行垃圾回收。
当然,每次都遍历全图,对于 Python 而言是一种巨大的性能浪费。所以,在 Python 的垃圾 回收实现中,mark-sweep 使用双向链表维护了一个数据结构,并且只考虑容器类的对象(只 有容器类对象才有可能产生循环引用)。具体算法这里我就不再多讲了,毕竟我们的重点是关 注应用。而分代收集算法,则是另一个优化手段。
Python 将所有对象分为三代。刚刚创立的对象是第 0 代;经过一次垃圾回收后,依然存在的 对象,便会依次从上一代挪到下一代。而每一代启动自动垃圾回收的阈值,则是可以单独指定 的。当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回 收。
事实上,分代收集基于的思想是,新生的对象更有可能被垃圾回收,而存活更久的对象也有更 高的概率继续存活。因此,通过这种做法,可以节约不少计算量,从而提高 Python 的性能。
3.Python上下文管理器
在Python中,上下文管理器(Context Manager)是一种用于管理资源的机制。它可以确保在进入和离开特定代码块时资源的正确获取和释放,无论是否发生异常。
上下文管理器通过定义__enter__()
和__exit__()
方法来实现。__enter__()
方法在进入代码块之前执行,负责获取资源或执行其他初始化操作。__exit__()
方法在离开代码块时执行,负责释放资源或进行清理操作。
使用上下文管理器可以确保资源的正确释放,即使发生异常也能够执行必要的清理操作,避免资源泄漏。
Python提供了两种使用上下文管理器的方式:使用with
语句和使用contextlib
模块的装饰器。
使用with
语句:
with open('file.txt', 'r') as file:
# 执行文件操作
content = file.read()
print(content)
# 离开with代码块后,文件资源会被自动关闭
在上面的示例中,open()
函数返回一个上下文管理器,它打开文件并返回文件对象。通过使用with
语句,我们可以在代码块中使用文件对象,并在离开代码块时自动关闭文件资源,无需手动调用file.close()
。
使用contextlib
模块的装饰器:
from contextlib import contextmanager
@contextmanager
def timer():
start = time.time()
yield
end = time.time()
print(f"Execution time: {end - start} seconds")
with timer():
# 执行需要计时的代码块
time.sleep(2)
在上面的示例中,我们使用contextlib
模块的contextmanager
装饰器定义了一个计时器的上下文管理器。通过使用with
语句,我们可以在代码块中执行需要计时的操作,并在离开代码块时自动打印执行时间。
使用上下文管理器可以更方便地管理资源,确保资源的获取和释放在正确的时机执行。它是一种良好的编程实践,可以提高代码的可读性和健壮性。同时,Python也提供了一些内置的上下文管理器,例如文件对象、数据库连接等,在使用这些资源时可以直接使用with
语句,简化了代码的编写。
4.Python数组的存储和处理Numpy模块
在Python中,NumPy(Numerical Python)是一个功能强大的库,用于处理大型、多维数组和矩阵,以及进行数值计算和科学计算。NumPy提供了高效的数组存储和处理功能,使得在Python中进行数值计算更加方便和高效。
下面是NumPy模块中数组的存储和处理的一些关键概念和功能:
4.1. NumPy数组(ndarray):
NumPy的核心数据结构是多维数组对象,称为ndarray(N-dimensional array)。它是一个固定大小的、相同类型的元素块集合。ndarray提供了高效的存储和操作多维数组数据的功能。
import numpy as np
# 创建一个一维数组
arr = np.array([1, 2, 3, 4, 5])
print(arr)
# 输出: [1 2 3 4 5]
# 创建一个二维数组
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr_2d)
# 输出:
# [[1 2 3]
# [4 5 6]]
4.2. 创建数组:
可以使用NumPy提供的函数来创建数组,例如numpy.array()
、numpy.zeros()
、numpy.ones()
、numpy.arange()
等。这些函数可以接受列表、元组或其他序列作为输入,并生成对应的NumPy数组。
import numpy as np
# 创建一个长度为5的零数组
zeros_arr = np.zeros(5)
print(zeros_arr)
# 输出: [0. 0. 0. 0. 0.]
# 创建一个形状为(2,3)的二维数组,元素全为1
ones_arr = np.ones((2, 3))
print(ones_arr)
# 输出:
# [[1. 1. 1.]
# [1. 1. 1.]]
# 创建一个从0到9的一维数组
range_arr = np.arange(10)
print(range_arr)
# 输出: [0 1 2 3 4 5 6 7 8 9]
4.3. 数组属性:
ndarray对象具有多个属性,例如shape
表示数组的维度和大小,dtype
表示数组的数据类型,size
表示数组中元素的总数等。
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.shape)
# 输出: (2, 3)
print(arr.dtype)
# 输出: int64
print(arr.size)
# 输出: 6
4.4 数组索引和切片:
可以使用索引和切片操作来访问和修改数组中的元素。NumPy数组的索引从0开始,可以使用整数索引、切片、布尔索引等方式进行数据的访问和操作。
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr[0])
# 输出: 1
print(arr[2:4])
# 输出: [3 4]
arr[3] = 10
print(arr)
# 输出: [ 1 2 3 10 5]
4.5. 数组运算:
NumPy提供了丰富的数学和逻辑运算函数,可以对数组进行元素级的数值计算,例如加法、减法、乘法、除法、求和、平均值、最大值、最小值等。这些运算可以对整个数组或数组的特定轴进行操作。
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 元素级加法
result = arr1 + arr2
print(result)
# 输出: [5 7 9]
# 元素级乘法
result = arr1 * arr2
print(result)
# 输出: [ 4 10 18]
# 求和
sum_result = np.sum(arr1)
print(sum_result)
# 输出: 6
# 平均值
mean_result = np.mean(arr2)
print(mean_result)
# 输出: 5.0
# 最大值
max_result = np.max(arr1)
print(max_result)
# 输出: 3
# 最小值
min_result = np.min(arr2)
print(min_result)
# 输出: 4
4.6. 广播(Broadcasting):
NumPy的广播功能允许不同形状的数组进行算术运算,自动地将较小的数组广播(复制)到较大数组的大小,以匹配形状,并执行运算。
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([[4], [5], [6]])
result = arr1 + arr2
print(result)
# 输出:
# [[5 6 7]
# [6
在上述示例中,数组arr1是一个2x3的数组,数组arr2是一个长度为3的一维数组。由于维度不匹配,但符合广播的规则,NumPy会自动将较小的数组arr2扩展为与arr1的形状相同,然后进行元素级的加法运算。
4.7. 数组的文件操作:
NumPy提供了函数来读取和写入数组数据到文件,例如numpy.save()
和numpy.load()
。这使得数据的持久化和共享变得更加方便。
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# 将数组保存到文件
np.save('array.npy', arr)
# 从文件加载数组
loaded_arr = np.load('array.npy')
print(loaded_arr)
# 输出: [1 2 3 4 5]
在上面的示例中,我们使用np.save()函数将数组arr保存到名为array.npy的文件中。然后,使用np.load()函数从文件中加载数组并将其存储在loaded_arr变量中。
4.8. 高级数组操作:
NumPy还提供了其他高级的数组操作,例如数组的转置、重塑、排序、去重、聚合函数、线性代数运算、统计分析函数等。文章来源:https://www.toymoban.com/news/detail-500373.html
通过利用NumPy模块,你可以高效地存储和处理大规模的数值数据,进行各种数学和科学计算,包括矩阵运算、信号处理、图像处理、统计分析、机器学习等领域。NumPy的广泛应用使得Python成为了一种强大的数值计算和科学计算语文章来源地址https://www.toymoban.com/news/detail-500373.html
import numpy as np
arr = np.array([[3, 2, 1], [4, 5, 6]])
# 转置数组
transposed_arr = arr.T
print(transposed_arr)
# 输出:
# [[3 4]
# [2 5]
# [1 6]]
# 重塑数组
reshaped_arr = arr.reshape(3, 2)
print(reshaped_arr)
# 输出:
# [[3 2]
# [1 4]
# [5 6]]
# 对数组进行排序
sorted_arr = np.sort(arr)
print(sorted_arr)
# 输出:
# [[1 2 3]
# [4 5 6]]
# 去除数组中的重复元素
unique_arr = np.unique(arr)
print(unique_arr)
# 输出: [1 2 3 4 5 6]
# 矩阵乘法
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])
mat_multiply = np.dot(mat1, mat2)
print(mat_multiply)
# 输出:
# [[19 22]
# [43 50]]
上述示例展示了NumPy模块中一些高级数组操作的用法。这些功能包括转置数组、重塑数组、排序数组、去重数组和矩阵乘法等。它们提供了更灵活和强大的数组处理功能,可以满足复杂的数值计算和科学计算需求。
到了这里,关于Python进阶知识(四)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!