python业务内容汇总_事件循环

1. 调用其他非包内的py文件

import sys
sys.path.append("path")
import path_py   #path路径下的path_py.py文件

2. 创建参数类

@dataclass的使用,方便调用参数值

  1. 首先引入dataclass.

使用此装饰器,新建student类,后续直接使用即可。

from dataclasses import dataclass
 
 
@dataclass
class Student:
    grade: int = 60
 
 
def test_demo():
    print(Student)
    print(Student.grade)
  1. 简单使用
from dataclasses import dataclass
@dataclass
class Student:
    # 定义学生类
    grade: int = 59
    pass_grade: int = 60
    def pass_or_not(self):
        # 简单的比较
        if self.grade > self.pass_grade:
            return True
        else:
            return False
 
 
def test_demo():
    xiaoming = Student(grade=60, pass_grade=59)
    # 新建学生小明,赋予小明成绩
    print(xiaoming.pass_or_not())
    xiaohong = Student()
    # 新建学生小红,使用默认成绩
    print(xiaohong.pass_or_not())

3. python异步的使用

import asyncio
from tqdm import tqdm
from tqdm.asyncio import tqdm_asyncio
1)创建事件以及需要异步运行的async方法

get_event_loop()

run_until_complete(异步方法)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()
2) 创建任务
task = []
for project in range(0,100):
    task.append(asyncio.create_task(function(project)))

async function中需要等待的时间需要进行 await

3)如果async function存在返回值
data = await asyncio.gather(*task)

或者

for done in asyncio.as_completed(task):
    await done
4)在异步中使用进度条

tqdm_asyncio.gather(*task)进行返回

或者使用

tqdm_asyncio.as_completed(task)

data = await tqdm_asyncio.gather(*task)
#or
for done in tqdm_asyncio.as_completed(task):
    await done

4.异步中的修饰器

1)python在正常形式上的装饰器

比如简单的统计在某一个function中的时间

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


@calculate_time
def test_fun():
    info = {"function": "test_fun", "desc": "describe"}
    return info

if __name__ == '__main__':
    print(test_fun())

2)python在异步上的装饰器

import time
import asyncio
def calculate_time(func):
    async def wrapper(*args, **kwargs):
        start_time = time.time()
        result = await func(*args, **kwargs)
        end_time = time.time()
        print(f"函数{func.__name__}执行时间{end_time - start_time}s")
        return result
    return wrapper

@calculate_time
async def test_fun():
    await asyncio.sleep(1)
    info = {"function": "test_fun", "desc": "describe"}
    return info

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    results = loop.run_until_complete(asyncio.gather(test_fun()))
    print(results)

3)不同的地方

1)与同步不同的情况是在修饰器 calculate_time中的wrapper需要使用async进行标识,并在wrapper中func是需要进行await

2)返回值上异步返回得到的resultslist类型,是因为进行asyncio.gather(test_fun())相当于是一个list列表

4)异步请求aiohttp

官方推荐使用一个客户端会话来发起所有请求,会话中记录了请求的cookie,但你还可以使用aiohttp.request来发送请求。

当我们使用 async def 就是定义了一个异步函数,异步逻辑由asyncio提供支持。

I 基础架构
  • async function
  • async create client session
  • async send request
  • await response

async with aiohttp.ClientSession() as session 为异步上下文管理器,在请求结束时或者发生异常请求时支持自动关闭实例化的客户端会话。

import asyncio
import aiohttp


async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://httpbin.org/get') as resp:
            print(resp.status)
            print(await resp.text())


if __name__ == '__main__':
    # python3.7才支持这种写法,作为一个入口函数,以debug模式运行事件循环
    asyncio.run(main(), debug=True)
    # python3.6及以下版本写法
    event_loop = asyncio.get_event_loop()
    results = event_loop.run_until_complete(asyncio.gather(main()))
    event_loop.close()
  • event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数。
  • coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
  • task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态。
  • future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别。
  • async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
II请求

①post请求方式

import asyncio
import aiohttp

async def main():
    data = b'\x00Binary-data\x00'  # 未经编码的数据通过bytes数据上传
    data = 'text'  # 传递文本数据
    data = {'key': 'value'}  # 传递form表单
    async with aiohttp.ClientSession() as sess:
        async with sess.post('http://httpbin.org/post', data=data) as resp:
            print(resp.status)
            print(await resp.text())


