Python子进程管理与进程信息获取

1. Python子进程模块subprocess

subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。

(1)run 方法

首先我们来看看 run 方法的使用,该方法的参数如下:

  • args:表示要执行的命令。必须是一个字符串或字符串参数列表。
  • stdinstdoutstderr:子进程的标准输入、输出和错误。其值可以是 subprocess.PIPEsubprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 Nonesubprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。
  • timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并抛出 TimeoutExpired 异常。
  • check:如果该参数设置为 True,并且进程退出状态码不是 0,则抛出 CalledProcessError 异常。
  • encoding:如果指定了该参数,则 stdinstdoutstderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。
  • shell:如果该参数为 True,将通过操作系统的 Shell 执行指定的命令。
  • capture_output:如果 capture_output = True,则将捕获 stdoutstderr,调用时内部的 Popen 对象将自动使用 stdout = PIPEstderr = PIPE 创建标准输出和标准错误对象;传递 stdoutstderr 参数时不能同时传递 capture_output 参数。如果希望捕获并将两个 stream 合并为一个,使用 stdout = PIPEstderr = STDOUT

下面我们来看一个例子,run 方法调用方式返回 CompletedProcess 实例:

import subprocessargs1 = ['python', 'src/Python子进程测试程序1.py']ret = subprocess.run(args=args1, capture_output=True, encoding='utf-8')  # 相当于在命令行执行:python src/Python子进程测试程序1.py
print(ret)  # CompletedProcess(args=['python', 'src/Python子进程测试程序1.py'], returncode=0, stdout='子进程输出: Hello World!\n', stderr='')if ret.returncode == 0:print('Success, stdout:', ret.stdout)  # Success, stdout: 子进程输出: Hello World!
else:print('Error, stderr:', ret.stderr)  # Error, stderr: python: can't open file 'D:\xxx\src\Python子进程测试程序1_Wrong.py': [Errno 2] No such file or directory

其中,Python子进程测试程序1.py 内容如下:

print('子进程输出: Hello World!')

(2)Popen 方法

Popensubprocess 的核心,子进程的创建和管理都靠它处理。

Popen 方法的参数如下:

  • args:Shell 命令,可以是字符串或者序列类型(如:列表、元组)。
  • bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认为 -10 表示不使用缓冲区,1 表示行缓冲,仅当 universal_newlines = True 时可用,也就是文本模式。正数表示缓冲区大小,负数表示使用系统默认的缓冲区大小。
  • stdinstdoutstderr:分别表示程序的标准输入、输出、错误句柄。
  • preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。
  • shell:如果该参数为 True,将通过操作系统的 Shell 执行指定的命令。
  • cwd:用于设置子进程的当前目录。
  • env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。

该方法会创建一个 Popen 对象, Popen 对象有以下几种方法:

  • poll():检查进程是否终止,如果终止返回 returncode,否则返回 None
  • wait(timeout):等待子进程终止。
  • communicate(input=None, timeout=None):和子进程交互,向子进程发送和读取数据。将 input 指定数据发送到 stdin;从 stdoutstderr 读取数据,直到到达文件末尾,等待进程终止。所以,返回值是一个元组:(stdout_data, stderr_data)。如果 timeout 时间内子进程不结束,则会抛出 TimeoutExpired 异常。其中需要注意的是,捕获异常之后,可以再次调用该函数,因为子进程并没有被 KILL。因此,如果超时结束程序的话,需要现正确 KILL 子进程。
  • send_signal(singnal):发送信号到子进程。
  • terminate():停止子进程,也就是发送 SIGTERM 信号到子进程。
  • kill():杀死子进程,发送 SIGKILL 信号到子进程。

Popen 方法的样例如下:

args2 = ['python', 'src/Python子进程测试程序2.py']proc = subprocess.Popen(args=args2,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding='utf-8')
print(proc)  # <Popen: returncode: None args: ['python', 'src/Python子进程测试程序2.py']>stdout, stderr = proc.communicate(input='AsanoSaki')
print('stdout:', stdout)  # stdout: 子进程输出:  AsanoSaki
print('stderr:', stderr)  # stderr: 空

