【Python基础】进程

文章目录

    • @[toc]
      • 程序与进程的区别与联系
      • 同步任务
        • 示例
      • 并行任务
        • 示例
        • 进程调度的“随机性”
      • 进程属性与方法
        • process_object.start()方法
        • process_object.join()方法
        • process_object.daemon属性
          • 没有设置守护进程的情况
          • 设置守护进程的情况
        • process_object.current_process()方法
      • 进程通信
        • 队列的使用
          • put()方法与full()方法
          • get()方法与empty()方法
          • put_nowait()方法
          • get_nowait()方法
          • task_done()方法与join()方法
            • 队列内置计数器的值不为0
            • 队列内置计数器的值为0
        • 使用队列完成进程通信
      • 进程池
        • multiprocessing.Pool
        • ProcessPoolExecutor API
          • 进程池的创建
          • done()方法
          • cancel()方法
          • result()方法
          • as_completed()方法
          • map()方法
          • wait()方法
      • 自定义进程类
        • 示例

因上努力

个人主页:丷从心·

系列专栏:Python基础

学习指南:Python学习指南

果上随缘


程序与进程的区别与联系

  • 程序是指存储在磁盘或其他存储介质上的一组指令或代码,是静态文件
  • 进程是程序的执行实例,是一个动态的实体,具有独立的内存空间和系统资源

同步任务

  • 我们在此之前编写的代码都是同步代码,代码从上到下按顺序执行,如果前一个任务没有完成,那么不能运行之后的任务
示例
import timedef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)start = time.time()work_1()
work_2()end = time.time()print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 4.020323038101196 s
  • 可以看到整个程序用时 4 4 4秒,work_2()需要等待work_1()运行结束后才能运行

并行任务

  • 使用进程来运行上面的代码,能够优化运行时间
示例
import time
import multiprocessingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# Windows 操作系统下进程任务必须拥有 main 入口
if __name__ == '__main__':# 通过 Process 类创建进程对象, 并使用 target 绑定进程对象要运行的任务p1 = multiprocessing.Process(target=work_1)p2 = multiprocessing.Process(target=work_2)start = time.time()# 运行进程p1.start()p2.start()p1.join()p2.join()end = time.time()print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 2.6914994716644287 s
  • 可以看到整个程序用时约 3 3 3秒,work_1()work_2并行运行
进程调度的“随机性”
  • 下面的示例可以看到操作系统调度进程时的“随机性”
import time
import threadingdef work_1():for i in range(5):print('任务1...')time.sleep(2)def work_2():for i in range(5):print('任务2...')time.sleep(2)t1 = threading.Thread(target=work_1)
t2 = threading.Thread(target=work_2)t1.start()
t2.start()
任务2...
任务1...
任务1...
任务2...
任务2...
任务1...
任务1...
任务2...
任务2...
任务1...
  • 可以看到任务 1 1 1和任务 2 2 2的调度顺序是我们无法确定的,是由操作系统的调度算法决定的

进程属性与方法

  • 在学习进程方法之前,我们需要知道Python程序是如何被运行的
    • 一个Python文件被解释器运行时会在操作系统中创建一个进程
    • 然后该进程会创建一个线程来运行文件中的代码,这个程序最初创建的线程称为主线程
    • 当主线程运行到p = multiprocessing.Process()时会创建一个新的进程,称为子进程
    • 主进程与子进程由操作系统进行调度,并发地运行,具体如何调度进程由操作系统的调度算法决定
    • 子进程在运行时,主进程不会等待子进程,而是继续向下执行,直到执行到文件末尾没有代码时,主进程会等待子进程运行结束后再退出
process_object.start()方法
  • p = multiprocessing.Process()只是创建了一个进程,并不会运行进程代码

  • p.start()使进程p达到就绪状态,等待操作系统进行调度,具体何时调度由操作系统决定

  • 以上面的并行任务的代码为例,先注释掉p1.join()p2.join()

import time
import multiprocessingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# Windows 操作系统下进程任务必须拥有 main 入口
if __name__ == '__main__':# 通过 Process 类创建进程对象, 并使用 target 绑定进程对象要运行的任务p1 = multiprocessing.Process(target=work_1)p2 = multiprocessing.Process(target=work_2)start = time.time()# 运行进程p1.start()p2.start()# p1.join()# p2.join()end = time.time()print(f'总共用时: {end - start} s')
总共用时: 0.012919902801513672 s
任务1...
任务2...
  • 可以看到主进程没有等待子进程,而是继续向下执行
  • 当执行到end = time.time()时,此时end记录的时间是主进程运行到这行代码的时间
  • 之后运行print(f'总共用时: {end - start} s'),输出时间0.012919902801513672 s,此时执行到了文件末尾没有其他代码,主进程会等待子进程运行结束后再退出
  • 为了能正确记录进程运行的时间,我们需要让主进程等待子进程
