Pull to refresh

Comments 63

Любопытно, но я рад, что до всего этого дошел сам в процессе изучения
Зачем somefoo = list(range(9)), если range уже возвращает список.
>>> type(range(5))
<class 'range'>


Objects of type range are created using the range() function. They don’t support slicing, concatenation or repetition, and using in, not in, min() or max() on them is inefficient.
docs.python.org/release/3.0.1/library/stdtypes.html#sequence-types-str-bytes-bytearray-list-tuple-range

Благодаря вам, я теперь знаю, что range() возвращает не список)
Да я уже понял, когда увидел форматирование, что автор использует Python 3.x, где range был заменен по-сути более быстрым xrange, который, конечно выдает не список. Для 2.x range() возвращает список.

>>> type(range(5))
<type 'list'>


Теперь вы знаете еще одно различие 2.# и 3.#.
Прошу прощения, ошибся веткой.
Да я уже тоже успел понять, когда прочитал дальше. :)
Начать следовало бы с того, что «Pythonic» это путь использования Python правильно, знаючи, воспользовавшись существующими инструментами, применяя их там, где нужно. Иными словами, «как правильно программировать на Python» и не более того.
Хорошо что Python имеет такой путь, а точнее его прокладывают для первопроходцев, т.к. на других языках его не дают и, в результате чего, мы имеем сотни версий того, как правильно писать на других языках.
somecontaineriterator = iter(somecontainer)   #unpythonic

some_container_iterator = iter(somecontainer)   #pythonic
как-то попадалась библиотека для конструирования путей к файлам, позволяющая вместо
foo + "/" + baz 
записывать просто
foo / baz
не напомните :?
Это, кстати, хоть и не Pythonic, но сама по себе идея отличная…
>>> bar = []                            # pythonic

на днях, рассматривая логи коммитов, нашел удивительную чехорду патчей:
-foo = []
+foo = [ ]
затем
-foo = [ ]
+foo = []

и так несколько раз. а как должно быть pythonic?

а нужно-ли делать, отступы на пустых строках, или нет:
def foo(x):
....
.... if x is None:

или
def foo(x):

.... if x is None:
?
Верно foo = [] (об этом и в PEP 8 говорится)
Отступы в строках делать не нужно. Это нигде не упоминается (поправьте, если не прав), но никакой необходимости в отбивке пустой строки нет, как ни крути.
Вот что примечательно: Отбивку IDE обычно делает автоматом и, если позже применить к такому коду pypi.python.org/pypi/pep8, то валидатор на отбивку ругнётся, хотя в PEP 8, таки да, об этом нет ни слова.
В Eclipse PyDev такая комбинация настроек удаляет отбивку для пустых строк при сохранении (конкретнее — «Auto-format… before saving»+«Right trim lines»)
value = (foo.bar()['first'][0]*baz.ham(1, 2)[5:9]               # pythonic
        + verify(34, 20)*skip(500, 360))

не-а. «The preferred place to break around a binary operator is *after* the operator, not before it.» (© pep-0008)

print("{} is {}, {} and lives in {}.".format(person[0], person[1], person[2], person[3]))

Если по новому стилю определять позиционные параметры, необходимо в фигурных скобках указывать их позицию:

print("{0} is {1}, {2} and lives in {3}.".format(person[0], person[1], person[2], person[3]))

А иначе — ValueError: zero length field name in format
Уродство какое-то.
print("{0} is {1}, {2} and lives in {3}.".format(*person))
Я лишь поправил очевидную ошибку в строковой операции. Ваше решение элегантно, спасибо. Буду впредь использовать именно такое.
А по моему такой вариант еще лучше:
print("%s is %d, %s and lives in %s." % person )
в 3-м питоне поддержку % уберут…
> А иначе — ValueError: zero length field name in format

Только до версии 2.7
Ну по поводу "\" несколько спорно.
Часто это удобней, чем вставлять скобки (например, этих скобок там уже и так много).
В том же Django в исходниках видел много раз.
исходники django анти-pythonic до мозга костей. попробуйте отстроить pylint для таких проектов :)
UFO landed and left these words here
то как выглядит код с вашей точки зрения и с точки зрения pylint — это просто две разные точки зрения. запустите и сами всё увидите. :)
UFO landed and left these words here
>> Не используйте «from foo import *»

Как раз сегодня столкнулся.

У меня есть 2 модуля: один с объектами-контейнерами, и второй с фабриками, где вызывается rest api, парсится xml и штампуются готовые объекты.
Во втором модуле используются ВСЕ объекты первого.
Что может случиться плохого, если я импортирую их как «from module1 import *»?

