Поздравляю с началом вашего приключения с Python! Не все в этом посте может иметь смысл прямо сейчас, но добавьте его в закладки и вернитесь к нему, если это окажется полезным позже. В конце концов вы должны попытаться перейти от сценариев к разработке программного обеспечения, и вот несколько идей для вас!
С большой силой приходит большая ответственность, и как разработчику Python вы должны быть более дисциплинированными, чем другие языки, которые не держат вас в руках и обеспечивают "хороший" дизайн.
Мне кажется, это помогает начать с дизайна сверху вниз.
def main():
text = get_text()
p_text = process_text(text)
catalogue = process_catalogue(p_text)
БУМ! Вы только что написали всю программу - теперь вам просто нужно вернуться и заполнить пробелы! Когда вы делаете это таким образом, это кажется менее пугающим. Лично я не считаю себя достаточно умным, чтобы решать очень большие проблемы, но я профессионал в решении небольших проблем. Так что давайте решать одну вещь за один раз. Я собираюсь начать с 'process_text'.
def process_text(text):
b_text = bundle_dialogue_items(text)
f_text = filter_dialogue_items(b_text)
c_text = clean_dialogue_items(f_text)
Я пока не совсем уверен, что означают эти вещи, но я знаю, что проблемы с текстом имеют тенденцию следовать шаблону, называемому «карта / уменьшение», что означает, что вы выполняете и обрабатываете что-то, а затем очищаете и комбинируете, поэтому Я вставил некоторые функции-заполнители. Я мог бы вернуться и добавить больше, если это необходимо.
Теперь давайте напишем 'process_catalogue'. Я мог бы написать «process_dict», но это звучало для меня неубедительно.
def process_catalogue(p_text):
speakers = make_catalogue(c_text)
s_speakers = sum_words_per_paragraph_items(speakers)
t_speakers = total_word_count(s_speakers)
Cool. Не так уж плохо. Вы можете подойти к этому не так, как я, но я подумал, что имеет смысл объединить элементы, подсчитать количество слов в каждом абзаце, а затем подсчитать все слова.
Итак, на данный момент я, вероятно, сделаю один или два маленьких модуля 'lib' (библиотеки), чтобы заполнить оставшиеся функции. Ради того, чтобы вы смогли запустить это, не беспокоясь об импорте, я собираюсь все это поместить в один файл .py, но в конце концов вы научитесь разбивать их, чтобы они выглядели лучше. Итак, давайте сделаем это.
# ------------------ #
# == process_text == #
# ------------------ #
def bundle_dialogue_items(lines):
cur_speaker = None
paragraphs = Counter()
for line in lines:
if re.match(p, line):
cur_speaker, dialogue = line.split(':')
paragraphs[cur_speaker] += 1
else:
dialogue = line
res = cur_speaker, dialogue, paragraphs[cur_speaker]
yield res
def filter_dialogue_items(lines):
for name, dialogue, paragraph in lines:
if dialogue:
res = name, dialogue, paragraph
yield res
def clean_dialogue_items(flines):
for name, dialogue, paragraph in flines:
s_dialogue = dialogue.strip().split()
c_dialouge = [clean_word(w) for w in s_dialogue]
res = name, c_dialouge, paragraph
yield res
ааа и маленькая вспомогательная функция
# ------------------- #
# == aux functions == #
# ------------------- #
to_clean = string.whitespace + string.punctuation
def clean_word(word):
res = ''.join(c for c in word if c not in to_clean)
return res
Так что это может быть неочевидно, но эта библиотека спроектирована как конвейер обработки данных. Существует несколько способов обработки данных, один - конвейерная обработка, а другой - пакетная обработка. Давайте посмотрим на пакетную обработку.
# ----------------------- #
# == process_catalogue == #
# ----------------------- #
speaker_stats = 'stats'
def make_catalogue(names_with_dialogue):
speakers = {}
for name, dialogue, paragraph in names_with_dialogue:
speaker = speakers.setdefault(name, {})
stats = speaker.setdefault(speaker_stats, {})
stats.setdefault(paragraph, []).extend(dialogue)
return speakers
word_count = 'word_count'
def sum_words_per_paragraph_items(speakers):
for speaker in speakers:
word_stats = speakers[speaker][speaker_stats]
speakers[speaker][word_count] = Counter()
for paragraph in word_stats:
speakers[speaker][word_count][paragraph] += len(word_stats[paragraph])
return speakers
total = 'total'
def total_word_count(speakers):
for speaker in speakers:
wc = speakers[speaker][word_count]
speakers[speaker][total] = 0
for c in wc:
speakers[speaker][total] += wc[c]
return speakers
Все эти вложенные словари становятся немного сложнее. В реальном производственном коде я бы заменил их более читаемыми классами (вместе с добавлением тестов и строк документации !!), но я не хочу делать это более запутанным, чем это уже есть! Хорошо, для вашего удобства ниже все это вместе.
import pprint
import re
import string
from collections import Counter
p = re.compile(r'(\w+?):')
def get_text_line_items(text):
for line in text.split('\n'):
yield line
def bundle_dialogue_items(lines):
cur_speaker = None
paragraphs = Counter()
for line in lines:
if re.match(p, line):
cur_speaker, dialogue = line.split(':')
paragraphs[cur_speaker] += 1
else:
dialogue = line
res = cur_speaker, dialogue, paragraphs[cur_speaker]
yield res
def filter_dialogue_items(lines):
for name, dialogue, paragraph in lines:
if dialogue:
res = name, dialogue, paragraph
yield res
to_clean = string.whitespace + string.punctuation
def clean_word(word):
res = ''.join(c for c in word if c not in to_clean)
return res
def clean_dialogue_items(flines):
for name, dialogue, paragraph in flines:
s_dialogue = dialogue.strip().split()
c_dialouge = [clean_word(w) for w in s_dialogue]
res = name, c_dialouge, paragraph
yield res
speaker_stats = 'stats'
def make_catalogue(names_with_dialogue):
speakers = {}
for name, dialogue, paragraph in names_with_dialogue:
speaker = speakers.setdefault(name, {})
stats = speaker.setdefault(speaker_stats, {})
stats.setdefault(paragraph, []).extend(dialogue)
return speakers
def clean_dict(speakers):
for speaker in speakers:
stats = speakers[speaker][speaker_stats]
for paragraph in stats:
stats[paragraph] = [''.join(c for c in word if c not in to_clean)
for word in stats[paragraph]]
return speakers
word_count = 'word_count'
def sum_words_per_paragraph_items(speakers):
for speaker in speakers:
word_stats = speakers[speaker][speaker_stats]
speakers[speaker][word_count] = Counter()
for paragraph in word_stats:
speakers[speaker][word_count][paragraph] += len(word_stats[paragraph])
return speakers
total = 'total'
def total_word_count(speakers):
for speaker in speakers:
wc = speakers[speaker][word_count]
speakers[speaker][total] = 0
for c in wc:
speakers[speaker][total] += wc[c]
return speakers
def get_text():
text = '''BOB: blah blah blah blah
blah hello goodbye etc.
JERRY:.............................................
...............
BOB:blah blah blah
blah blah blah
blah.
BOB: boopy doopy doop
P1: Bla bla bla.
P2: Bla bla bla bla.
P1: Bla bla.
P3: Bla.'''
text = get_text_line_items(text)
return text
def process_catalogue(c_text):
speakers = make_catalogue(c_text)
s_speakers = sum_words_per_paragraph_items(speakers)
t_speakers = total_word_count(s_speakers)
return t_speakers
def process_text(text):
b_text = bundle_dialogue_items(text)
f_text = filter_dialogue_items(b_text)
c_text = clean_dialogue_items(f_text)
return c_text
def main():
text = get_text()
c_text = process_text(text)
t_speakers = process_catalogue(c_text)
# take a look at your hard work!
pprint.pprint(t_speakers)
if __name__ == '__main__':
main()
Так что этот сценарий почти наверняка излишен для этого приложения, но суть в том, чтобы увидеть, как (сомнительно) читаемый, поддерживаемый, модульный код Python может выглядеть.
Уверен, что вывод выглядит примерно так:
{'BOB': {'stats': {1: ['blah',
'blah',
'blah',
'blah',
'blah',
'hello',
'goodbye',
'etc'],
2: ['blah',
'blah',
'blah',
'blah',
'blah',
'blah',
'blah'],
3: ['boopy', 'doopy', 'doop']},
'total': 18,
'word_count': Counter({1: 8, 2: 7, 3: 3})},
'JERRY': {'stats': {1: ['', '']}, 'total': 2, 'word_count': Counter({1: 2})},
'P1': {'stats': {1: ['Bla', 'bla', 'bla'], 2: ['Bla', 'bla']},
'total': 5,
'word_count': Counter({1: 3, 2: 2})},
'P2': {'stats': {1: ['Bla', 'bla', 'bla', 'bla']},
'total': 4,
'word_count': Counter({1: 4})},
'P3': {'stats': {1: ['Bla']}, 'total': 1, 'word_count': Counter({1: 1})}}