[英]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.