[英]Turn python code into a generator function
如何將此代碼轉換為生成器 function? 或者我可以通過其他方式避免將所有數據讀入 memory 嗎? 現在的問題是我的 memory 已滿。 執行代碼很長一段時間后我被殺死了。
代碼:
data = [3,4,3,1,2]
def convert(data):
for index in range(len(data)):
if data[index] == 0:
data[index] = 6
data.append(8)
elif data[index] == 1:
data[index] = 0
elif data[index] == 2:
data[index] = 1
elif data[index] == 3:
data[index] = 2
elif data[index] == 4:
data[index] = 3
elif data[index] == 5:
data[index] = 4
elif data[index] == 6:
data[index] = 5
elif data[index] == 7:
data[index] = 6
elif data[index] == 8:
data[index] = 7
return data
for i in range(256):
output = convert(data)
print(len(output))
Output:
266396864
290566743
316430103
346477329
376199930
412595447
447983143
490587171
534155549
582826967
637044072
692630033
759072776
824183073
903182618
982138692
1073414138
1171199621
1275457000
1396116848
1516813106
Killed
要回答這個問題:要將 function 變成生成器 function,您所要做的就是yield
一些東西。 你可以這樣做:
def convert(data):
for index in range(len(data)):
...
yield data
然后,您可以像這樣遍歷 output:
iter_converted_datas = convert(data)
for _, converted in zip(range(256), iter_converted_datas):
print(len(converted))
我還建議對此代碼進行一些改進。
首先,如果您打算遍歷整個列表長度,請使用 enumerate:
for _, converted in enumerate(iter_converted_datas):
接下來要做的是擺脫所有這些 elif 語句。
一個有用的事情可能是為您的生成器 function 提供一個字典參數,告訴它如何轉換數據值(第一個是特殊情況,因為它也附加)。
這是該 dict 的樣子:
replacement_dict = {
0: 6,
1: 0,
2: 1,
3: 2,
4: 3,
5: 4,
6: 5,
7: 6,
8: 7,
}
順便說一句:用字典替換一系列 elif 語句是 python 中非常典型的事情。 它並不總是合適的,但通常效果很好。
現在你可以像這樣編寫你的生成器:
def convert(data, replacement_dict):
for index in range(len(data)):
if index==0:
lst.append(8)
data[index] = replacement_dict[index]
yield data
並像這樣使用它:
iter_converted_datas = convert(data, replacement_dict)
for _, converted in enumerate(iter_converted_datas):
print(len(converted))
現在,您可能有多個要對其進行其他修改的索引。 您可以將它們拆分為具有不同目的的多個字典: replacement_dict
和modification_dict
。
修改字典看起來像這樣(它現在只有條目,對於索引 0):
modification_dict = {
0: lambda data: data.append(8)
}
現在,我們將修改生成器 function 以同時接受 replacement_dict 和 modify_dict:
def convert(data, replacement_dict, modification_dict):
for index in range(len(data)):
# this modifies the data
try:
modification_dict[index](data)
except KeyError:
pass
# this replaces the data
data[index] = replacement_dict[index]
yield data
現在,像這樣使用它:
iter_converted_datas = convert(data, replacement_dict, modification_dict)
for _, converted in enumerate(iter_converted_datas):
print(len(converted))
但我們需要退后一步:您的 memory 被填滿的原因是您創建了一個增長非常快的例程。 而且,如果您繼續超過 256 次迭代,那么列表會變得更長而沒有盡頭。
如果您想迭代列表的第 X 次迭代而不將整個列表存儲到 memory 中,則必須進行相當多的更改。
我對如何開始的建議:創建一個 function 以獲得任何起始輸入值的第 X 次迭代。
這是一個僅根據替換字典生成輸出的生成器。 根據替換字典的內容,這可能是無限的,或者它可能有一個結束(在這種情況下它會引發KeyError
)。
def process_replacements(index, replacement_dict):
while True:
yield (index := replacement_dict[index])
接下來我們可以編寫 function 來處理第 X 次迭代以獲得起始值:
def process_xth(index, xth, replacement_dict):
# emit the xth value from the original value
for _ in range(xth):
index = process_replacements(index, replacement_dict)
return index
現在您可以處理起始數據列表中任何值的第 X 次迭代:
index = 0
xth = 256
process_xth(data[index], xth, data, replacement_dict)
但是,每當我們遇到 0 值時,我們都沒有附加到data
列表中。 我們可以這樣做,但正如您所發現的,最終 8 的列表會變得太大。 相反,我們需要做的是保持 COUNT 我們在末尾添加了多少個 8。
您可以采取以下措施對其進行優化:
您可以使用enumerate(data)
而不是range(len(data))
) 。 這使您可以訪問元素及其索引。 例子:
編輯:根據這篇文章, range
比enumerate
快。 如果您關心速度,則可以忽略此更改。
for index, element in enumerate(data):
if element == 0:
data[index] = 6
其次,大多數if
語句具有可預測的模式。 所以你可以像這樣重寫它們:
def convert(data):
for idx, elem in enumerate(data):
if elem == 0:
data[idx] = 6
data.append(8)
if elem <= 8:
data[index] = elem - 1
由於list
是可變的,因此您不需要返回data
。 它就地修改它。
我看到您詢問生成器功能,但這並不能解決您的 memory 問題。 你用完了 memory 因為,好吧,你把所有東西都保存在 memory 中......
您的解決方案的 memory 復雜度為 O*((8/7)^n) 其中 n 是要轉換的調用次數。 這是因為每次調用 convert() 時,數據結構都會擴展 1/7 的元素(平均而言)。 之所以如此,是因為結構中的每個數字都有 1/7 的概率為零。
所以 memory 復雜度是 O*((8/7)^n),因此是指數的。 但我們能做得更好嗎?
我們可以。 我們可以在 memory 中只保留調用 convert() 時結構中存在的零的數量。 這樣,我們將有一個線性的 memory 復雜度 O*(n)。 這需要付出代價嗎?
是的。 元素訪問時間不再具有恆定的復雜度 O(1),但它具有線性復雜度 O(n),其中 n 是對 convert() 的調用次數(至少這是我想出的)。 但它解決了內存不足的問題。
這是一個代碼:
from copy import deepcopy # to keep original list untouched ;)
class Data:
def __init__(self, seed):
self.seed = deepcopy(seed)
self.iteration = 0
self.zero_counts = list()
self.len = len(seed)
def __len__(self):
return self.len
def __iter__(self):
return SeededDataIterator(self)
def __repr__(self):
return "[" + (", ".join(f"{n}" for n in self)) + "]"
def __getitem__(self, index: int):
if index >= self.len:
raise IndexError
if index < len(self.seed):
ret = self.seed[index] - self.iteration
else:
inner_it_idx = index - len(self.seed)
for i, cnt in enumerate(self.zero_counts):
if inner_it_idx < cnt:
ret = 9 + i - self.iteration
break
else:
inner_it_idx -= cnt
ret = ret if ret > 6 else ret % 7
return ret
def convert(self):
zero_count = sum((self[i] == 0) for i, _ in enumerate(self.seed))
for i, count in enumerate(self.zero_counts):
i = 9 + i - self.iteration
i = i if i > 6 else i % 7
if i == 0:
zero_count += count
self.zero_counts.append(zero_count)
self.len += self.zero_counts[self.iteration]
self.iteration += 1
class DataIterator:
"""Iterator class for the Data class"""
def __init__(self, seed_data):
self.seed_data = seed_data
self.index = 0
def __next__(self):
if self.index >= self.seed_data.len:
raise StopIteration
ret = self.seed_data[self.index]
self.index += 1
return ret
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.