其中,Python子进程测试程序2.py 内容如下:

s = input()
print('子进程输出: ', s)

现在我们来看一下 communicate 的用法,我们将测试程序修改为运行时间超过一秒:

args2 = ['python', 'src/Python子进程测试程序2.py']proc = subprocess.Popen(args=args2,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding='utf-8')try:stdout, stderr = proc.communicate(input='AsanoSaki', timeout=1)  # 设置1s超时时间
except subprocess.TimeoutExpired:print('子进程运行超时')proc.kill()  # 需要KILL子进程stdout, stderr = proc.communicate()  # 捕获异常之后,可以再次调用该函数print('stdout:', stdout)  # stdout: 子进程输出:  AsanoSakiprint('stderr:', stderr)  # stderr: 空

现在的 Python子进程测试程序2.py 内容如下:

s = input()
print('子进程输出: ', s)for i in range(10**9):pass

2. ThreadPoolExecutor线程池

concurrent.futures 模块是 Python3.2 中引入的新模块,用于支持异步执行,以及在多核 CPU 和网络 I/O 中进行高效的并发编程。线程池的基类是 concurrent.futures 模块中的 ExecutorExecutor 提供了两个子类,即 ThreadPoolExecutorProcessPoolExecutor ,简化了跨平台异步编程的实现。其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。如果使用线程池/进程池来管理并发编程,那么只要将相应的 Task 函数提交给线程池/进程池,剩下的事情就由线程池/进程池来搞定。

首先,让我们先来理解多进程和多线程两种并发编程的方式:

  • 多进程:当通过多进程来实现并发编程时,程序会将任务分配给多个进程,这些进程可以在不同的 CPU 上同时运行。进程之间是独立的,各自有自己的内存空间等,可以实现真正的并行执行。不过,进程之间的通信比较耗时,需要使用 IPC(进程间通信)机制,而且进程之间的切换比线程之间的切换耗时,所以创建进程的代价较高。
  • 多线程:当通过多线程来实现并发编程时,程序会将任务分配给多个线程,这些线程可以在同一个进程中的不同 CPU 核上同时运行。线程之间共享进程的内存空间,因此开销比较小。但是需要注意,在 Python 解释器中,线程是无法实现真正的并行执行,因为 Python 有 GIL(全局解释器锁),它确保同时只有一个线程运行 Python 代码。因此,一个 Python 进程中的多个线程并不能并行执行,在使用多线程编程时不能完全利用多核 CPU。

ThreadPoolExecutor 创建一个线程池,任务可以提交到这个线程池中执行。ThreadPoolExecutorProcessPoolExecutor 更容易使用,且没有像进程那样的开销。它可以让我们在一个 Python 解释器中进行跨线程异步编程,因为它规避了 GIL。

Exectuor 提供了如下常用方法:

  • submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,**kwargs 代表以关键字参数的形式为 fn 函数传入参数。
  • map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
  • shutdown(wait=True):关闭线程池。

程序将 fn 函数 submit 给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。

Future 对象提供了如下方法:

  • cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True
  • cancelled():返回 Future 代表的线程任务是否被成功取消。
  • running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True
  • done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True
  • result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
  • exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None
  • add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。

在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。

使用线程池来执行线程任务的步骤如下:

  1. 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
  2. 定义一个普通函数作为线程任务。
  3. 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
  4. 当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线程池。

下面我们来看一个例子:

from concurrent.futures import ThreadPoolExecutordef thread(num):print('Threads:', num)def getResult():  # 有返回结果的函数return 'Get Result'# 新建ThreadPoolExecutor对象并指定最大的线程数量
with ThreadPoolExecutor(max_workers=3) as executor:# 提交多个任务到线程池中executor.submit(thread, 1)  # Threads: 1executor.submit(thread, 2)  # Threads: 2t = executor.submit(getResult)print(t.result())  # Get Result# 或者按如下方式实现
threadPool = ThreadPoolExecutor(max_workers=3)
for i in range(3):threadPool.submit(thread, i)
threadPool.shutdown(wait=True)
# Threads: 0
# Threads: 1
# Threads: 2

