【问题标题】:Combing concurrent.future.as_complete() with dictionary using zip()使用 zip() 将 concurrent.future.as_complete() 与字典结合起来
【发布时间】:2022-01-06 13:42:22
【问题描述】:

我是第一次使用 concurrent.futures 并遵循官方指南。

问题:在 as_completed() 块中,如何访问 future_to_url 中的 k、v?

k 变量至关重要。

使用类似的东西:

for (future, k,v) in zip(concurrent.futures.as_completed(future_to_url), urls.items()):

我偶然发现了this post,但是我无法破译重现的语法

原创

def start():
    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        future_to_url = {executor.submit(visit_url, v): v for k, v in urls.items()}
        for future in concurrent.futures.as_completed(future_to_url):
            data = future.result()
            json = data.json()
            print(f"k: {future[k]}")

第二次尝试 - 使用破坏的 zip

def start():
    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        future_to_url = {executor.submit(visit_url, v): v for k, v in urls.items()}
        for (future, k, v) in zip(concurrent.futures.as_completed(future_to_url), urls.items()):
            data = future.result()
            json = data.json()
            print(f"k: {k}")

第三次失败尝试 - 使用地图 source

for future, (k, v) in map(concurrent.futures.as_completed(future_to_url), scraping_robot_urls.items()):

TypeError: 'generator' 对象不可调用

Fourth Broken Attempt - 在 as_completed() 循环之前存储 k,v 对并将它们与枚举索引配对

    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        future_to_url = {executor.submit(get_response, v): v for k, v in scraping_robot_urls.items()}
        info = {k: v for k, v in scraping_robot_urls.items()}
        for i, future in enumerate(concurrent.futures.as_completed(future_to_url)):
            url = future_to_url[future]
            data = future.result()
            print(f"data: {data}")
            print(f"key: {list(info)[i]} / url: {url}")

这不能作为 URL 工作,与密钥不匹配,它们似乎不匹配,我不能依赖这种行为工作。

为了完整起见,这里是依赖项

def visit_url(url):
    return requests.get(url)

urls = {
  'id123': 'www.google.com', 
  'id456': 'www.bing.com', 
  'id789': 'www.yahoo.com'
}

灵感来源:

【问题讨论】:

  • 洞察力是,使用 executor.submit 生成的未来作为保存引用的字典的键。然后concurrent.futures.as_completed(对dict.keys()进行操作)

标签: python


【解决方案1】:

这与期货无关,更多地与列表理解有关。

    future_to_url = {executor.submit(visit_url, v): v for k, v in urls.items()}

正在循环 urls dict 中的所有内容并获取 key 和 value(k, v) 并将其提交给执行程序以运行 visit_url。 k 和 v 在 for 循环之外将不可用,因为这些变量的范围属于 for 循环。

如果您想获得调用的结果以及调用它的 URL,您可以将 URL 作为返回元组传回:

from tornado import concurrent


def start():
    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        future_to_url = {executor.submit(visit_url, k, v): v for k, v in urls.items()}
        for future in concurrent.futures.as_completed(future_to_url):
            id, data = future.result()
            json = data.json()
            print(f"id: {id}")
            print(f"data: {json}")

def visit_url(id, url):
    return id, requests.get(url)

urls = {
  'id123': 'www.google.com',
  'id456': 'www.bing.com',
  'id789': 'www.yahoo.com'
}

在 OP 制作 cmets 之后(主要是通过使用 visit_url 函数的范围在 exec 之后将上下文/键传回,这看起来很脏)我可以提出一种更 OOP 的方式来执行此操作:

import requests
from tornado import concurrent

class URL:
    def __init__(self, id, url):
        self.id = id
        self.url = url
        self.response = None

    def vist(self):
        self.response = requests.get(self.url)
        return self

def start():
    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        future_to_url = {executor.submit(c.vist): c for c in urls}
        for future in concurrent.futures.as_completed(future_to_url):
            data = future.result()
            print(f"response: {data.response}")
            print(f"id: {data.id}")

urls = [
  URL('id123', 'http://www.google.com'),
  URL('id456', 'http://www.bing.com'),
  URL('id789', 'http://www.yahoo.com')
]

start()

这可以确保响应、ID 和 URL 在它们的类中一起出现,这对某些人来说可能更清晰。提交给执行器的 for 循环也被简化了。

【讨论】:

    【解决方案2】:

    对于后人,我受到了 testfile 响应的启发。

    我通过在 visit_url() 函数中隐藏 k 解决了这个问题。

    def visit_url(url, k):
        return k, requests.get(url)
    

    我现在可以在 as_completed() 循环中访问密钥。这是可预测的,因为密钥和 URL 将匹配。与绑定外部循环不同,在 as_completed() 循环内绑定一个。由于外部请求以随机顺序解析,因此行为异常。

        with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
            future_to_url = {executor.submit(visit_url, v, k): v for k, v in scraping_robot_urls.items()}
            for future in concurrent.futures.as_completed(future_to_url):
                url = future_to_url[future]
                key, data = future.result()
                print(f"key: {key} / url: {url}")
    

    这个解决方案对我来说就像一个 hack,因为我正在使用另一个函数的范围将“状态/变量”传递给其他东西。

    我将悬赏这个问题,因为我希望学习如何更好地处理这种情况并自我教育。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 2018-09-30
      相关资源
      最近更新 更多