微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

python中可迭代对象的有效重用 警告

如何解决python中可迭代对象的有效重用 警告

假设我有一个返回 100 个值的迭代器。在某些不可预测的时刻,值从一个类别切换到另一个类别(在这种情况下,ints 到 strs),并且执行的操作也需要更改(更新变量以打印字符数)。

我猜最基本的示例代码是这样的:

query(query:string):string {
        var response = "No response ...";
        var sendrequest = (query:string):Promise<string> => {
            return new Promise( (resolve,reject) => {
                this.connection.query(query,(err:string,result:string) => {
                    if (err) { reject(err); } else { resolve(result); }
                });
            });
        }

        sendrequest(query).then( (result:string) => {
            console.log("Résultat :" + result);
            return result;
        });
        return response;
    }

这看起来很有效,但 if 语句似乎也浪费在最后 59 或 60 个条目上,因为我们可以确定项目的类型不会恢复为 int。

这里有一个变体,其中使用了两个不同的套件,因此最后一个条目不需要 def perform_operations(iterable): most_recent_int = None for item in iterable: if type(item) is int: most_recent_int = item else: print(f"int of {len(item)} characters") return most_recent_int iterable = list(range(100,140)) + [str(i) for i in range(60)] print(f"most recent int: {perform_operations(iterable)}") 语句:

if

这看起来应该更有效率。

我能想到的唯一可能执行更少的操作并且可能被更快地解释的是:

def perform_operations(iterable):
    most_recent_int = None
    for item in (iterator := iter(iterable)):
        if type(item) is int:
            most_recent_int = item
        else:
            print(f"int of {len(item)} characters")
            break
    for remaining_str_item in iterator:
        print(f"int of {len(remaining_str_item)} characters")
    return most_recent_int

iterable = list(range(100,140)) + [str(i) for i in range(60)]
print(f"most recent int: {perform_operations(iterable)}")

或者:

def perform_operations(iterable):
    most_recent_int = None
    for item in (iterator := iter(iterable)):
        if type(item) is int:
            most_recent_int = item
        else:
            break
    while True:
        print(f"int of {len(item)} characters")
        try:
            item = next(iterator)
        except stopiteration:
            break
    return most_recent_int

iterable = list(range(100,140)) + [str(i) for i in range(60)]
print(f"most recent int: {perform_operations(iterable)}")

对最有效的解决方案感兴趣,但也对惯用和先例的解决方案感兴趣。

解决方法

在示例情况下,条件类型检查非常快,完全没有必要使用更复杂的解决方案。

但是,假设您有一个迭代器,它昂贵区分XY这两个类别,在这种情况下,最有效的解决方案是尝试最少的歧视。假设:

  1. 我们不受内存限制,可以将可迭代对象冻结为 list L;和
  2. 我们有一个谓词 is_y,如果值在类别 True 中则返回 Y,否则返回 False

那么您可以将冻结的 L 视为p 下排序的列表。在这种情况下,确定类别在哪里从 X 变为 Y 的问题简化为二分搜索。

所以......在即将到来的 Python 3.10 中,bisect.bisect 最终获得了一个 key 参数:

# freeze the iterable
frozen = list(iterable)

# find the position such that frozen[:pos] are all category X
pos = bisect.bisect_left(frozen,key=is_y)

# then proceed to deal with each half as you wish
for x in frozen[:pos]:
    do_x(x)

for y in frozen[pos:]:
    do_y(y)

在 Python 3.10 之前,bisect 不支持 key 参数。在这种情况下,您可以简单地将每个对象包装起来以支持 __lt__,其中该实现只计算包装对象上的反向谓词。

class wrapper:
    def __init__(self,underlying):
        self.underlying = underlying

    def __lt__(self,other):
        return not is_y(self.underlying)

如果您内存受限,例如迭代可能非常大,那么这种方法仍然可以迭代使用:

  • 读入固定大小的缓冲区;
  • 尝试对该缓冲区执行 bisect_left,如果返回值为 len(buffer),则您尚未到达类别切换点。

警告

这种情况在实践中似乎极不可能,几乎总是通过 iftry: ... except ... 进行类别歧视的成本很低,并且担心它肯定会过早地优化代码以使其不可读且难以推理约。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。