3. 系统信息获取模块Psutil

现在可能会有人在想那我们如何获取子进程/线程在运行时的时间开销或者内存占用等信息呢?Python 有一个第三方模块 psutil,专门用来获取操作系统以及硬件相关的信息,比如:CPU、磁盘、网络、内存等等。

首先我们需要安装 psutil,直接通过 pip 命令安装即可:

pip install psutil

(1)查看 CPU 相关信息:

import psutilprint(psutil.cpu_count())  # CPU的逻辑数量:12print(psutil.cpu_count(logical=False))  # CPU的物理核心数量:6print(psutil.cpu_times())  # CPU的用户/系统/空闲时间
# scputimes(user=26860.4375, system=10963.515624999884, idle=676060.796875, interrupt=740.875, dpc=477.75)for _ in range(3):# interval表示每隔0.5s刷新一次,percpu表示查看所有的CPU使用率print(psutil.cpu_percent(interval=0.5, percpu=True))
# [21.2, 0.0, 32.4, 0.0, 16.1, 0.0, 0.0, 0.0, 3.2, 0.0, 0.0, 0.0]
# [25.8, 0.0, 3.1, 0.0, 0.0, 0.0, 3.1, 3.1, 0.0, 0.0, 0.0, 18.8]
# [32.4, 0.0, 21.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 6.2, 0.0]print(psutil.cpu_stats())  # CPU的统计信息,包括上下文切换、中断、软中断以及系统调用次数等
# scpustats(ctx_switches=3399680458, interrupts=1365489476, soft_interrupts=0, syscalls=2283205750)print(psutil.cpu_freq())  # CPU的频率
# scpufreq(current=2592.0, min=0.0, max=2592.0)

(2)查看内存及磁盘相关信息:

print(psutil.virtual_memory())  # 内存使用情况,分别为总内存、可用内存、内存占用率、已使用的内存大小、剩余的内存大小
# svmem(total=17022177280, available=7125008384, percent=58.1, used=9897168896, free=7125008384)print(psutil.swap_memory())  # 交换内存信息(专门用来临时存储数据)
# sswap(total=6030352384, used=5409898496, free=620453888, percent=89.7, sin=0, sout=0)print(psutil.disk_partitions())  # 磁盘分区、磁盘使用率和磁盘IO信息
# [sdiskpart(device='C:\\', mountpoint='C:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260),
#  sdiskpart(device='D:\\', mountpoint='D:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260),
#  sdiskpart(device='E:\\', mountpoint='E:\\', fstype='NTFS', opts='rw,fixed', maxfile=255, maxpath=260)]print(psutil.disk_usage("C:\\"))  # 某个磁盘使用情况
# sdiskusage(total=160253673472, used=101791543296, free=58462130176, percent=63.5)print(psutil.disk_io_counters())  # 磁盘IO统计信息,分别为读次数、写次数、读的字节数、写的字节数、读时间、写时间
# sdiskio(read_count=1833834, write_count=1831471, read_bytes=69098376704, write_bytes=59881958400, read_time=17952, write_time=2323)

(3)查看网络相关信息:

