[英]Python list comprehension - access last created element?
是否可以访问列表理解中生成的前一个元素。
我正在研究一些玩具加密的东西。 给定一个任意大的密钥 integer、一个初始化值和一个元素列表作为要加密的消息。 我需要用之前的加密元素和密钥对每个元素进行异或运算。 下面的循环就可以了。
previous = initialization_value
cipher = []
for element in message:
previous = element ^ previous ^ key
cipher.append(previous)
我觉得应该可以将其转化为列表理解,但我不确定如何处理初始值或访问先前生成的值。 这可能吗?如果可能的话,理解是什么?
没有一种好的、Pythonic 的方法可以通过列表理解来做到这一点。 考虑列表推导式的最佳方式是替代map
和filter
。 换句话说,每当您需要获取列表时,您都会使用列表理解
将其元素用作某些表达式的输入(例如对元素进行平方)
根据某些条件删除其某些元素
这些东西的共同点是它们每次只查看一个列表元素。 这是一个很好的经验法则; 即使您理论上可以编写您显示为列表推导式的代码,它也会很笨拙且不符合 Python 风格。
大概可以做到; 请参阅列表推导式的秘密名称。 不过,它不是pythonic。
作为生成器:
def cypher(message, key, seed):
for element in message:
seed = element ^ seed ^ key
yield seed
list(cypher(message, key, initial_seed))
您可以使用reduce()完成此操作。 这不是列表理解,而是函数式风格的方法:
cipher = []
def f(previous, element):
previous = element ^ previous ^ key
cipher.append(previous)
return previous
reduce(f, message, initialization_value)
不过,在这种情况下,它并不比普通循环更漂亮。
这是一个如何使用枚举访问列表理解中的最后一个和下一个创建的元素的示例。
之前的值...
这里 -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'), ('H', 'I'), ('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'), ('H', 'I'), ('I', 'J'), ('J', None)]
这与sql中的lag()和lead()分析函数非常相似。
我更喜欢使用更像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)
您可以在迭代序列时使用辅助对象来存储所有内部状态:
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]
话虽如此,将先前加密的元素添加到异或中并不会使您的算法比仅使用密钥对每个元素进行异或更难破解。 攻击者可以将密文中的任何字符与先前加密的字符进行异或,从而取消加密期间进行的异或。
因此,您可以附加到列表推导式中的列表,并使用最后添加的元素作为该列表推导式的新元素,然后最终将该推导式分配给列表变量。
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)
itertools.accumulate()正是为此而存在的:
from itertools import accumulate
list(accumulate(message, lambda prev, element: prev^element^key), initial=initialization_value)
一个简单的一行,不需要理解或任何复杂的东西。
自 Python 3.8 起,现在支持赋值表达式,可用于实现在迭代中记住最后一个值的列表理解:
previous = initialization_value
cipher = [previous := element ^ previous ^ key for element in message]
如果您更喜欢将所有内容写在一行中,您可以在 singleton 列表中初始化previous
,并使用and
运算符丢弃它以让列表理解接管:
cipher = [previous := initialization_value] and [previous := element ^ previous ^ key for element in message]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.