Запихивать всё в один модуль не очень круто: несколько дюжин тупых объектов, и файл легко разростется на тысячи строчек кода. Импортировать же явно всю эту безразмерную кучу объектов тоже не красиво. «Засорение» глобального пространства имен в этом конкретном случае не выглядит как источник проблем.
хм…
from module1 import *
container


vs

import module1
module1.container


или в виде алиаса (если «module1» лень печатать или конфликтует с кем-то)

import module1 as m
m.container
а
>>> foo = "path1/path2/path3"
>>> baz = "somefile"
>>> os.path.join(foo, baz)


на Windows как сработает? Я правда не в курсе…

«Общие исключения» — это к pythonic/unpythonic не относится. Это для всех ЯП верно в равной степени вроде бы.

«Функции являются объектами» — что то пример как то не в тему… Так же можно сказать что в C функции — объекты. Может кривой перевод?

Ну и отметить, что речь о Python3 не помешало бы.
а
>>> foo = «path1/path2/path3»
>>> baz = «somefile»
>>> os.path.join(foo, baz)

на Windows как сработает? Я правда не в курсе…

отлично работает…

«Функции являются объектами» — что то пример как то не в тему… Так же можно сказать что в C функции — объекты. Может кривой перевод?

Ничего кривого в переводе нет, сказано именно то, что хотел и сказал автор. В C функции — объекты? Простите, но вы несете какой-то бред! В C можно делать так?:
def some_func():
    return 42

some_func.some_property = 42
some_func.some_method = some_func

print(some_func.some_property)
print(some_func.some_method() == some_func())
Т.е. пример с os.path.join() выведет не path1/path2/path3\somefile а самый что ни на есть path1\path2\path3\somefile? Если так, то здорово, я не задавался этим вопросом раньше (НЕ использовать os.path.join НЕ призываю, просто уточнил).

Насчет «функции есть объекты» — я не ставил этот факт под сомнение (и не утверждал что в C функции это объекты тоже), а именно указал что пример не совсем убедительный. Передавать функцию в качестве аргумента другой функции можно во многих языках, в том числе в НЕ объектно-ориентированных), даже в С можно (по ссылке), не говоря уж о функциональных ЯП. И я не воспринимаю пример из статьи как доказательство того, что функции — объекты.
Привели бы в статье пример, который привели сейчас — ни слова не возразил бы.
Ишь, налетели))
Логика работы os.path.join() аналогичка «cd ..».
os.path.join("/a/b/", «c») -> "/a/b/c"
os.path.join("/a/b/", "/c") -> "/c"
Я имею в виду — развернет ли оно POSIX слеши "/" в виндовые бекслеши "\", если аргументы уже содержат в себе слеши или нет?

т.е. корректна ли запись
os.path.join("a/b/", "c")
в Windows, или нужно писать что то в стиле
os.path.join(*"a/b/".split("/"), "c")

Например если в линуксе задать путь в виндовом стиле (с бекслешами)
os.path.join(r"c:\a\b\c", "d")
то выведет чушь
'c:a\\b\\c/d'

Я бы сам попробовал, просто не на чем.
Нет )
>>> os.path.join('a/b', 'c')
'a/b\\c'

функция лишь вставляет os.sep между аргументами. а файл открывается потому, что винда понимает и прямые и обратные слеши (см os.sep, os.altsep ).
Посмотрел код ntpath.py… Вы правы
>>> os.path.join('a/b', 'c')
'a/b\\c'
но зато
>>> os.path.join('a/b/', 'c')
'a/b/c'
Ну вот, получается еще один пример в статье можно считать не совсем удачным (Сама статья ничего, это мне все за заминусованный комментарий обидно)
здесь терминологическая путаница — понятие first-class object (aka значение) и object в ООП (который содержит свойства и методы) — это две разные штуки.

функции в С не являются first-class object — ими являются указатели. а функции в python и то и другое. ;)
В Си (точнее, в C++, т.к. в Си вообще нет объектов) функции не являются объектами.
И даже не являются переменными. Есть только типизированные указатели на функцию, для которых есть операция вызова ().
>Не используйте «from foo import *»

Все зависит от места применения, если это файл с правилами роутинга (например urls в django) то как раз хорошая идея написать from foo.views import *

Вот тут:
>>> counter = 0                                                     # unpythonic
>>> while counter < len(somecontainer):
...     callable_consuming_container_elements(somecontainer[counter])
...     counter += 1
...
...
>>> for item in somecontainer:                                      # pythonic
...     callable_consuming_container_elements(item)


Тоже не корректность, ибо данный случай конечно правилен, но если вы хотите изменять элементы массива достаточно большим алгоритмом (чтоб без lambda), то придется-таки делать while.
for counter, item in enumerate(somecontainer):
    pass


Нет?
Хотя многое из описанного уже знал, но этот топик безусловно идет в избранное.
Читаю dive into python — там есть большая часть написанного. На редкость удачное руководство.
Классы не предназначены для группировки функциональности

