【问题标题】:"Lazy" list item evaluation in PythonPython中的“懒惰”列表项评估
【发布时间】:2018-11-05 23:01:51
【问题描述】:

由于没有更好的词,我就用了这个标题。

我想要的是能够做这样的事情:

>>> from random import randint
>>> fruits = [
...     "Orange",
...     "Banana",
...     f"{randint(2,5)} Cherries",
... ]

>>> fruits[2]
'3 Cherries'

>>> fruits[2]
'5 Cherries'

>>> fruits[2]
'2 Cherries'

但是,字符串中的文字表达式在创建列表时被评估一次,并且每次访问它时都会给出相同的结果。

我想知道除了编写一些复杂的边缘情况处理之外,是否有更简单/更智能的方法来处理这个问题(我们毕竟是程序员;谁不喜欢编写漂亮的代码并且优雅而花哨?)。 我说的是边缘情况处理,因为我的 49 个字符串中只有 6 个需要这种“特殊”行为。

到目前为止,我尝试的是从 randint 调用中创建一个 lambda 函数,但这并没有帮助;同样的结果。 也许这是惰性评估的一个案例,但我需要一些关于如何(或是否?)将它与列表一起使用的指导。

【问题讨论】:

    标签: python lambda lazy-evaluation f-string


    【解决方案1】:

    如果您希望 一些 项是字面的,但有些是可调用的,您可以创建自己的 list 版本:

    from random import randint
    
    class LazyList(list):
        'Like a list, but invokes callables'
        def __getitem__(self, key):
            item = super().__getitem__(key)
            if callable(item):
                item = item()
            return item
    
    fruits = LazyList([
        "Orange",
        "Banana",
        lambda: f"{randint(2,5)} Cherries",
    ])
    
    print(fruits[2])
    print(fruits[2])
    

    【讨论】:

    • 比 o11c 的要好得多.. 您是否需要重载许多其他方法才能使其在迭代、切片、打印为 repr 和 str 等方面表现出色?还是他们都通过__getitem__ 本身?排序呢?
    • @PatrickArtner 不,这几乎可以保证不适用于 CPython 中的内置类型。
    【解决方案2】:

    你在正确的轨道上,想要一个 lambda:

    from random import randint
    
    fruits = [
            lambda: "Orange",
            lambda: "Banana",
            lambda: f"{randint(2, 5)} Cherries",
    ]
    
    print(fruits[2]())
    print(fruits[2]())
    print(fruits[2]())
    

    有一些方法可以省略(),但可能不值得。

    【讨论】:

      【解决方案3】:

      一开始将 lambda 放入列表中听起来不错,但随后您必须调用列表中的这个特殊(或所有)索引。你不能迭代列表,你不能对列表进行排序,...

      创建一个为您提供列表的函数更容易:

      from random import randint
      
      def gimme_fruits ():
          return [ "Orange", "Banana", f"{randint(2,5)} Cherries",]
      
      print(gimme_fruits())
      

      输出:

      ['Orange', 'Banana', '3 Cherries']
      
      ['Orange', 'Banana', '4 Cherries']
      
      ['Orange', 'Banana', '2 Cherries']
      

      每个列表的樱桃数量都是“固定的”——但您可以这样做:

      for f in gimme_fruits() + gimme_fruits() + gimme_fruits():
          print(f)
      

      获取维生素:

      Orange
      Banana
      3 Cherries
      Orange
      Banana
      3 Cherries
      Orange
      Banana
      3 Cherries
      

      【讨论】:

        【解决方案4】:

        免责声明:我是图书馆的作者

        你可以使用seqtools做惰性映射,它支持迭代和切片:

        >>> fruits = seqtools.smap(lambda x: x if isinstance(x, str) else x(), 
        ...                        fruits)
        >>> fruits[0]
        'Orange'
        >>> fruits[-1]
        '3 Cherries'
        

        如果您不想在每次调用时重新评估函数元素,可以添加一些缓存:

        fruits = seqtools.add_cache(fruits)
        

        显然,项目分配不能通过映射进行,因此您将不得不为此欺骗自己。

        尽管排序最终会评估列表中的所有项目,因此您最好提前执行此操作并调用常规 list.sort() 方法。

        【讨论】:

          猜你喜欢
          • 2018-04-01
          • 2019-01-07
          • 2015-09-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-14
          • 2021-12-25
          相关资源
          最近更新 更多