process_object.join()方法
  • p.join()使主进程等待子进程,子进程任务执行结束后主进程再继续向下执行
  • 仍然以上面的并行任务的代码为例,取消注释p1.join()p2.join()
import time
import multiprocessingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# Windows 操作系统下进程任务必须拥有 main 入口
if __name__ == '__main__':# 通过 Process 类创建进程对象, 并使用 target 绑定进程对象要运行的任务p1 = multiprocessing.Process(target=work_1)p2 = multiprocessing.Process(target=work_2)start = time.time()# 运行进程p1.start()p2.start()p1.join()p2.join()end = time.time()print(f'总共用时: {end - start} s')
任务1...
任务2...
总共用时: 2.6913790702819824 s
  • 可以看到主进程等待子进程运行结束后才继续向下执行,正确记录了子进程运行的时间
process_object.daemon属性
  • 设置守护进程,需要在进程启动之前进行设置
  • 如果一个进程是守护进程,那么主进程运行到文件末尾后不论子进程任务是否结束都会自动退出
没有设置守护进程的情况
import time
import multiprocessingdef work():for i in range(5):print(i)time.sleep(1)if __name__ == '__main__':p = multiprocessing.Process(target=work)# p.daemon = Truep.start()print('主进程即将退出...')
主进程即将退出...
0
1
2
3
4
设置守护进程的情况
import time
import multiprocessingdef work():for i in range(5):print(i)time.sleep(1)if __name__ == '__main__':p = multiprocessing.Process(target=work)p.daemon = Truep.start()print('主进程即将退出...')
主进程即将退出...
  • 可以看到并没有输出 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4,主进程就退出了
process_object.current_process()方法
  • process_object.current_process()方法用于获取当前进程对象的引用
  • 可以用来获取进程名称和进程号
import multiprocessing
import osdef work():pid = multiprocessing.current_process().pidppid = os.getppid()name = multiprocessing.current_process().nameprint(f'pid: {pid},\t ppid: {ppid}, name: {name}')if __name__ == '__main__':for i in range(5):t = multiprocessing.Process(target=work)t.name = f'进程-{i}'t.start()t.join()
pid: 16464,	 ppid: 23240, name: 进程-0
pid: 21140,	 ppid: 23240, name: 进程-1
pid: 7884,	 ppid: 23240, name: 进程-2
pid: 19412,	 ppid: 23240, name: 进程-3
pid: 6196,	 ppid: 23240, name: 进程-4
  • os.getppid()方法用于获取进程的父进程号

进程通信

队列的使用
put()方法与full()方法
  • put()方法用于将数据上传到队列
  • full()方法用于判断队列是否已满
from queue import Queuequeue = Queue(3)  # 设置队列最大容量为 3queue.put(1)
queue.put(2)
queue.put(3)print(queue.full())queue.put(4)  # 因为队列已满, 所以会导致阻塞, 直到队列重新有空闲空间时解阻塞
True
  • 当队列中已满时,使用put()方法会阻塞,直到队列重新重新有空闲空间时解阻塞
get()方法与empty()方法
  • get()方法用于从队列中读取数据
  • empty()方法用于判断队列是否为空
from queue import Queuequeue = Queue()queue.put(1)
queue.put(2)
queue.put(3)print(queue.get())
print(queue.get())
print(queue.get())
print(queue.get())  # 因为队列为空, 所以会导致阻塞, 直到队列重新有数据时解阻塞
1
2
3
  • 当队列中为空时,使用get()方法会阻塞,直到队列重新有数据时解阻塞
put_nowait()方法
  • put_nowait()方法用于向队列中上传数据,如果队列已满,则抛出异常
