Дзэн Питона
Изучение культуры которая окружает язык, приближает вас на шаг к лучшим программистам. Если вы всё еще не прочли «Zen of Python» то откройте интерпретатор Python и введите import this. Для каждого элемента в списке вы найдете пример здесь
Однажды моё внимание привлекло:
Красивое лучше уродливого
UPDATE: На Hacker News было много обсуждений этой статьй. Несколько поправок от них.
Дайте мне функцию, которая принимает список чисел и возвращает только четные числа, деленые на два.
#-----------------------------------------------------------------------
halve_evens_only = lambda nums: map(lambda i: i/2, filter(lambda i: not i%2, nums))
#-----------------------------------------------------------------------
def halve_evens_only(nums):
return [i/2 for i in nums if not i % 2]
Помните очень простые вещи в Python
Обмен значений двух переменных:
a, b = b, a
Шаговый аргумент для срезов. Например:
a = [1,2,3,4,5]
>>> a[::2] # iterate over the whole list in 2-increments
[1,3,5]
Частный случай x[::-1] является средством выражения x.reverse().
>>> a[::-1]
[5,4,3,2,1]
UPDATE: Имейте в виду x.reverse() переворачивает список, а срезы дают вам возможность делать это:
>>> x[::-1]
[5, 4, 3, 2, 1]
>>> x[::-2]
[5, 3, 1]
- Не используйте изменяемые типы переменных для значений по умолчанию
def function(x, l=[]): # Don't do this
def function(x, l=None): # Way better
if l is None:
l = []
UPDATE: Я понимаю что не объяснил почему. Я рекомендую прочитать статью Fredrik Lundh. Вкратце этот дизайн иногда встречается. «Значения по умолчанию всегда вычисляются тогда, и только тогда, когда def заявлена на исполнение.»
- Используйте iteritems а не items
iteritems использует generators и следовательно это лучше при работе с очень большими списками.
d = {1: "1", 2: "2", 3: "3"}
for key, val in d.items() # builds complete list when called.
for key, val in d.iteritems() # calls values only when requested.
Это похоже на range и xrange когда xrange вызывает значения только когда попросят.
UPDATE: Заметьте что iteritems, iterkeys, itervalues удаленны из Python 3.x. dict.keys(), dict.items() и dict.values() вернут views instead вместо списка. docs.python.org/release/3.1.5/whatsnew/3.0.html#views-and-iterators-instead-of-lists
- Используйте isinstance а не type
Не делайте:
if type(s) == type(""): ...
if type(seq) == list or
type(seq) == tuple: ...
лучше:
if isinstance(s, basestring): ...
if isinstance(seq, (list, tuple)): ...
Почему не стоит делать так: stackoverflow.com/a/1549854/504262
Заметьте что я использую basestring а не str, поскольку вы можете пытаться проверить соответствие unicode к str. Например:
>>> a=u'aaaa'
>>> print isinstance(a, basestring)
True
>>> print isinstance(a, str)
False
Это происходит потому что в Python версиях < 3.0 существует два строковых типа: str и unicode:
object
|
|
basestring
/
/
str unicode
- Изучите различные коллекций
Python имеет различные типы контейнеров данных являющихся лучшей альтернативой базовым list и dict в различных случаях.
В большинстве случаев используются эти:
UPDATE: Я знаю большинство не использовало это. Невнимательность с моей стороны. Некоторые могут это написать так:
freqs = {}
for c in "abracadabra":
try:
freqs[c] += 1
except:
freqs[c] = 1
Некоторые могут сказать, лучше было бы:
freqs = {}
for c in "abracadabra":
freqs[c] = freqs.get(c, 0) + 1
Скорее используйте коллекцию типа defaultdict:
from collections import defaultdict
freqs = defaultdict(int)
for c in "abracadabra":
freqs[c] += 1
Другие коллекций
namedtuple() # factory function for creating tuple subclasses with named fields
deque # list-like container with fast appends and pops on either end
Counter # dict subclass for counting hashable objects
OrderedDict # dict subclass that remembers the order entries were added
defaultdict # dict subclass that calls a factory function to supply missing values
UPDATE: Как отметили в нескольких комментария на Hacker News я мог бы использовать Counter вместо defaultdict.
>>> from collections import Counter
>>> c = Counter("abracadabra")
>>> c['a']
5
- Создавая классы в Python задействуйте magic methods
__eq__(self, other) # Defines behavior for the equality operator, ==.
__ne__(self, other) # Defines behavior for the inequality operator, !=.
__lt__(self, other) # Defines behavior for the less-than operator, <.
__gt__(self, other) # Defines behavior for the greater-than operator, >.
__le__(self, other) # Defines behavior for the less-than-or-equal-to operator, <=.
__ge__(self, other) # Defines behavior for the greater-than-or-equal-to operator, >=.
Существует ряд других магических методов.
- Условные назначения
x = 3 if (y == 1) else 2
Оно делает именно то, как это звучит: «назначить 3 для x если y=1, иначе назначить 2 для x». Вы также можете применить это если у вас есть нечто более сложное:
x = 3 if (y == 1) else 2 if (y == -1) else 1
Хотя в какой то момент оно идет слишком далеко.
Обратите внимание что вы можете использовать выражение if...else в любом выражение. Например:
(func1 if y == 1 else func2)(arg1, arg2)
Здесь будет вызываться func1 если y=1, и func2 в противном случае. В обоих случаях соответствующая функция будет вызываться с аргументами arg1 and arg2.
Аналогично, также справедливо следующее:
x = (class1 if y == 1 else class2)(arg1, arg2)
где class1 и class2 являются классами.
- Используйте Ellipsis когда это необходимо.
UPDATE: Один из комментаторов с Hacker News упоминал: «Использование многоточие для получения всех элементов, является нарушением принципа „Только Один Путь Достижения Цели“. Стандартное обозначение это [:]». Я с ним согласен. ЛУчший пример использования в NumPy на stackoverflow:
Многоточие используется что бы нарезать многомерные структуры данных.
В данной ситуации это означает, что нужно расширить многомерный срез во всех измерениях.
Пример:
>>> from numpy import arange
>>> a = arange(16).reshape(2,2,2,2)
Теперь у вас есть 4-х мерная матрица порядка 2x2x2x2. Для того что бы выбрать все первые элементы 4-го измерения, вы можете воспользоваться многоточием:
>>> a[..., 0].flatten()
array([ 0, 2, 4, 6, 8, 10, 12, 14])
что эквивалентно записи:
>>> a[:,:,:,0].flatten()
array([ 0, 2, 4, 6, 8, 10, 12, 14])
Предыдущие предложения.
При создание класса вы можете использовать __getitem__ для того что бы ваш объект класса вел себя как словарь. Возьмите этот класс в качестве примера:
class MyClass(object):
def __init__(self, a, b, c, d):
self.a, self.b, self.c, self.d = a, b, c, d
def __getitem__(self, item):
return getattr(self, item)
x = MyClass(10, 12, 22, 14)
Из-за __getitem__ вы сможете получить значение a объекта x как x['a']. Вероятно это известный факт.
Этот объект используется для расширения срезов Python docs.python.org/library/stdtypes.html#bltin-ellipsis-object Таким образом если мы добавим:
def __getitem__(self, item):
if item is Ellipsis:
return [self.a, self.b, self.c, self.d]
else:
return getattr(self, item)
Мы сможем использовать x[...] что бы получить список всех элементов.
>>> x = MyClass(11, 34, 23, 12)
>>> x[...]
[11, 34, 23, 12]
P.S
Это перевод поста Satyajit Ranjeev – "A few things to remember while coding in python.". Но оформить в хаб переводов не хватило 0.5 кармы, и сохранить черновик не позволяет, поэтому выкладываю как есть. Просьба все замечания по переводу и орфографии присылать в личку, и сильно не пинать =)
Автор: yurtaev