首页 > 后端开发 > Python教程 > Python 化 JavaScript

Python 化 JavaScript

Mary-Kate Olsen
发布: 2025-01-14 22:19:48
原创
992 人浏览过

Pythonizing JavaScript

Python 拥有许多强大的实用函数,例如 rangeenumeratezip 等,这些函数基于可迭代对象和迭代器协议构建。结合生成器函数,这些协议自 2016 年左右起就在所有 Evergreen 浏览器和 Node.js 中可用,但在我看来,它们的使用率却低得令人吃惊。在这篇文章中,我将使用 TypeScript 实现其中一些辅助函数,希望能改变这种现状。

迭代器、可迭代对象和生成器函数

迭代器协议

迭代器协议是一种生成值序列的标准方法。要使一个对象成为迭代器,它必须通过实现 next 方法来遵守迭代器协议,例如:

<code class="language-typescript">const iterator = {
  i: 0,
  next() {
    return { done: false, value: this.i++ };
  }
};</code>
登录后复制
登录后复制

然后,我们可以重复调用 next 方法来获取值:

<code class="language-typescript">console.log(iterator.next().value); // → 0
console.log(iterator.next().value); // → 1
console.log(iterator.next().value); // → 2
console.log(iterator.next().value); // → 3
console.log(iterator.next().value); // → 4</code>
登录后复制
登录后复制

next 方法应该返回一个对象,该对象包含一个 value 属性(包含实际值)和一个 done 属性(指定迭代器是否已耗尽,即是否无法再生成值)。根据 MDN 的说法,这两个属性都不是严格必需的,如果两者都缺失,则返回值被视为 { done: false, value: undefined }

可迭代对象协议

可迭代对象协议允许对象定义其自身的迭代行为。要遵守可迭代对象协议,对象必须使用 Symbol.iterator 键定义一个方法,该方法返回一个迭代器。许多内置对象(如 ArrayTypedArraySetMap)都实现了此协议,因此可以使用 for...of 循环对其进行迭代。

例如,对于数组,values 方法被指定为数组的 Symbol.iterator 方法:

<code class="language-typescript">console.log(Array.prototype.values === Array.prototype[Symbol.iterator]); // → true</code>
登录后复制
登录后复制

我们可以结合迭代器和可迭代对象协议来创建一个可迭代的迭代器,如下所示:

<code class="language-typescript">const iterable = {
  i: 0,
  [Symbol.iterator]() {
    const iterable = this;
    return {
      next() {
        return { done: false, value: iterable.i++ };
      }
    };
  }
};</code>
登录后复制
登录后复制

这两个协议的名称不幸地非常相似,至今仍然让我感到困惑。

正如您可能猜到的那样,我们的迭代器和可迭代对象示例是无限的,这意味着它们可以永远生成值。这是一个非常强大的特性,但也容易成为一个陷阱。例如,如果我们要在一个 for...of 循环中使用可迭代对象,则循环将永远持续下去;或者用作 Array.from 的参数,JS 最终会抛出一个 RangeError,因为数组会变得太大:

<code class="language-typescript">// 将无限循环:
for (const value of iterable) {
  console.log(value);
}

// 将抛出 RangeError
const arr = Array.from(iterable);</code>
登录后复制
登录后复制

迭代器和可迭代对象甚至可以无限的原因是它们是惰性求值的,即只有在使用时才会生成值。

生成器函数

虽然迭代器和可迭代对象是宝贵的工具,但编写起来有点麻烦。作为替代方案,引入了生成器函数。

生成器函数使用 function* (或 function *,星号可以在 function 关键字和函数名称之间任意位置)指定,允许我们中断函数的执行,使用 yield 关键字返回值,并在稍后继续中断的地方继续执行,同时保持其内部状态:

<code class="language-typescript">const iterator = {
  i: 0,
  next() {
    return { done: false, value: this.i++ };
  }
};</code>
登录后复制
登录后复制

Python 实用程序

如引言中所述,Python 有一些非常有用的内置实用程序,它们基于上述协议。JavaScript 最近也为迭代器增加了一些辅助方法,例如 .drop().filter(),但(也许还没有)拥有 Python 中一些更有趣的实用程序。

让我们动手实践!

现在理论部分已经结束,让我们开始实现一些 Python 函数吧!

注意:此处显示的这些实现都不应按原样用于生产环境。 它们缺乏错误处理和边界条件检查。

enumerate(iterable [,start])

Python 中的 enumerate 为输入序列或可迭代对象中的每个项目返回一系列元组,其中第一个位置包含计数,第二个位置包含项目:

<code class="language-typescript">console.log(iterator.next().value); // → 0
console.log(iterator.next().value); // → 1
console.log(iterator.next().value); // → 2
console.log(iterator.next().value); // → 3
console.log(iterator.next().value); // → 4</code>
登录后复制
登录后复制

enumerate 还接受一个可选的 start 参数,指示计数器应从何处开始:

<code class="language-typescript">console.log(Array.prototype.values === Array.prototype[Symbol.iterator]); // → true</code>
登录后复制
登录后复制

让我们使用生成器函数在 TypeScript 中实现它。我们可以使用 python 文档中概述的实现作为指导

<code class="language-typescript">const iterable = {
  i: 0,
  [Symbol.iterator]() {
    const iterable = this;
    return {
      next() {
        return { done: false, value: iterable.i++ };
      }
    };
  }
};</code>
登录后复制
登录后复制

由于 JavaScript 中的字符串实现了可迭代对象协议,我们可以简单地将字符串传递给我们的 enumerate 函数并像这样调用它:

<code class="language-typescript">// 将无限循环:
for (const value of iterable) {
  console.log(value);
}

// 将抛出 RangeError
const arr = Array.from(iterable);</code>
登录后复制
登录后复制

repeat(elem [,n])

repeat 是内置 itertools 库的一部分,它重复给定的输入 elem n 次,如果未指定 n,则无限重复。我们再次可以将 python 文档中的实现作为起点。

<code class="language-typescript">function* sequence() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const seq = sequence();
console.log(seq.next().value); // → 0;
console.log(seq.next().value); // → 1;
console.log(seq.next().value); // → 2;

// 将无限循环,从 3 开始
for (const value of seq) {
  console.log(value);
}</code>
登录后复制

(此处省略了 cyclerange 函数的实现,因为篇幅过长,但其逻辑与原文相同,只是将代码用 TypeScript 重写)

结论

这是我的第一篇博客文章,我希望您觉得它有趣,并且也许您会在未来的项目中使用迭代器、可迭代对象和生成器。如果您有任何疑问或需要澄清,请留下评论,我很乐意提供更多信息。

需要注意的是,与使用计数器的原始 for 循环相比,性能相差甚远。这在许多情况下可能无关紧要,但在高性能场景中绝对很重要。当我将 PCM 数据绘制到画布上并使用迭代器和生成器时,发现帧丢失了,这让我很苦恼。事后看来这可能是显而易见的,但当时对我来说却并非如此 :D

干杯!

以上是Python 化 JavaScript的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板