print(psutil.net_io_counters())  # 网卡的网络IO统计信息
# snetio(bytes_sent=629698806, bytes_recv=1756588411, packets_sent=1280472, packets_recv=2023810, errin=0, errout=0, dropin=0, dropout=0)print(psutil.net_io_counters(pernic=True))  # 列出所有网卡的信息
# {'Ethernet': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
#  'Local Area Connection* 3': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
#  'Local Area Connection* 4': snetio(bytes_sent=0, bytes_recv=0, packets_sent=0, packets_recv=0, errin=0, errout=0, dropin=0, dropout=0),
#  ......]print(psutil.net_if_addrs())  # 网络接口信息
# {'Ethernet': [snicaddr(family=<AddressFamily.AF_LINK: -1>, address='04-D4-C4-74-A4-F0', netmask=None, broadcast=None, ptp=None), snicaddr(family=<AddressFamily.AF_INET: 2>, address='169.254.216.112', netmask='255.255.0.0', broadcast=None, ptp=None)],
#  'Local Area Connection* 3': [snicaddr(family=<AddressFamily.AF_LINK: -1>, address='38-00-25-26-9C-70', netmask=None, broadcast=None, ptp=None), snicaddr(family=<AddressFamily.AF_INET: 2>, address='169.254.169.242', netmask='255.255.0.0', broadcast=None, ptp=None), snicaddr(family=<AddressFamily.AF_INET6: 23>, address='fe80::5e4e:63b6:7416:787b', netmask=None, broadcast=None, ptp=None)],
#  ......]print(psutil.net_if_stats())  # 网卡的详细信息,包括是否启动、通信类型、传输速度、mtu
# {'Ethernet': snicstats(isup=False, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=0, mtu=1500),
#  'vEthernet (Default Switch)': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=4294, mtu=1500),
#  'Loopback Pseudo-Interface 1': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=1073, mtu=1500),
#  ......]print(psutil.net_connections())  # 当前机器的网络连接,里面接受一个参数,默认是"inet",当然我们也可以指定为其它,比如"tcp"
# [sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_DGRAM: 2>, laddr=addr(ip='127.0.0.1', port=1309), raddr=(), status='NONE', pid=7516),
#  sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=9100), raddr=(), status='LISTEN', pid=6004),
#  sconn(fd=-1, family=<AddressFamily.AF_INET6: 23>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::', port=49667), raddr=(), status='LISTEN', pid=3768),
#  ......]print(psutil.users())  # 当前登录的用户信息
# [suser(name='AsanoSaki', terminal=None, host=None, started=1694966965.3425539, pid=None)]import datetime
print(psutil.boot_time())  # 系统的启动时间:1694912508.6818905
print(datetime.datetime.fromtimestamp(psutil.boot_time()))  # 2023-09-17 09:01:48.681890

(4)查看进程相关信息:

print(psutil.pids())  # 当前存在的所有进程的PID
# [0, 4, 8, 140, 212, 584, 756, 1052, 1160, 1188, 1292, 1364, 1384, ...]print(psutil.pid_exists(0))  # 某个进程是否存在
# Trueprint(psutil.process_iter())  # 所有进程对象(Process)组成的迭代器
# <generator object process_iter at 0x00000263C4D2AF20>print(psutil.Process(pid=10712))  # 根据PID获取一个进程对应的Process对象
# psutil.Process(pid=10712, name='pycharm64.exe', status='running', started='08:56:02')p = psutil.Process(pid=10712)  # 获取该Process对象print(p.name())  # 进程名称,pycharm64.exeprint(p.exe())  # 进程的exe路径:E:\PyCharm 2020.3.3\bin\pycharm64.exeprint(p.cwd())  # 进程的工作目录:E:\PyCharm 2020.3.3\jbr\binprint(p.cmdline())  # 进程启动的命令行:['E:\\PyCharm 2020.3.3\\bin\\pycharm64.exe']print(p.status())  # 进程状态:runningprint(p.username())  # 进程用户名:LAPTOP-23NEHV3U\AsanoSakiprint(p.create_time())  # 进程创建时间,返回时间戳:1694998562.3625667print(p.cpu_times())  # 进程使用的CPU时间
# pcputimes(user=277.09375, system=34.265625, children_user=0.0, children_system=0.0)print(p.memory_info())  # 进程所使用的内存
# pmem(rss=1507303424, vms=1790066688, num_page_faults=1466884, peak_wset=1536196608,
#      wset=1507303424, peak_paged_pool=881616, paged_pool=876144, peak_nonpaged_pool=268096,
#      nonpaged_pool=154024, pagefile=1790066688, peak_pagefile=1798070272, private=1790066688)print(p.num_threads())  # 进程内的线程数量,即这个进程开启了多少个线程:68

现在我们使用 psutil 模块实现获取 ThreadPoolExecutor 线程任务运行的时间与内存占用信息:

args2 = ['python', 'src/Python子进程测试程序2.py']proc = subprocess.Popen(args=args2,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding='utf-8')def getProcessInfo(pid):p = psutil.Process(pid)  # 获取pid所代表的子进程start_time = time.time()memory = 0while(True):try:memory = max(memory, p.memory_info().rss)except:breakruntime = (time.time() - start_time) * 1000return runtime, memorythreadPool = ThreadPoolExecutor()
task = threadPool.submit(getProcessInfo, proc.pid)
stdout, stderr = proc.communicate(input='AsanoSaki')
runtime, memory = task.result()
print(runtime)  # 510.7400417327881
print(memory)  # 40865792threadPool.shutdown(wait=True)
proc.kill()

其中,Python子进程测试程序2.py 内容如下:

s = input()
print('子进程输出: ', s)

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

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

相关文章

笔记1.5:计算机网络体系结构

从功能上描述计算机网络结构 分层结构 每层遵循某个网络协议完成本层功能 基本概念 实体&#xff1a;表示任何可发送或接收信息的硬件或软件进程。 协议是控制两个对等实体进行通信的规则的集合&#xff0c;协议是水平的。 任一层实体需要使用下层服务&#xff0c;遵循本层…

uniapp 小程序 父组件调用子组件方法

答案&#xff1a;配合小程序API > this.selectComponent("")&#xff0c;来选择组件&#xff0c;再使用$vm选择组件实例&#xff0c;再调用方法&#xff0c;或者data 1 设置组件的id,如果你的多端&#xff0c;请跟据情况设置ref,class,id&#xff0c;以便通过小…

区块链(1):区块链简介

区快链是通过密码技术保护的分布式数据库这是比特币背后的技术。 本文将逐步带您了解区块链。 1 区块链BLOCKCHAIN的类的定义 区块链有一个区块列表。 它从一个单独的块开始&#xff0c;称为 genesis block【创世区块】 2 区块链BLOCK的类的定义 第一个区块叫做 Genesis[…

QT5到QT6产生的一些变化【QT环境搭建篇】

1、QT 重写enterEvent 鼠标进入事件没有作用 widget中的事件函数 由原来的event(QEvent *event) 变为enterEvent(QEnterEvent *event) 重写改成这样就可以了 void enterEvent(QEnterEvent *event); 2、error C4996: QString::SkipEmptyParts&#xff08;“SkipEmptyParts”:…

一阶低通滤波器滞后补偿算法

一阶低通滤波器的推导过程和双线性变换算法请查看下面文章链接: PLC算法系列之数字低通滤波器(离散化方法:双线性变换)_双线性离散化_RXXW_Dor的博客-CSDN博客PLC信号处理系列之一阶低通(RC)滤波器算法_RXXW_Dor的博客-CSDN博客_rc滤波电路的优缺点1、先看看RC滤波的优缺点…

Redis 篇

1、为什么要用缓存&#xff1f; 使用缓存的目的就是提升读写性能。而实际业务场景下&#xff0c;更多的是为了提升读性能&#xff0c;带来更好的性能&#xff0c;带来更高的并发量。 Redis 的读写性能比 Mysql 好的多&#xff0c;我们就可以把 Mysql 中的热点数据缓存到 Redis…

设计模式:组合模式

目录 组件代码实现优缺点源码中应用总结 组合模式是一种结构型设计模式&#xff0c;用于将对象组织成树形结构&#xff0c;以表示“部分-整体”的层次结构。组合模式使得客户端可以统一地处理单个对象和组合对象&#xff0c;而不需要区分它们之间的差异。 在组合模式中&#x…

Linux学习第14天:Linux设备树(一):枝繁叶茂见晴天

本节笔记主要学习了Linux设备树相关知识点&#xff0c;由于内容较多&#xff0c;打算分两天进行总结。今天着重学习Linux设备树&#xff0c;主要包括前三节内容&#xff0c;分别是概念、格式和语法。 本节思维导图内容如下&#xff1a; 一、什么是设备树 设备树可以用一个图来进…

Vivado XADC IP核 使用详解

本文介绍Vivado中XADC Wizard V3.3的使用方法。 XADC简介 XADC Wizard Basic Interface Options&#xff1a; 一共三种&#xff0c;分别是AXI4Lite、DRP、None。勾选后可在界面左侧看到相应通信接口情况。Startup Channel Selection Simultaneous Selection&#xff1a;同时监…

