Fluent Python 第17章 futuresを使った並行処理 (Python 3.7版)

<article class="single">
  <header class="article">
    <h1>Fluent Python 第17章</h1>
  </header>
  <div class="content">
<p>「Fluent Python 17章 futuresを使った並行処理」の前半部分をPython 3.7で実装します。 <a href="https://github.com/fluentpython/example-code">公式のHP</a>にもPython 3.7用のアップデートがあります。</p>
<h2><span class="pheasant-header"><span class="header"><span class="number">1</span> <span class="title">共通項目</span></span></span></h2>
<div class="pheasant-header"><div class="other"><p class="caption"><span class="prefix">例</span> <span class="number">17.1</span>
<span class="title">共通項目</span></p>
<div class="content">
<div class="pheasant-fenced-code"><div class="input"><pre><code class="python">import asyncio
import sys
import time
from concurrent import futures

import aiohttp
import nest_asyncio
import requests

nest_asyncio.apply()

POP20_CC = (&#34;CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR&#34;).split()

BASE_URL = &#34;http://flupy.org/data/flags&#34;

def get_flag(cc):
    url = &#34;{}/{cc}/{cc}.gif&#34;.format(BASE_URL, cc=cc.lower())
    resp = requests.get(url)
    return resp.content

def show(text):
    sys.stdout.write(text + &#34; &#34;)
    sys.stdout.flush()

def main(func):
    t0 = time.time()
    count = func(POP20_CC)
    elapsed = time.time() - t0
    msg = &#34;\n{} flags downloaded in {:.2f}s&#34;
    print(msg.format(count, elapsed))</code></pre>
</div></div>

</div></div></div>

<h2><span class="pheasant-header"><span class="header"><span class="number">2</span> <span class="title">逐次型ダウンロードスクリプト</span></span></span></h2>
<div class="pheasant-header"><div class="other"><p class="caption"><span class="prefix">例</span> <span class="number">17.2</span>
<span class="title">flags.py</span></p>
<div class="content">
<div class="pheasant-fenced-code"><div class="input"><pre><code class="python">def download_many(cc_list):
    for cc in sorted(cc_list):
        get_flag(cc)
        show(cc)

    return len(cc_list)

if __name__ == &#34;__main__&#34;:
    main(download_many)</code></pre>
</div><div class="stdout"><pre><code class="text">BD BR CD CN DE EG ET FR ID IN IR JP MX NG PH PK RU TR US VN 
20 flags downloaded in 0.43s</code></pre></div></div>

</div></div></div>

<h2><span class="pheasant-header"><span class="header"><span class="number">3</span> <span class="title">concurrent.futuresを使ったダウンロードスクリプト</span></span></span></h2>
<div class="pheasant-header"><div class="other"><p class="caption"><span class="prefix">例</span> <span class="number">17.3</span>
<span class="title">flags_threadpool.py</span></p>
<div class="content">
<div class="pheasant-fenced-code"><div class="input"><pre><code class="python">MAX_WORKERS = 20

def download_one(cc):
    get_flag(cc)
    show(cc)
    return cc

def download_many_threadpool(cc_list):
    workers = min(MAX_WORKERS, len(cc_list))
    with futures.ThreadPoolExecutor(workers) as executor:
        res = executor.map(download_one, sorted(cc_list))

    return len(list(res))

if __name__ == &#34;__main__&#34;:
    main(download_many_threadpool)</code></pre>
</div><div class="stdout"><pre><code class="text">BD BR DE CN CD ET FR ID EG IN IR JP MX PK RU US NG PH TR VN 
20 flags downloaded in 0.13s</code></pre></div></div>

</div></div></div>

<div class="pheasant-header"><div class="other"><p class="caption"><span class="prefix">例</span> <span class="number">17.4</span>
<span class="title">flags_threadpool_ac.py</span></p>
<div class="content">
<div class="pheasant-fenced-code"><div class="input"><pre><code class="python">def download_many_threadpool_ac(cc_list):
    cc_list = cc_list[:5]
    with futures.ThreadPoolExecutor(max_workers=3) as executor:
        to_do = []
        for cc in sorted(cc_list):
            future = executor.submit(download_one, cc)
            to_do.append(future)
            msg = &#34;Scheduled for {}: {}&#34;
            print(msg.format(cc, future))

        results = []
        for future in futures.as_completed(to_do):
            res = future.result()
            msg = &#34;{} result: {!r}&#34;
            print(msg.format(future, res))
            results.append(res)

    return len(results)

if __name__ == &#34;__main__&#34;:
    main(download_many_threadpool_ac)</code></pre>
</div><div class="stdout"><pre><code class="text">Scheduled for BR: &lt;Future at 0x272fbbc6208 state=running&gt;
Scheduled for CN: &lt;Future at 0x272fbbc6668 state=running&gt;
Scheduled for ID: &lt;Future at 0x272fbbbbb38 state=running&gt;
Scheduled for IN: &lt;Future at 0x272fbba0358 state=pending&gt;
Scheduled for US: &lt;Future at 0x272fbba00f0 state=pending&gt;
CN BR ID &lt;Future at 0x272fbbc6208 state=finished returned str&gt; result: &#39;BR&#39;
&lt;Future at 0x272fbbbbb38 state=finished returned str&gt; result: &#39;ID&#39;
&lt;Future at 0x272fbbc6668 state=finished returned str&gt; result: &#39;CN&#39;
IN &lt;Future at 0x272fbba0358 state=finished returned str&gt; result: &#39;IN&#39;
US &lt;Future at 0x272fbba00f0 state=finished returned str&gt; result: &#39;US&#39;

5 flags downloaded in 0.07s</code></pre></div></div>

</div></div></div>

<h2><span class="pheasant-header"><span class="header"><span class="number">4</span> <span class="title">asyncio/aiohttpを使ったダウンロードスクリプト</span></span></span></h2>
<div class="pheasant-header"><div class="other"><p class="caption"><span class="prefix">例</span> <span class="number">17.5</span>
<span class="title">flags_asyncio.py</span></p>
<div class="content">
<div class="pheasant-fenced-code"><div class="input"><pre><code class="python">async def get_flag_async(session, cc):
    url = &#34;{}/{cc}/{cc}.gif&#34;.format(BASE_URL, cc=cc.lower())
    async with session.get(url) as resp:
        return await resp.read()

async def download_one_async(session, cc):
    await get_flag_async(session, cc)
    show(cc)
    return cc

async def download_many_async(cc_list):
    async with aiohttp.ClientSession() as session:
        aws = [
            asyncio.create_task(download_one_async(session, cc))
            for cc in sorted(cc_list)
        ]
        res = await asyncio.gather(*aws)

    return len(res)

if __name__ == &#34;__main__&#34;:
    main(lambda x: asyncio.run(download_many_async(x)))</code></pre>
</div><div class="stdout"><pre><code class="text">DE BD BR NG ID RU EG MX IN TR CN CD ET IR PH JP US VN PK FR 
20 flags downloaded in 0.06s</code></pre></div></div>

</div></div></div></div>



</article>