Не понял как это понимать. В классе не должно быть статических методов или не должно быть только их, или ещё что?

И ещё вопрос по модулям — видел разные варианты: от весь код модуля в init до «один файл — один класс», как и различные комбинации (что-то в инит, где-то несколько классов в файле, где-то один). Какой стиль pythonic?

А вообще неплохо бы автокорректор какой-то чтобы был, расставлял что pythonic, а что нет. Я вот в своём коде на python практически не вижу отличий от php, а исходя из «уходом от принципа «существует много способов сделать это»» делаю формальный вывод, что код pythonic (раз код работает, значит я нашёл единственный способ сделать это :) ), хотя интуитивно понимаю, что нет, способ не единственный и вряд ли оптимальный по любому критерию, кроме легкости чтения php-шником, изучающим python :(
Под «Классы не предназначены для группировки функциональности» понимается следующее. Очень часто программисты чистых ООП языков по привычки пишут код полностью классами. Хотя очень часто в этом нет необходимости. Например в Java вся математика собрана в классе Math, т.к. по другому реализовать язык не позволяет. В python же не нужно делать подобные классы, когда можно просто реализовать набор функций в модуле.

По поводу ухода от «существует много способов сделать это». Это больше касается разработки языка, к этому стремятся разработчики Python. К этому стоит стремится и когда пишешь собственную функциональность.
Грубо говоря, модуль используется там, где привычно использовать глобальные классы, а классы там, где вложенные?

Привычно оставляет единственный способ «сделать это» (например изменить состояние объекта) полная инкапсуляция данных и разграничение уровней доступа к методам на public (интерфейсы), protected, private и т. п., то есть нельзя извне обратиться к свойству объекта, кроме как через публичные методы объекта, предусмотренные разработчиком. В python же, с его полной интроспекцией, это практически невозможно, как я понял? Или я вообще не понял о чём фраза «К этому стоит стремится и когда пишешь собственную функциональность»?
>Классы не предназначены для группировки функциональности

В том смысле, что если у вас есть набор каких-то функций, которые не работают над общими (инкапсулированными?) данными, то их лучше оформить не как класс со статическими методами а как модуль с набором простых функций. Пример:
#UNPYTHONIC
# math.py
class trig:

    @staticmethod
    sin(self, val):
        pass

    @staticmethod
    cos(self, val):
        pass

    @staticmethod
    tan(self, val):
        pass

#PYTHONIC
# math/trig.py
sin(val):
    pass

cos(val):
    pass

tan(self, val):
    pass


В то же время если нужно работать с общими данными, например формирование запроса в CURL или работа с картинкой, то нужно это оформлять в виде класса, чтоб не передавать обрабатываемые данные первым аргументом (как делают в CURL или в GD в PHP)

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

Корректоры для Python есть, Pylint например, но я с ним не работал, не знаю что он проверяет а что нет. Вообще научиться писать pythonic код не очень сложно как мне кажется. Пара статей вроде этой и вроде ясно.
Самое распространенное забыли: Использовать enumerate
По-поводу использования from foo import * — это вполне допустимо, если вы объявляете
__all__ = ["echo", "surround", "reverse"]  # список используемых штук из модуля

Тогда область видимости не засоряется.
А это чего за конструкция? Какой тип возвращает?
{ line.strip() for line in a_file }
В 2.7 это допустимо.
В 2.6 можно делать set(line.strip() for line in a_file).
это сет. Такую конструкцию в третьей ветке ввели
«Используйте стандартную библиотеку» это надо вырезать на камне и поставить в центре каждого города. В ней действительно очень много всего, а народ продолжает писать свои велосипедики… Например сам долго писал какие-то свои парсеры, вместо строки import csv :)
Добавлю, что стандартную библиотеку не только очень приятно использовать, но её ещё интересно читать (в смысле исходники).
«Списки предназначены для хранения однотипных данных.»
чего это вдруг?

*args

Это семантически верно. Обычно в списках хранят однотипные данные, которые последовательно (и однотипно) кто-то обрабатывает. А для разнотипных значений, которые должны обрабатываться вместе, служат кортежи (typles). Например тот же *arg — кортеж.
В питоне просто не стали делать это правило строгим, чтобы не путать людей. Но во многих языках, в том же Хаскеле например, можно только так.
Хорошая статья для начинающих.

А есть еще вопросы «неоднозначной питоничности»
Например использование функциональных фич вроде генераторов и лямбд.

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

В итоге, если вы можете упихать функцию в 10 строк, хотя на С или Java она бы занимала 100, то лучше этого не делать и обойтись 30-ю.
Only those users with full accounts are able to leave comments. Log in, please.