Pull to refresh

Вещи, о которых следует помнить, программируя на Python

Reading time 5 min
Views 64K

Дзэн Питона



Изучение культуры, которая окружает язык, приближает вас на шаг к лучшим программистам. Если вы всё еще не прочли «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 вместо списка. 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 кармы, и сохранить черновик не позволяет, поэтому выкладываю как есть. Просьба все замечания по переводу и орфографии присылать в личку, и сильно не пинать =)
Tags:
Hubs:
+112
Comments 27
Comments Comments 27

Articles