(目录)


一、Python3 AI编程助手

在VSCode中使用Fitten Code插件 image.png

二、如何正确执行windows下的命令并不出乱码

windows下默认是gbk编码命令行字符串。

import subprocess

# 定义要运行的命令
command = "date /T"

# 使用subprocess模块运行命令
process = subprocess.Popen(
    command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

# 获取命令的输出结果
output, error = process.communicate()

# 打印输出结果
print(output.decode("gbk"))  # 根据命令行编码进行解码

# 打印错误信息
if error:
    print(error.decode("gbk"))  # 根据命令行编码进行解码

三、批量启动并发任务

python建议:

  • 使用多线程处理I/O等待
  • 使用多进程处理CPU受限

使用concurrent.futures来批量启动并发任务——通过进程/线程池。

1. 等待进程/线程池中的任务全部完成再返回

from concurrent import futures
import math
import time


def calc(val):
    time.sleep(1)
    result = math.sqrt(float(val))
    print(val, "sqrt:", result)
    return result


def use_threads(num, values):
    t1 = time.time()
    with futures.ThreadPoolExecutor(num) as tex:
        results = tex.map(calc, values)
        for res in results:
            print("threads:", res)
    t2 = time.time()
    return t2 - t1


def use_processes(num, values):
    t1 = time.time()
    with futures.ProcessPoolExecutor(num) as pex:
        results = pex.map(calc, values)
        for res in results:
            print("processes:", res)
    t2 = time.time()
    return t2 - t1


def main(workers, values):
    print(f"Using {workers} workers for {len(values)} values")
    t_sec = use_threads(workers, values)
    print(f"Threads took {t_sec:.4f} seconds")
    p_sec = use_processes(workers, values)
    print(f"Processes took {p_sec:.4f} seconds")


if __name__ == "__main__":
    v = input("workers: ")
    workers = int(v)
    values = list(range(1, 6))  # 1 .. 5
    main(workers, values)

2. 不等待并第一时间返回

from concurrent import futures
import math


def calc(val):
    result = math.sqrt(float(val))
    return val, result


def use_threads(num, values):
    with futures.ThreadPoolExecutor(num) as tex:
        tasks = [tex.submit(calc, value) for value in values]
        for f in futures.as_completed(tasks):
            yield f.result()


def use_processes(num, values):
    with futures.ProcessPoolExecutor(num) as pex:
        tasks = [pex.submit(calc, value) for value in values]
        for f in futures.as_completed(tasks):
            yield f.result()


def main(workers, values):
    print(f"Using {workers} workers for {len(values)} values")
    print("Using threads:")
    for val, result in use_threads(workers, values):
        print(f"{val} {result:.4f}")
    print("Using processes:")
    for val, result in use_processes(workers, values):
        print(f"{val} {result:.4f}")


if __name__ == "__main__":
    v = input("workers: ")
    workers = int(v)
    values = list(range(1, 6))  # 1 .. 5
    main(workers, values)

yield用于第一时间返回结果。

四、事件驱动的异步执行

借助于gevent实现。安装gevent:

pip install gevent

使用gevent版本的socket

import gevent
from gevent import socket

hosts = ["www.sina.com", "www.baidu.com", "juejin.cn"]
jobs = [gevent.spawn(gevent.socket.gethostbyname, host) for host in hosts]
gevent.joinall(jobs, timeout=5)
for job in jobs:
    print(job.value)

使用猴子补丁的socket

import gevent
from gevent import monkey

monkey.patch_all()
import socket

hosts = ["www.sina.com", "www.baidu.com", "juejin.cn"]
jobs = [gevent.spawn(socket.gethostbyname, host) for host in hosts]
gevent.joinall(jobs, timeout=5)
for job in jobs:
    print(job.value)

使用gevent实现的ping——借助于gevent.socket的相关函数

import gevent.time
from gevent.socket import gethostbyname, create_connection


def ping(host, count=1, timeout=2):
    """
    Ping a host count times, with a given timeout.
    :param host: The host to ping.
    :param count: The number of times to ping.
    :param timeout: The timeout in seconds.
    :return: A list of floats representing the ping times in seconds.
    """
    ip = gethostbyname(host)
    ping_times = []

    for _ in range(count):
        start = gevent.time.time()
        try:
            create_connection((ip, 80), timeout=timeout).close()
            end = gevent.time.time()
            ping_times.append(end - start)
        except gevent.timeout.Timeout:
            ping_times.append("Request timed out.")

    return ping_times


# 使用示例
if __name__ == "__main__":
    host = "www.sina.com"
    ping_times = ping(host, count=4, timeout=2)
    for ping_time in ping_times:
        print(ping_time)

使用gevent的猴子补丁来实现windows下的批量ping

import gevent
from gevent.monkey import patch_all

patch_all() # 打猴子补丁
import subprocess

ping_cmd = "ping -n 1 "


def ping(host):
    """
    发送ping命令并等待响应
    这里shell=True表示要通过shell来发送命令,此时命令部分使用的是字符串。
    如果不使用shell=True,则表示不使用shell来发送命令,此时命令部分要使用列表,比如:
    ['ping', '-c', '1', host]
    """
    p = subprocess.Popen(
        ping_cmd + host, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    stdout, stderr = p.communicate()
    if "往返行程的估计时间(以毫秒为单位):" in stdout.decode("gbk"):
        print(f"{host} is alive")


hosts = ["192.168.53." + str(i) for i in range(1, 255)]  # 生成IP地址列表
jobs = [gevent.spawn(ping, host) for host in hosts]  # 创建gevent任务列表
gevent.joinall(jobs, timeout=60)  # 等待所有任务结束,最多等待60秒

不论是gevent版本的函数,还是猴子补丁(monkey-patching)都只对Python代码起效,对C编写的库无效。

五、异步时间驱动网络框架twisted

twisted官网:https://twisted.org/

### knock_server.py
from twisted.internet import protocol, reactor


class Knock(protocol.Protocol):
    def dataReceived(self, data):
        r_data = str(data, encoding="utf-8")
        print("Client:", r_data)
        if r_data.startswith("Knock knock"):
            response = "Who's there?"
        else:
            response = r_data + " who?"
        print("Server:", response)
        self.transport.write(bytes(response, encoding="utf-8"))


class KnockFactory(protocol.Factory):
    def buildProtocol(self, addr):
        return Knock()


reactor.listenTCP(8000, KnockFactory())
reactor.run()

### knock_client.py
from twisted.internet import reactor, protocol


class KnockClient(protocol.Protocol):

    def connectionMade(self):
        data = bytes("Knock knock", encoding="utf-8")
        self.transport.write(data)

    def dataReceived(self, data):
        r_data = str(data, encoding="utf-8")
        if r_data.startswith("Who's there?"):
            response = bytes("Disappearing client", encoding="utf-8")
            self.transport.write(response)
        else:
            self.transport.loseConnection()
            reactor.stop()


class KnockFactory(protocol.ClientFactory):
    protocol = KnockClient


def main():
    f = KnockFactory()
    reactor.connectTCP("localhost", 8000, f)
    reactor.run()


if __name__ == "__main__":
    main()