from queue import Queuequeue = Queue(3)  # 设置队列最大容量为 3queue.put(1)
queue.put(2)
queue.put(3)queue.put_nowait(4)  # 如果队列已满, 则抛出异常
Traceback (most recent call last):File "C:/Users/FOLLOW_MY_HEART/Desktop/Python Basics/【Python基础】进程/test.py", line 9, in <module>queue.put_nowait(4)  # 如果队列已满, 则抛出异常File "D:\Environment\anaconda\envs\py310\lib\queue.py", line 191, in put_nowaitreturn self.put(item, block=False)File "D:\Environment\anaconda\envs\py310\lib\queue.py", line 137, in putraise Full
queue.Full
get_nowait()方法
  • get_nowait()方法用于从队列中读取数据,如果队列为空,则抛出异常
from queue import Queuequeue = Queue()queue.put(1)
queue.put(2)
queue.put(3)print(queue.get())
print(queue.get())
print(queue.get())
print(queue.get_nowait())  # 如果队列为空, 则抛出异常
1
2
3
Traceback (most recent call last):File "C:/Users/FOLLOW_MY_HEART/Desktop/Python Basics/【Python基础】进程/test.py", line 12, in <module>print(queue.get_nowait())  # 如果队列为空, 则抛出异常File "D:\Environment\anaconda\envs\py310\lib\queue.py", line 199, in get_nowaitreturn self.get(block=False)File "D:\Environment\anaconda\envs\py310\lib\queue.py", line 168, in getraise Empty
_queue.Empty
task_done()方法与join()方法
  • queue.get()会使队列内置计数器的值加 1 1 1,而queue.get()不会使队列内置计数器的值减 1 1 1

  • task_done()方法用于将队列内置计数器的值减 1 1 1

  • join()方法用于判断队列内置计数器的值是否为 0 0 0,如果不为 0 0 0则会阻塞,直到队列内置计数器的值为 0 0 0时解阻塞

队列内置计数器的值不为0
from queue import Queuequeue = Queue()
queue.put(1)queue.get()queue.join()print('不能运行到这里...')

队列内置计数器的值为0
from queue import Queuequeue = Queue()
queue.put(1)queue.get()
queue.task_done()queue.join()print('能够运行到这里...')
能够运行到这里...
使用队列完成进程通信
from multiprocessing import Process, Queue, Event# 向队列中写入数据
def write(queue, event):for num in range(10):queue.put(num)event.set()print('正在上传...')queue.put('END')event.set()# 从队列中读取数据
def read(queue, event):while True:event.wait()num = queue.get()if num == 'END':breakprint(f'从队列中读取到的数据为: {num}')if __name__ == '__main__':# 创建队列对象queue = Queue()event = Event()p_write = Process(target=write, args=(queue, event))p_read = Process(target=read, args=(queue, event))p_write.start()p_read.start()p_write.join()p_read.join()print('任务完成...')
正在上传...
从队列中读取到的数据为: 0
正在上传...
从队列中读取到的数据为: 1
正在上传...
正在上传...
正在上传...
从队列中读取到的数据为: 2
正在上传...
从队列中读取到的数据为: 3
从队列中读取到的数据为: 4
从队列中读取到的数据为: 5
正在上传...
正在上传...
正在上传...
从队列中读取到的数据为: 6
从队列中读取到的数据为: 7
从队列中读取到的数据为: 8
正在上传...
从队列中读取到的数据为: 9
任务完成...

进程池

  • 进程对象的创建需要时间,在需要创建大量进程对象的时候会发生性能下降的情况
  • 使用进程池会创建出一定数量的进程对象,并且进程在执行完任务后不会被操作系统销毁,这样下一个任务可以重复使用之前创建的这些进程对象
multiprocessing.Pool
import time
import os
from multiprocessing import Pooldef get_html(page):time.sleep(1)pid = os.getpid()print(f'pid: {pid},\t Successfully obtained page {page}...')return pageif __name__ == '__main__':# 创建进程池对象pool = Pool(3)# 通过 apply_async 提交需要异步执行的函数到进程池中for i in range(10):pool.apply_async(get_html, args=(i,))pool.close()  # 关闭进程池提交pool.join()
pid: 20516,	 Successfully obtained page 1...
pid: 23224,	 Successfully obtained page 0...
pid: 15036,	 Successfully obtained page 2...
pid: 23224,	 Successfully obtained page 4...
pid: 20516,	 Successfully obtained page 3...
pid: 15036,	 Successfully obtained page 5...
pid: 20516,	 Successfully obtained page 7...
pid: 23224,	 Successfully obtained page 6...
pid: 15036,	 Successfully obtained page 8...
pid: 20516,	 Successfully obtained page 9...
ProcessPoolExecutor API
进程池的创建
import time
from concurrent.futures import ProcessPoolExecutordef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)# 通过 submit 提交需要执行的函数到进程池中task_1 = executor.submit(get_html, 1)task_2 = executor.submit(get_html, 2)
Successfully obtained page 1...
Successfully obtained page 2...
done()方法
  • done()方法用于判断某个任务是否完成
