【问题标题】:Python list comprehension - access last created element?Python列表理解 - 访问最后创建的元素?
【发布时间】:2009-04-27 18:46:27
【问题描述】:

是否可以访问在列表推导中生成的前一个元素。

我正在研究一些玩具加密的东西。给定作为任意大整数的密钥、一个初始化值和一个元素列表作为要加密的消息。我需要将每个元素与前一个加密元素和密钥进行异或。下面的循环就可以了。

previous = initialization_value
cipher = []
for element in message:
    previous = element ^ previous ^ key
    cipher.append(previous)

我觉得应该可以将其转换为列表理解,但我不确定如何处理初始值或访问先前生成的值。 有可能吗?如果有的话,理解是什么?

【问题讨论】:

    标签: python


    【解决方案1】:

    没有一个好的 Pythonic 方法可以通过列表理解来做到这一点。考虑列表推导的最佳方式是替代mapfilter。换句话说,只要你需要一个列表,你就可以使用列表推导式

    • 将其元素用作某些表达式的输入(例如,对元素进行平方)

    • 根据某些条件删除它的一些元素

    这些东西的共同点是它们每次只查看一个列表元素。这是一个很好的经验法则;即使理论上您可以将显示的代码编写为列表推导式,也将是笨拙且不符合 Python 的。

    【讨论】:

    • +1:这就是为什么我们仍然有 for 语句的原因——对于与这个问题完全一样的情况。
    • 为此获得本机功能会很方便。数据库和以数据为中心的语言(如 SAS)有一个 lag 关键字。问题中的编码不必要地冗长恕我直言,并且没有一个很好的pythonic解决方案。有趣的是,我没有发现此功能的 PEP
    【解决方案2】:

    应该是可以的;见The Secret Name of List Comprehensions。不过,这不是pythonic。

    【讨论】:

    • 似乎不再适用于 Python 3。
    【解决方案3】:

    作为生成器:

    def cypher(message, key, seed):
        for element in message:
            seed = element ^ seed ^ key
            yield seed
    
    list(cypher(message, key, initial_seed))
    

    【讨论】:

    • 我喜欢这个解决方案,即使它不是 OP 要求的。
    【解决方案4】:

    您可以使用reduce() 完成此操作。这不是列表推导,而是函数式风格方法:

    cipher = []
    def f(previous, element):
        previous = element ^ previous ^ key
        cipher.append(previous)
        return previous
    reduce(f, message, initialization_value)
    

    在这种情况下,它并不比普通循环更漂亮。

    【讨论】:

    • 使用reduce前检查性能;它通常会导致结构效率非常低。
    • “for 循环”版本更加更简洁,因此仅将此答案视为“理论上可以做到的其他方式”。
    【解决方案5】:

    这是一个示例,如何使用枚举访问列表推导中最后和下一个创建的元素。

    上一个值...

    这里-1是偏移量,None是默认值。

    A = ['A','B','C','D','E','F','G','H','I','J']
    [(A[i-1] if (i-1) >= 0 else None,x) for i,x in enumerate(A)
    

    输出为元组(上一个,当前)

    [(无,'A'), ('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E'), ('E', 'F'), ('F','G'), ('G','H'), ('你好'), ('I', 'J')]

    下一个值...

    A = ['A','B','C','D','E','F','G','H','I','J']
    [(x, A[i+1] if (i+1) < len(A) else None) for i,x in enumerate(A)]
    

    输出为元组(当前,下一个)

    [('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E'), ('E', 'F'), ('F','G'), ('G','H'), ('你好'), ('我','J'), ('J', 无)]

    这和sql中的lag()和lead()解析函数非常相似。

    【讨论】:

      【解决方案6】:

      您可以在迭代序列时使用辅助对象来存储所有内部状态:

      class Encryption:
        def __init__(self, key, init_value):
          self.key = key
          self.previous = init_value
        def next(self, element):
          self.previous = element ^ self.previous ^ self.key
          return self.previous
      
      enc = Encryption(...)
      cipher = [enc.next(e) for e in message]
      

      话虽如此,将先前加密的元素添加到异或中并不会使您的算法更难破解,而不仅仅是使用密钥对每个元素进行异或。攻击者可以将密文中的任何字符与之前的加密字符进行异或运算,从而取消加密期间完成的异或运算。

      【讨论】:

        【解决方案7】:

        我更喜欢使用类似 enumerate 生成器的东西

        def emit_previous(iterable, initial=None):
            previous = initial
            for item in iterable:
                yield previous, item
                previous = item
        
        cipher = []
        for previous, element in emit_previous(message, initial=initialization_value):
            seed = element ^ previous ^ key
            cipher.append(seed)
        

        【讨论】:

          【解决方案8】:

          因此,您可以追加到列表推导中的列表,并使用最后添加的元素作为该列表推导的新元素,然后最终将该推导分配给列表变量。

          message = [0,1,2,3,4,5,6,7]
          key = 8
          previous = 9
          
          cipher = [previous]
          
          cipher = [(cipher.append(element^cipher[-1]^key), cipher[-1])[1] for element in message]
          
          print(cipher)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-09-12
            • 2017-08-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多