2009-12-23 3 views
4

У меня есть несколько вопросов о лучших практиках в Python. Не так давно я хотел бы сделать что-то подобное с моим кодом:Python - Вещи, которые я не должен делать?

... 
junk_block = "".join(open("foo.txt","rb").read().split()) 
... 

Я не больше этого делать, потому что я могу видеть, что он делает код труднее читать, но будет код работать медленнее, если я расколоть заявления до, как так:

f_obj = open("foo.txt", "rb") 
f_data = f_obj.read() 
f_data_list = f_data.split() 
junk_block = "".join(f_data_list) 

Я также заметил, что нет ничего мешает вам делать в «импорт» в функциональном блоке, есть ли причина, почему я должен это делать?

+26

Я единственный, кто думает, что первый пример более читабельен? – Kimvais

+3

Нет, нет. – Smashery

+0

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

ответ

21

До тех пор, пока вы находитесь внутри функции (не на верхнем уровне модуля), присвоение промежуточных результатов локальным баренам имеет существенно-незначительную стоимость (на верхнем уровне модуля, при присвоении «локальным» баренам подразумевается взбивание на dict- -модуля- и является значительно более дорогим, чем в функции; средство никогда не должно иметь «существенный» код на верхнем уровне модуля ... всегда фиксируйте существенный код внутри функции! -).

Общая философия Python включает в себя «квартира лучше, чем вложенная», и которая включает в себя сильно «вложенные» выражения. Глядя на ваш оригинальный пример ...

junk_block = "".join(open("foo.txt","rb").read().split()) 

Есть еще одна важная проблема: когда этот файл закрывается? В CPython сегодня вам не нужно беспокоиться - подсчет ссылок на практике гарантирует своевременное закрытие.Но большинство других реализаций Python (Jython на JVM, IronPython на .NET, PyPy на всех видах поддержки, pynie на Parrot, Unladen Swallow на LLVM, если и когда он созревает по опубликованной дорожной карте, ...) do не гарантия использование подсчета ссылок - многие стратегии сбора мусора могут быть задействованы со всеми другими преимуществами.

без каких-либо гарантий подсчета ссылок (и даже в CPython это всегда считается артефактом реализации, не части семантики языка!), То можно было бы исчерпать ресурсы, путем выполнения таких «открытым, но не близко» кода в жесткой петле - сбор мусора вызван нехваткой памяти и не рассматривает другие ограниченные ресурсы, такие как дескрипторы файлов. С 2,6 (и 2,5, с «импортом из будущего»), Python имеет большое решение через RAII («Приобретение ресурсов является инициализация») подход поддерживается with заявление:

with open("foo.txt","rb") as f: 
    junk_block = "".join(f.read().split()) 

является наименьших «неназванный» способ, который обеспечит своевременное закрытие файла во всех совместимых версиях Python. Более сильная семантика делает его предпочтительным.

Помимо обеспечения правильной и разумной ;-) семантики не так много, чтобы выбирать между вложенными и сглаженными версиями выражения, такого как это. Учитывая задачу «удалить все пробелы пробелов из содержимого файла», у меня возникнет соблазн сравнить альтернативные подходы, основанные на re и методе строк .translate (последний, особенно в Python 2. *, часто является самым быстрым способом чтобы удалить все символы из определенного набора!), прежде чем перейти к подходу «раскол и воссоединение», если он окажется более быстрым - но это действительно совсем другая проблема ;-).

+0

+1 для упоминания «с». – Kimvais

+1

@Alex spellcheck: Witout -> без – Tshepang

+0

@Tshepang, спасибо, исправлено. –

4

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

Во-вторых, import в функциональном блоке полезен, если в этой функции есть определенная библиотечная функция, поскольку область импортируемого символа является только блоком, в котором он импортируется, если вы когда-либо используете что-то один раз вы можете просто импортировать его там, где он вам нужен, и не нужно беспокоиться о конфликтах имен в других функциях. Это особенно удобно с операторами from X import Y, поскольку Y не будет квалифицироваться по его содержащемуся имени модуля и, таким образом, может конфликтовать с аналогичной функцией в другом модуле, который используется в другом месте.

+0

Если вы хотите импортировать что-то только для определенной функции, то, пожалуйста, добавьте его в главный блок импорта прямо вверху. Это работает по многим причинам: (1) упрощает расширение кода в будущем, если вы решите использовать одну и ту же библиотеку импорта в других функциях. (2) помогает, если позже вы решите импортировать другую библиотеку. (3) Наличие всех операторов импорта в одном месте говорит любому, кто смотрит на ваш код, какие библиотеки вы используете. Более того, если вы беспокоитесь о том, чтобы сообщить, к какой функции относится импорт, добавьте комментарий – inspectorG4dget

+0

Импорт определенной функции из библиотеки и импорт всей библиотеки не обязательно являются взаимоисключающими. Например, вы можете «импортировать X» в верхней части своего файла, но затем также «из X import Y» в функцию, в которой вы много используете Y, особенно если «X» является довольно длинным именем модуля. – Amber

+0

@Dav - Я не знаю о скоростях каждого из этих подходов, но вместо 'from X import Y' вы можете просто сказать« Y = X.Y »и отказаться от второго оператора импорта. – Smashery

0

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

Вы также должны спросить, действительно ли разница в производительности в рассматриваемом коде. Часто читаемость имеет большую ценность, чем производительность.

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

3

из PEP 8 (который стоит читать в любом случае)

Импорта всегда помещается в верхней части файла, только после любого модуля комментариев и строки документации, и перед модулем глобал и констант

0

Я думаю, что эти два кода являются удобочитаемыми. Я (и это только вопрос личного стиля), вероятно, будет использовать первый, добавив строку с коментами, что-то вроде: «Открыть файл и преобразовать данные внутри в список»

Также есть случаи, когда я использую во-вторых, может быть, не так разделены, но что-то вроде

f_data = open("foo.txt", "rb").read() 
f_data_list = f_data.split() 
junk_block = "".join(f_data_list) 

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

Определенно, код не будет (по крайней мере, намного) медленнее, так как единственная «перегрузка», которую вы делаете, заключается в оценке результатов.

2

Эта линия имеет тот же результат, как это:..

junk_block = открыт ("foo.txt", "Р.Б.") следующим образом() вместо (»», '')

в вашем Например, вы разбиваете слова текста на список слов, а затем присоединяете их обратно вместе без пробелов. В приведенном выше примере вместо этого используется метод str.replace().

Отличие:

Ваш строит объект файла в память, формирует строку в память, читая его, строит список в память, разделяя строку, создает новую строку, вступив в список.

Mine создает файл в памяти, строит строку в памяти, читая его, создает новую строку в памяти, заменяя пробелы.

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

Большая часть памяти будет собираться немедленно, но несколько пользователей одновременно будут работать с ОЗУ.

Смежные вопросы