import time
from concurrent.futures import ProcessPoolExecutordef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)# 通过 submit 提交需要执行的函数到进程池中task_1 = executor.submit(get_html, 1)task_2 = executor.submit(get_html, 2)# done() 方法用于判断某个任务是否完成print(f'task_1 完成情况: {task_1.done()}')
task_1 完成情况: False
Successfully obtained page 1...
Successfully obtained page 2...
cancel()方法
  • cancel()方法用于取消未运行的任务,已经运行的任务无法被取消
import time
from concurrent.futures import ProcessPoolExecutordef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=1)# 通过 submit 提交需要执行的函数到进程池中task_1 = executor.submit(get_html, 1)task_2 = executor.submit(get_html, 2)# cancel() 方法用于取消未运行的任务, 已经运行的任务无法被取消print(f'task_2 任务取消: {task_2.cancel()}')
task_2 任务取消: True
Successfully obtained page 1...
  • 通过将max_workers的值修改为 1 1 1,使得task_2未能运行时就被取消
result()方法
  • submit()方法的返回值是一个future对象
  • 通过对future对象调用result()方法可以获取任务的返回值
import time
from concurrent.futures import ProcessPoolExecutordef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)# 通过 submit 提交需要执行的函数到进程池中task_1 = executor.submit(get_html, 1)task_2 = executor.submit(get_html, 2)# 通过对 future 对象调用 result() 方法获取任务的返回值print(f'task_1 返回结果: {task_1.result()}')print(f'task_2 返回结果: {task_2.result()}')
Successfully obtained page 1...
Successfully obtained page 2...
task_1 返回结果: 1
task_2 返回结果: 2
as_completed()方法
  • as_completed()方法用于获取已经执行成功的任务的返回值
import time
from concurrent.futures import ProcessPoolExecutor, as_completeddef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)page_list = [1, 2, 3, 4]# 批量提交任务并获取已经执行成功的任务的返回值all_tasks = [executor.submit(get_html, page) for page in page_list]# 只要进程任务执行完就能获取到返回值, 完成一个任务获取一个任务的返回值for future in as_completed(all_tasks):data = future.result()print(f'Get data {data}')
Successfully obtained page 1...
Successfully obtained page 2...
Successfully obtained page 3...
Get data 1
Successfully obtained page 4...
Get data 2
Get data 3
Get data 4
map()方法
  • map()方法用于提交任务并获取已经执行成功的任务的返回值
import time
from concurrent.futures import ProcessPoolExecutordef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)page_list = [1, 2, 3, 4]# map() 方法用于提交任务并获取已经执行成功的任务的返回值for data in executor.map(get_html, page_list):print(f'Get data {data}')  # 打印的返回值顺序与列表顺序一致
Successfully obtained page 1...
Successfully obtained page 2...
Successfully obtained page 3...
Successfully obtained page 4...
Get data 1
Get data 2
Get data 3
Get data 4
wait()方法
  • wait()方法用于使主进程堵塞,直到指定任务完成后,主进程才解堵塞
import time
from concurrent.futures import ProcessPoolExecutor, waitdef get_html(page):print(f'Successfully obtained page {page}...')time.sleep(1)return pageif __name__ == '__main__':# 创建进程池对象executor = ProcessPoolExecutor(max_workers=2)page_list = [1, 2, 3, 4]# 批量提交任务并获取已经执行成功的任务的返回值all_tasks = [executor.submit(get_html, page) for page in page_list]# wait() 方法用于使主进程堵塞, 直到指定任务完成后, 主进程才解堵塞wait(all_tasks)print('主进程解堵塞, 执行剩余代码...')
Successfully obtained page 1...
Successfully obtained page 2...
Successfully obtained page 3...
Successfully obtained page 4...
主进程解堵塞, 执行剩余代码...

自定义进程类

  • 自定义进程类需要继承Process
  • 需要重写Process类中的run()方法,用于运行进程任务
