aiohttp如何在类中保存持久性ClientSession?

时间:2018-11-27 09:45:22

标签: python httprequest python-asyncio aiohttp

我正在编写一个类,它将使用aiohttp进行http请求。根据文档,我不应该为每个请求创建一个ClientSession,所以我想重用同一会话。

代码:

public void CreateUniqueBuyList(List<EveObjModel> buyList)
{
    //sort by ascending type_id and then by ascending price and reverse it. so that,
    // object with higher price come first
    List<EveObjModel>tempList = buyList.OrderBy(x => x.type_id).ThenBy(x => x.price).Reverse().ToList();
    List<EveObjModel> uniqueBuyList = new List<EveObjModel>();
    for (int i = 0; i < tempList.Count; ++i) {
        if ((i > 1) && tempList[i - 1].type_id == tempList[i].type_id) continue; // if duplicate type_id then don't take it again
        uniqueBuyList.Add(tempList[i]);
    }

    foreach (EveObjModel item in uniqueBuyList.OrderBy(item => item.type_id))
    {
        buyListtextField.Text += $"Eve Online Item! Type-ID is: {item.type_id}, Price is {item.price}\n";
    }
}

这样做的时候

class TestApi:
   def __init__(self):
      self.session = aiohttp.ClientSession()

   # async defs methods from here 

我得到错误:TestApi()

持久保存Unclosed client session对象的解决方案是什么?

2 个答案:

答案 0 :(得分:2)

一行上的表达式TestApi()本身会创建一个TestApi对象,并立即将其丢弃。 aiohttp抱怨该会话从未关闭过(退出async with块或显式调用close()),但即使没有警告,也不要不分配将API对象指向实际使用该变量的变量。

要重用该会话,您的代码需要有权访问该会话或拥有它的对象:

async def fetch(url):
    async with aiohttp.request('GET', url) as resp:
        resp.raise_for_status()
        return await resp.read()

async def main():
    url1_data, url2_data = asyncio.gather(
        fetch('http://url1', fetch('http://url2'))
    url3_data, url4_data = asyncio.gather(
        fetch('http://url3', fetch('http://url4'))

一种选择是将session参数添加到fetch(和其他函数)中,并用在main()中创建的会话一致地调用它。更好的选择是创建一个API类,并将fetch之类的全局函数转换为方法:

class Http:
    async def __aenter__(self):
        self._session = aiohttp.ClientSession()
        return self

    async def __aexit__(self, *err):
        await self._session.close()
        self._session = None

    async def fetch(self, url):
        async with self._session.get(url) as resp:
            resp.raise_for_status()
            return await resp.read()

main()仍然可以作为一个函数存在,但是可以一致地使用持有会话的对象:

async def main():
    async with Http() as http:
        url1_data, url2_data = await asyncio.gather(
            http.fetch('http://url1', http.fetch('http://url2'))
        url3_data, url4_data = await asyncio.gather(
            http.fetch('http://url3', http.fetch('http://url4'))

在上面的代码中,async with语句用于确保只要保留作用域就关闭会话。

答案 1 :(得分:0)

实际上,我看不到您的代码有什么根本错误。

除了,当TestApi对象被破坏时(可能在程序完成时),您将需要调用 close 函数。否则,您会收到此警告。

(这是一个协程,需要等待:https://docs.aiohttp.org/en/stable/client_reference.html