2016-11-09 58 views
1

我有一個JSON文件,並且想寫一個函數來返回文件中接下來的10個對象的列表。我從一個類FileProcessor和方法get_row()開始,它返回一個生成器,該生成器從文件中生成一個JSON對象。另一種方法get_chunk()應返回接下來的10個對象。從文件生成塊

這是我到目前爲止有:

class FileProcessor(object): 

    def __init__(self, filename): 
     self.FILENAME = filename 

    def get_row(self): 
     with open(os.path.join('path/to/file', self.FILENAME), 'r') as f: 
      for i in f: 
       yield json.loads(i) 

    def get_chunk(self): 
     pass 

我已經試過這樣的,但每次它只返回第10個行。

def get_chunk(self): 
     chunk = [] 
     consumer = self.consume() 
     for i in self.get_row(): 
      chunk.append(i) 
     return chunk  

那麼寫get_chunk()的正確方法是什麼?

+0

您確定標準庫中包含的json解析器不支持增量加載嗎?或者不能擴展到這樣做? – SwiftsNamesake

+0

'FileProcessor.get_row'方法是否正常工作? IOW,是文本文件中的每一行_guaranteed_是一個完整的JSON對象嗎? –

+0

@ PM2Ring是的,它返回一個完整的JSON對象 –

回答

2

這是一個簡單的生成器,它從另一個生成器獲取值並將它們放入列表中。它應該與你的FileProcessor.get_row方法一起工作。

def count(n): 
    for v in range(n): 
     yield str(v) 

def chunks(it, n): 
    while True: 
     yield [next(it) for _ in range(n)] 

for u in chunks(count(100), 12): 
    print(u) 

輸出

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'] 
['12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'] 
['24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35'] 
['36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47'] 
['48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59'] 
['60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71'] 
['72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83'] 
['84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95'] 

注意,這僅產生完整的塊。如果這是一個問題,你可以這樣做:

def chunks(it, n): 
    while True: 
     chunk = [] 
     for _ in range(n): 
      try: 
       chunk.append(next(it)) 
      except StopIteration: 
       yield chunk 
       return 
     yield chunk 

將前面的輸出後打印

['96', '97', '98', '99'] 


一個更好的辦法來做到這一點是使用itertools.islice,將處理的部分最終塊:

from itertools import islice 

def chunks(it, n): 
    while True: 
     a = list(islice(it, n)) 
     if not a: 
      return 
     yield a 

感謝Antti Haapala提醒我關於islice。 :)

+0

我正在忙着寫我的一個答案,你打敗了我!我使用'map'來創建生成器,所以我想我會發布以顯示更多選項。但我喜歡你的答案。 – tdelaney

2

(注:PM 2Ring打我吧!)

get_row方法不返回行 - 它返回一個發電機,你遍歷它會產生行。你可以看到在的get_chunk方法中。煩人的是,每次你撥打get_row時,它會再次打開文件並返回第一個對象。 get_chunk的問題在於,您不會傳遞所需的行數,並且不會將for循環限制爲該數字。 get_chunk獲取文件中的所有行。

重新思考怎麼樣?你真正需要的是一個讀取行並對json進行反序列化的生成器。 map函數已經構建完成。你可以用python的next函數獲取單行,並且可以用itertools.islice獲得多行。你的課只是一個已經實現的東西的簡單包裝,所以只需使用本地工具,並跳過完全寫你自己的類。

拳我生成測試文件

>>> with open('test.json', 'w') as fp: 
...  for obj in 'foo', 'bar', 'baz': 
...   fp.write(json.dumps(obj) + '\n') 

...

現在我可以創建可用於獲取行的行或列表的迭代器。在cpython中,您可以安全地打開map函數中的文件,但您也可以在with子句中執行您的工作。

>>> json_iter = map(json.loads, open('test.json')) 
>>> next(json_iter) 
'foo' 
>>> 
>>> with open('test.json') as fp: 
>>>  json_iter = map(json.loads, open('test.json')) 
>>>  next(json_iter) 
'foo' 

我可以得到所有的物體在一個循環

>>> for obj in map(json.loads, open('test.json')): 
...  print(obj) 
... 
foo 
bar 
baz 

或者把其中的一些在列表

>>> list(itertools.islice(json_iter, 2)) 
['foo', 'bar'] 

或合併操作

>>> json_iter = map(json.loads, open('test.json')) 
>>> for obj in json_iter: 
...  if obj == 'foo': 
...   list(itertools.islice(json_iter,2)) 
... 
['bar', 'baz'] 

的點是,簡單的map基地d迭代器可以做你想做的,而不必每次想到新的用例都更新一個包裝類。

+0

感謝您幫助我解決這個問題!我認爲地圖功能非常好,我很高興瞭解它,但我想知道是否可以使用大型數據集。我們的JSON文件平均爲2-3 GB,每個文件中的任何1k和1mm對象之間都有。在對這些文件中的某些文件測試map函數時,它似乎很慢,我想是因爲它處理整個數據集 - 是正確的嗎? –

+0

@JoeFusaro - 我假設你使用python 3.x,否則它會被炸燬(你需要在2.x上使用'itertools.imap')。只要你像我所顯示的一樣使用文件(沒有對文件執行'read'或'readlines'這樣的操作),對於每次迭代它從文件中讀取1行,反序列化該行並返回python對象。如果json對象本身很大,這將需要一些時間讀取數據塊,掃描一個新行,建立一個新的行字符串,然後解碼成python對象。但是,一次只能創建一個json對象。 – tdelaney

+0

啊,我使用2.7,我應該指定 - 對此感到抱歉。將嘗試imap功能。謝謝! –