示例
import requests
import multiprocessingclass ProcessSpider(multiprocessing.Process):def __init__(self, url):super().__init__()self.url = urldef run(self):response = requests.get(self.url).contentfile_name = self.url.split('/')[-1]with open(file_name, 'wb') as f:f.write(response)print('下载完成...')if __name__ == '__main__':url_list = ['http://pic.bizhi360.com/bbpic/98/10798.jpg','http://pic.bizhi360.com/bbpic/92/10792.jpg','http://pic.bizhi360.com/bbpic/86/10386.jpg']for url in url_list:process_spider = ProcessSpider(url)process_spider.start()
下载完成...
下载完成...
下载完成...

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数仓开发:DIM层数据处理

一、了解DIM层 这个就是数仓开发的分层架构 我们现在是在DIM层&#xff0c;从ods表中数据进行加工处理&#xff0c;导入到dwd层&#xff0c;但是记住我们依然是在DIM层&#xff0c;而非是上面的ODS和DWD层。 二、处理维度表数据 ①先确认hive的配置 -- 开启动态分区方案 -- …

c++:优先级队列(priority queue)使用及底层详解,附带仿函数初步使用

文章目录 优先级队列的使用大堆小堆**注意** 优先级队列的模拟实现pushpopsizeemptytop 仿函数仿函数是什么pushpop 仿函数结合优先级队列的优势 优先级队列的使用 优先级队列本质是就是完全二叉树,是个堆.我们可以用优先级队列来取出一段序列中的前N个最大值. priority_queue…

Postman的一些使用技巧

Postman 是一个流行的 API 开发工具&#xff0c;用于设计、开发、测试、发布和监控 API。在现代web开发中使用非常广泛。后端开发必备而且必会的工具。 目录 1.配置环境变量 2.动态变量 3.脚本 4.测试 5.模拟 6.监控 7.集合运行器 8.响应保存 9.请求历史 10.同步请求…

【论文阅读笔记】关于“二进制函数相似性检测”的调研(Security 22)

个人博客链接 注&#xff1a;部分内容参考自GPT生成的内容 [Security 22] 关于”二进制函数相似性检测“的调研&#xff08;个人阅读笔记&#xff09; 论文&#xff1a;《How Machine Learning Is Solving the Binary Function Similarity Problem》&#xff08;Usenix Securi…

面试算法-链表-反转链表(golang、c++)

目录 1、题目 2、解题思路 2.1 遍历、迭代 2.2 递归 3、源代码 3.1 c 3.2 golang 4、复杂度分析 4.1 遍历、迭代法 4.2 迭代法 1、题目 链表是一种常用的数据结构&#xff0c;链表的特点是插入、删除节点的效率非常高&#xff0c;因为他不需要移动其他任何元素&…

Linux——守护进程化(独立于用户会话的进程)

目录 前言 一、进程组ID与会话ID 二、setsid() 创建新会话 三、daemon 守护进程 前言 在之前&#xff0c;我们学习过socket编程中的udp通信与tcp通信&#xff0c;但是当时我们服务器启动的时候&#xff0c;都是以前台进程的方式启动的&#xff0c;这样很不优雅&#xff0c…

数据分析:基于DESeq2的转录组功能富集分析

介绍 DESeq2常用于识别差异基因&#xff0c;它主要使用了标准化因子标准化数据&#xff0c;再根据广义线性模型判别组间差异&#xff08;组间残差是否显著判断&#xff09;。在获取差异基因结果后&#xff0c;我们可以进行下一步的富集分析&#xff0c;常用方法有基于在线网站…

银行智能化数据安全分类分级实践分享

文章目录 前言一、数据安全智能分类分级平台建设背景二、数据安全分类分级建设思路和实践1、做标签– 数据安全标签体系2、打标签– 鹰眼智能打标平台 3.03、用标签– 全行统一“数据安全打标签结果”服务提供前言 随着国家对数据安全的高度重视,以及相关法律法规的出台,数据…

python数据分析中数据可视化简单入门

1.折线图表 首先引入相关包pyecharts&#xff0c;如果没下载可以先下载 pip install pyecharts from pyecharts.charts import Lineline Line() # 添加x轴 line.add_xaxis([呱了个呱,羊村,牟多,蜂地,喵帕斯]) # 添加y轴 line.add_yaxis("GDP",[50,30,40,34,63,22])…

epoll的LT和ET模式介绍