# 复杂的post请求
async def main2():
    pyload = {'key': 'value'}  # 传递pyload
    async with aiohttp.ClientSession() as sess:
        async with sess.post('http://httpbin.org/post', json=pyload) as resp:
            print(resp.status)
            print(await resp.text())


if __name__ == '__main__':
    asyncio.run(main())
    asyncio.run(main2())

②get请求(url中带有参数)

import asyncio
import aiohttp


async def main():
    """以下三种方式均可以"""
    params = {'key1': 'value1', 'key2': 'value2'}
    params = [('key', 'value1'), ('key', 'value2')]
    params = 'key=value+1'
    async with aiohttp.ClientSession() as sess:
        async with sess.get('http://httpbin.org/get', params=params) as resp:
            print(resp.status)
            print(await resp.text())


if __name__ == '__main__':
    asyncio.run(main())

③文件流

import asyncio
import aiohttp


async def main():
    """传递文件"""
    files = {'file': open('report.xls', 'rb')}
    async with aiohttp.ClientSession() as sess:
        async with sess.post('http://httpbin.org/post', data=files) as resp:
            print(resp.status)
            print(await resp.text())


async def main2():
    """实例化FormData可以指定filename和content_type"""
    data = aiohttp.FormData()
    data.add_field('file',
                   open('report.xls', 'rb'),
                   filename='report.xls',
                   content_type='application/vnd.ms-excel')
    async with aiohttp.ClientSession() as sess:
        async with sess.post('http://httpbin.org/post', data=data) as resp:
            print(resp.status)
            print(await resp.text())


async def main3():
    """流式上传文件"""
    async with aiohttp.ClientSession() as sess:
        with open('report.xls', 'rb') as f:
            async with sess.post('http://httpbin.org/post', data=f) as resp:
                print(resp.status)
                print(await resp.text())


async def main4():
    """因为content属性是 StreamReader(提供异步迭代器协议),所以您可以将get和post请求链接在一起。python3.6及以上才能使用"""
    async with aiohttp.ClientSession() as sess:
        async with sess.get('http://python.org') as resp:
            async with sess.post('http://httpbin.org/post', data=resp.content) as r:
                print(r.status)
                print(await r.text())


if __name__ == '__main__':
    asyncio.run(main())
    asyncio.run(main2())
    asyncio.run(main3())
    asyncio.run(main4())

④cookie共享

import aiohttp
import asyncio


async def main():
    async with aiohttp.ClientSession() as session:
        await session.get('http://httpbin.org/cookies/set?my_cookie=my_value')
        filtered = session.cookie_jar.filter_cookies('http://httpbin.org')
        print(filtered)
        assert filtered['my_cookie'].value == 'my_value'
        async with session.get('http://httpbin.org/cookies') as r:
            json_body = await r.json()
            print(json_body)
            assert json_body['cookies']['my_cookie'] == 'my_value'


if __name__ == "__main__":
    asyncio.run(main())

默认ClientSession使用的是严格模式的 aiohttp.CookieJar. RFC 2109,明确的禁止接受url和ip地址产生的cookie,只能接受 DNS 解析IP产生的cookie。

可以通过设置aiohttp.CookieJar 的 unsafe=True 来配置

jar = aiohttp.CookieJar(unsafe=True) session = aiohttp.ClientSession(cookie_jar=jar)

⑤SSL验证,代理

默认情况下,aiohttp对HTTPS协议使用严格检查,如果你不想上传SSL证书,可将ssl设置为False。

r = await session.get('https://example.com', ssl=False)

无授权代理

async with aiohttp.ClientSession() as session:
    async with session.get("http://python.org", proxy="http://proxy.com") as resp:
        print(resp.status)

代理授权的两种方式

# 第一种
async with aiohttp.ClientSession() as session:
    proxy_auth = aiohttp.BasicAuth('user', 'pass')
    async with session.get("http://python.org", proxy="http://proxy.com", proxy_auth=proxy_auth) as resp:
        print(resp.status)

# 第二种
session.get("http://python.org", proxy="http://user:pass@some.proxy.com")

⑥其他

  • resp.history 查看重定向历史
  • resp.headers 获取响应头
  • resp.cookies 获取响应cookie
  • resp.status 获取响应状态码
  • resp.text(encoding='utf-8) 获取响应文本
  • resp.json() 获取json响应数据
  • resp.read() 获取二进制响应数据
  • resp.content.read(100) 获取流响应,每次获取100个字节