qt4,qt5,qt6嵌入式linux运行Qt程序不同的地方

下面为Qt 4、Qt 5 和 Qt 6分别举例说明它们在嵌入式Linux上运行Qt程序的一些不同之处&#xff1a; Qt 4 示例场景&#xff1a; 假设你正在开发一个嵌入式Linux设备上的控制面板应用程序&#xff0c;该应用程序使用Qt 4。 不同之处&#xff1a; 构建系统&#xff1a; 使用qma…

长胜证券:煤价突破900元大关 GLP-1减重药进入集中获批期

上星期五&#xff0c;两市股指早盘震动上扬&#xff0c;午后回落走低。到收盘&#xff0c;沪指跌0.28%报3117.74点&#xff0c;深成指跌0.52%报10144.59点&#xff0c;创业板指涨跌0.45%报2002.73点&#xff0c;科创50指数涨0.71%&#xff1b;两市合计成交7217亿元&#xff0c;…

【算法训练-二叉树 一】【遍历二叉树】前序遍历、中序遍历、后续遍历、层序遍历、锯齿形层序遍历、二叉树右视图

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【二叉树的遍历】&#xff0c;使用【二叉树】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为…

OmniShade - Mobile Optimized Shader

OmniShade Pro是一款专为移动设备设计的高性能着色器。它包含多种技术,使其几乎可以实现从现实到卡通到动漫的任何外观,但由于自适应系统仅计算任何功能集所需的内容,它的速度也非常快。 它旨在弥合Unity的标准着色器和移动着色器之间的差距,但由于其高级别的风格化、组合…

浏览器事件机制详解

目录 前言 事件类型 鼠标事件 表单事件 窗口事件 DOM事件 多媒体事件 拖拽与放置事件 移动设备事件 剪切板事件 错误事件 过渡、动画事件 事件监听 onevent addEventListener(event) 事件触发 事件流程 捕获阶段 目标阶段 冒泡阶段 事件对象 总结 相关代…

Pyhton压缩JS代码

文章目录 1.安装依赖2.目录结构3.代码4.执行结果 1.安装依赖 pip install jsmin2.目录结构 3.代码 import jsmindef run(src_path, tgt_path):with open(src_path, "r", encodingutf-8) as input_file:with open(tgt_path, "w", encodingutf-8) as outpu…

【Java 基础篇】Java 字节流详解:从入门到精通

Java中的字节流是处理二进制数据的关键工具之一。无论是文件操作、网络通信还是数据处理&#xff0c;字节流都发挥着重要作用。本文将从基础概念开始&#xff0c;深入探讨Java字节流的使用&#xff0c;旨在帮助初学者理解和掌握这一重要主题。 什么是字节流&#xff1f; 在Ja…

万能适配器basequickadapter + recycleview实现单选并且默认选择第一个

1、首先&#xff0c;确保您已经添加了BaseQuickAdapter和RecyclerView的依赖项。您可以在项目的build.gradle文件中添加以下依赖项&#xff1a; dependencies {implementation com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4implementation androidx.recyclerview:r…

C/C++ sscanf正则测试

分号分割&#xff0c;等号解析 #include <iostream> #include <string.h>typedef struct{char name[32];int priority; }codes_t;int codes_get(char* str, codes_t* codes){int i 0;char *ptr;std::cout<<"before strtok str: "<<str<&…

Claude 使用指南 | 可与GPT-4媲美的语言模型

本文全程干货&#xff0c;让你轻松使用上claude&#xff0c;这也是目前体验cluade的唯一途径&#xff01;废话不多说&#xff0c;直接上教程&#xff0c;cluade的能力不逊于GPT4&#xff0c;号称是ChatGPT4.0最强竞品。相对Chatgpt来说&#xff0c;Claude不仅是完全免费的&…

每个高级前端工程师都应该知道的前端布局

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 😜 分享个人创业过程中的趣事 快来免费体验ChatGpt plus版本的,我们出的钱 体验地址:https://chat.waixingyun.cn 可以加入网站底部技术群,一起找bug,另外新版作图神器已上线…