Мой код гнездится слишком глубоко. Есть ли способ лучше?

Этот конкретный код, который я написал недавно в другом вопросе, и я не уверен, что он оптимален. Однако я не мог найти способ с меньшим отступлением. Здесь?

def msg_generator(self): ''' Provides messages until bot dies ''' while self.alive: for msg in self.irc.recv(self.buffer).split(('\r\n').encode()): if len(msg) > 3: try: yield Message(msg.decode()) except Exception as e: self.log('%s %s\n' % (except_str, str(e))) 

Я продолжаю слышать, что слишком много гнездятся, но это кажется необходимым. В настоящее время это четыре углубления.

С головы до ног вы можете сделать это:

 def msg_generator(self): ''' Provides messages until bot dies ''' while self.alive: for msg in self.irc.recv(self.buffer).split(('\r\n').encode()): if len(msg) <= 3: continue try: yield Message(msg.decode()) except Exception as e: self.log('%s %s\n' % (except_str, str(e))) 

Или вы можете реорганизовать функции.

 def msg_generator(self): ''' Provides messages until bot dies ''' while self.alive: for msg in self.irc.recv(self.buffer).split(('\r\n').encode()): if not len(msg) > 3: continue yield handle_message(msg) def handle_message(msg): try: return Message(msg.decode()) except Exception as e: self.log('%s %s\n' % (except_str, str(e))) 

Или используйте что-то вроде этого:

 from itertools import imap def msg_generator(self): ''' Provides messages until bot dies ''' while self.alive: it = iter(self.irc.recv(self.buffer).split(('\r\n').encode())) return imap(handle_message, (msg for msg in it if len(msg) > 3) def handle_message(msg): try: return Message(msg.decode()) except Exception as e: self.log('%s %s\n' % (except_str, str(e))) 

Последняя опция не идеальна, потому что если есть исключение, функция func вернет None которая не является реальным сообщением, поэтому вы также можете отфильтровать ее после использования filter() или иметь другой конец дескриптора None msg.

проблема с вышеприведенным кодом заключается не столько в том, что он слишком глубоко вложен, но и в том, что код слишком экстенсиональный , недостаточно преднамеренный . под этим я подразумеваю, что, хотя зубцы и колеса и рычаги, которые вам нужны для сборки вашего оборудования, находятся на своем месте и (предположительно) работают, слишком мало обозначенных модулей видно – слишком много проблем «забито» во слишком много неявных , немаркированные способы, приводящие к трудно читаемому коду (для этой части «комлекта», вам нравится слушать http://www.infoq.com/presentations/Simple-Made-Easy – это очень полезный разговор , и это ставит пальцем на проблемы с вашим кодом).

позвольте мне привести пример:

 ... for msg in self.irc.recv(self.buffer).split(('\r\n').encode()): ... 

я так понимаю, вы говорите о «сообщениях» здесь. так почему бы не назвать это message переменной? почему abbrv. Это слово? message настолько яснее.

теперь это for message in xxx . что такое xxx ? ну, вы уже знаете. на Pythonic English, это правда, в большинстве случаев, что for apple in appples; for pear in pears for apple in appples; for pear in pears должен быть хороший, разумно семантический способ написания вещей: for ... in петле выделяются элементы, по одному, из набора элементов, поэтому for <singular> in <plural> в порядке большую часть времени. который дает нам

 ... for message in messages: ... 

откуда берутся эти сообщения? в соответствии с вашим кодом, они являются результатом выполнения self.irc.recv(self.buffer).split(('\r\n').encode()) .

теперь это облом. для начала, ('\r\n').encode() определенно совпадает с '\r\n'.encode() , что меньше на один уровень круглых скобок. теперь это, безусловно, постоянный, так зачем вычислять его заново на каждом шагу? также может быть лучше декодировать весь возврат буфера с помощью irc.recv() вместо первого разделения, а затем декодировать по очереди.

но это вторично по отношению к рассматриваемой теме. важно: какой бы низкоуровневый код не был необходим, чтобы дать вам следующий список строк из irc , он, вероятно, не должен появляться в этом месте. скорее, что-то вроде

 messages = self.irc.get_messages() 

или, может быть

 messages = <SOME_LIBRARY>.get_irc_messages( self.irc ) 

было бы уместным. детали, конечно, подвержены многим спорным дискуссиям. лично я стараюсь не конфликтовать мои объекты с объектами с сохранением данных в основном родовых типов без каких-либо функциональных возможностей, связанных с ними, с одной стороны, и с моими библиотеками – безграмотными коллекциями названных частей функциональности – с другой. Простая сделанная легкая презентация, связанная выше, делает точно такую ​​же точку, кстати, и позволяет убедительно утверждать. (в сторону: конечно, это летит в лицо нынешнему мышлению ООП, но google для исполнения Егге в Королевстве существительных, если вы полагаете, что OOP ≡ good n'importe quois .)

настоящий метод связан с ответом на сообщения, а не их получением . подумайте об этом, как колонку «Ваш истинный» в газете: здесь мы находимся в офисе, который придумывает и компилирует ответы, которые будут разосланы читателям; те, кто открывают входящий бумажный портфель, забирают телефон и просеивают по электронной почте, имеют совершенно другую работу, имеющую место в другом офисе. поэтому, как открыть конверт не должно быть чем-то, что вы должны реализовать в этом месте . требуется аутсорсинг.

независимо от того, хотите ли вы, чтобы эта переменная messages явной или сгруппирована с вызовом встроенного API, зависит от числа факторов, таких как доступное пространство, и того, хотите ли вы переработать это возвращаемое значение в другой точке в подпрограмме. это выглядит хорошо для меня:

 ... for message in self.irc.get_messages(): ... 

for ... in будет прозрачно работать с генераторами и списками-возвратами, что хорошо.

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

если бы я писал тестовые примеры против вашего кода, я бы определенно выделил эту часть irc.recv и протестировал ее отдельно. подумайте о своем коде таким образом: что такое хорошие модули, каждый из которых имеет одну проблему и разумный уровень изоляции, звуковые параметры вызова, которые делают хорошую проверку? какие разумные имена для части моего кода? – Если вы этого не сделаете, ваш код просто перестанет влагать слишком глубоко, сам по себе.