2016-03-15 3 views
4

Я использую python-docx для управления текстовыми документами. Вот то, что я в настоящее время необходимо изменить текст в обычных пунктах:Python Combine For-Loops

doc = Document('idk.docx') 
for paragraph in doc.paragraphs: 
    if "oldtext1" in paragraph.text: 
     paragraph.replace("oldtext1","Something") 
    if "oldtext2" in paragraph.text: 
     paragraph.replace("oldtext2","Somethingelse") 

Если я хочу, чтобы изменить текст в таблице, мне нужно сделать следующее

tables = doc.tables 
for table in tables: 
    for row in table.rows: 
     for cell in row.cells: 
      for paragraph in cell.paragraphs: 
       if "oldtext1" in paragraph.text: 
        paragraph.replace("oldtext1","Something") 
       if "oldtext2" in paragraph.text: 
        paragraph.replace("oldtext2","Somethingelse") 

код работает отлично и текст заменен, но проблема в том, что я пытаюсь заменить ВСЕ экземпляры текста в документе, и я не хочу иметь 2 отдельных цикла (1 для обычного текста в параграфах и другой для текста в таблицах)

Is есть простой способ комбинировать эти циклы, поэтому мне не нужно иметь ame if-statements в 2 разных циклах?

+0

Вы зацикливание над разными вещами, так что я не вижу ничего плохого в этом коде –

+1

@ cricket_007 Я согласен, хотя я бы рекомендовал положить обработку в-пункт в чтобы избежать повторения кода – DaveBensonPhillips

+0

@HumphreyTriscuit - я собирался сказать это, но это личное предпочтение, и я не был уверен, что оба блока будут одинаковыми. –

ответ

3

Я бы просто использовать генератор понимание:

from itertools import chain 

for paragraph in chain(doc.paragraphs, (paragraph for table in doc.tables for row in table.rows for cell in row.cells for paragraph in cell.paragraphs)): 
    paragraph.replace("oldtext1","Something") 
    paragraph.replace("oldtext2","Somethingelse") 

Принимая во внимание, что вам не нужно делать проверку опережения для paragraph.replace()

+0

Итак, это сначала проходит через все абзацы, а затем просматривает весь текст в ячейках? – Bijan

+1

@Bijan Да, но если вы измените порядок добавления, он будет делать обратное – DaveBensonPhillips

+0

Примечание: Используя ['itertools.chain'] (https://docs.python.org/3/library/itertools.html#itertools. цепочка) и изменение listcomp на genexpr позволит вам итерировать лениво, вместо того, чтобы создавать 2 потенциально огромные временные 'list' спереди. Также необходимо изменить порядок циклов. 'from itertools import chain',' для пункта в цепочке (doc.paragraphs, (para для таблицы в doc.tables для строки в table.rows для ячейки в row.cells для para в cell.paragraphs)): 'получит те же результаты (если ранняя петля не мутирует одно из значений в 'doc.tables'), но быстрее производит начальные результаты с более низкой пиковой памятью. – ShadowRanger

1

Хотя генератор понимания работает отлично, это может быть пылесосу делегируйте эту задачу своей собственной функции. Это хорошее количество читаемости.

# Python 2.X 
def get_all_paragraphs(document): 
    for paragraph in document.paragraphs: 
     yield paragraph 

    for table in document.tables: 
     for row in table.rows: 
      for cell in row.cells: 
       for paragraph in cell.paragraphs: 
        yield paragraph 

Это может быть очищены некоторые в Python 3.X, используя yield from конструкцию.

# Python 3.X 
def get_all_paragraphs(document): 
    yield from document.paragraphs 

    for table in document.tables: 
     for row in table.rows: 
      for cell in row.cells: 
       yield from cell.paragraphs 

Я не могу придумать способ, чтобы обойти «для строки в строках ... для ячейки в строке ...» модель, однако.

Использование является:

for paragraph in get_all_paragraphs(doc): 
    paragraph.replace("oldtext1","Something") 
    paragraph.replace("oldtext2","Somethingelse")