目录 1.epoll的LT和ET模式介绍 2.epoll的ET模式如何处理 2.1 epoll的ET模式编程读取数据的处理方式 2.2 将描述符设置为非阻塞模式的方法 3.ET模式的总结 4.epoll的LT模式和ET模式总结 5.IO复用总结 1.epoll的LT和ET模式介绍 epoll对文件描述符有两种操作模式: LT(Leve…

Linux(openEuler、CentOS8)常用的IP修改方式(文本配置工具nmtui+配置文件+nmcli命令)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS类似&#xff0c;可参考本文&#xff09;---- 一、知识点 &#xff08;一&#xff09;文本配置工具nmtui(openEuler已预装) nmtui&#xff08;NetworkManager Text User Interface&#xff09;是一…

在线OJ——链表经典例题详解

引言&#xff1a;本篇博客详细讲解了关于链表的三个经典例题&#xff0c;分别是&#xff1a;环形链表&#xff08;简单&#xff09;&#xff0c;环形链表Ⅱ&#xff08;中等&#xff09;&#xff0c;随机链表的复制&#xff08;中等&#xff09;。当你能毫无压力地听懂和成功地…

【莫比乌斯变换-02】关于莫比乌斯变换属性梳理

文章目录 一、说明二、多视角看莫比乌斯变换2.1 从几何角度2.2 复分析中的莫比乌斯变换2.3 莫比乌斯变换运算组合2.4 莫比乌斯变换的不动点2.5 三个点决定一个莫比乌斯变换2.6 交叉比2.7 莫比乌斯变换的逆变换 三 莫比乌斯变换性质证明3.1 证明1&#xff1a;莫比乌斯变换将圆变…

【莫比乌斯变换-03】python实现圆对圆的变换

文章目录 一、说明二、python实现复平面的莫比乌斯变换三、线的变换四、画笑脸 一、说明 我们在前面的文章中&#xff0c;叙述了莫比乌斯变换的复数分析&#xff0c;以及种种几何属性&#xff0c;本篇中叙述如何程序地实现&#xff1a;复平面上的圆在莫比乌斯变换下的图像是另…

基于高德 API 的自动获取气候数据的 Python 脚本

文章目录 高德申请 Key脚本介绍运行结果示例 源代码&#xff1a; https://github.com/ma0513207162/PyPrecip。pyprecip\reading\read_api.py 路径下。 项目介绍&#xff1a;PyPrecip 是一个专注于气候数据处理的 Python 库&#xff0c;旨在为用户提供方便、高效的气候数据处理…

linux基本操作

vim的基本操作 正常模式&#xff1a;启动vim后默认处于正常模式。不论位于什么模式&#xff0c;按下Esc建都会进入正常模式。 插入模式&#xff1a;在正常模式中按下i&#xff0c;l&#xff0c;a&#xff0c;A等键&#xff0c;会进入插入模式。现在只用记住按i键会进行插入模…

文本内容如何扫码展示?文本转二维码常用技巧

随着网络的不断发展&#xff0c;现在很多人会通过二维码的方式来作为内容的载体&#xff0c;用来给其他人提供需要的信息或者其他内容。其中&#xff0c;将文本制作二维码就是很常用的一种方式&#xff0c;通过扫码展示文字内容&#xff0c;比如信件、物品信息、联系方式等类型…

EPAI手绘建模APP资源管理和模型编辑器2

g) 矩形 图 26模型编辑器-矩形 i. 修改矩形的中心位置。 ii. 修改矩形的长度和宽度。 h) 正多边形 图 27模型编辑器-内接正多边形 图 28模型编辑器-外切正多边形 i. 修改正多边形的中心位置。 ii. 修改正多边形中心距离端点的长度。 iii. 修改正多边形的阶数。阶数为3&…

【Unity 协程】

Unity中的协程&#xff08;Coroutine&#xff09;是一种编程结构&#xff0c;它允许你以一种看似同步的方式编写可能需要异步执行的代码。协程特别适用于需要在一定时间后执行操作&#xff0c;或者在循环执行某段代码直到某个条件满足时的场景。 协程使用IEnumerator委托来实现…

基于HAL库的stm32中定时器的使用--定时器中断每隔一秒进行led灯的闪烁以及定时器生成PWM

一&#xff1a;什么是定时器 &#xff08;1&#xff09;stm32定时器&#xff0c;是存在于stm32单片机中的一个外设。stm32共有八个定时器&#xff0c;两个高级定时器&#xff08;TIM1、TIM8&#xff09;&#xff0c;四个通用定时器&#xff08;TIM2、TIM3、TIM4、TIM5&#xff…