Эффективные И Неэффективные Методы Программирования На Python

Привет, Хабр! Предлагаю вашему вниманию перевод статьи Хорошие и плохие практики кодирования на Python от Дуомли.

Python — это язык программирования высокого уровня, в котором особое внимание уделяется читабельности.

Он разрабатывается, поддерживается и часто используется в соответствии с The Zen of Python или PEP 20. В этой статье показано несколько примеров хороших и плохих практик написания кода на Python, с которыми вы, вероятно, столкнетесь.



Использование распаковки для написания компактного кода

Упаковка и распаковка — мощные инструменты Python. Вы можете использовать распаковку для присвоения значений переменным:
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

>>> a, b = 2, 'my-string' >>> a 2 >>> b 'my-string'

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



>>> a, b = b, a >>> a 'my-string' >>> b 2

Это потрясающе! Распаковку также можно использовать для присвоения значений нескольким переменным в более сложных случаях.

Например, вы можете написать так:

>>> x = (1, 2, 4, 8, 16) >>> a = x[0] >>> b = x[1] >>> c = x[2] >>> d = x[3] >>> e = x[4] >>> a, b, c, d, e (1, 2, 4, 8, 16)

Но вместо этого вы также можете использовать более краткий и, возможно, более читабельный способ:

>>> a, b, c, d, e = x >>> a, b, c, d, e (1, 2, 4, 8, 16)

Это круто, правда? Но можно написать ещё круче:

>>> a, *y, e = x >>> a, e, y (1, 16, [2, 4, 8])

Хитрость в том, что переменная с * собирает значения, которые не присвоены другим переменным.



Использование цепочки для написания компактного кода

Python позволяет объединять операторы сравнения в цепочку.

Таким образом, вам не нужно проверять, верны ли два или более сравнения:

>>> x = 4 >>> x >= 2 and x <= 8 True

Вместо этого можно использовать более компактную форму записи:

>>> 2 <= x <= 8 True >>> 2 <= x <= 3 False

Python также поддерживает присвоение переменных по цепочке.

Итак, если вы хотите назначить такой же присваивать значение нескольким переменным одновременно, это можно сделать простым способом:

>>> x = 2 >>> y = 2 >>> z = 2

Более компактный способ — использовать распаковку:

>>> x, y, z = 2, 2, 2

Однако все выглядит еще круче, если вы используете цепное присвоение:

>>> x = y = z = 2 >>> x, y, z (2, 2, 2)

Будьте осторожны, когда значения разные! Все переменные относятся к одному и тому же значению.



Проверьте отсутствие

None не является уникальным объектом в Python. Имеет аналоги, например, null в C-подобных языках.

Вы можете проверить, ссылается ли переменная на None, используя операторы сравнения.

== И != :

>>> x, y = 2, None >>> x == None False >>> y == None True >>> x != None True >>> y != None False

Однако предпочтительнее использовать является И не является :

>>> x is None False >>> y is None True >>> x is not None True >>> y is not None False

Причем лучше использовать дизайн x не имеет значения «Нет» , а не менее читаемый вариант ( х — нет ).



Перечисление последовательностей и отображений

Вы можете реализовать циклы в Python несколькими способами.

Python предлагает несколько встроенных классов, упрощающих их реализацию.

Вы почти всегда можете использовать диапазон для создания цикла, производящего целые числа:

>>> x = [1, 2, 4, 8, 16] >>> for i in range(len(x)): .

print(x[i]) .

1 2 4 8 16

Однако есть лучший способ сделать это:

>>> for item in x: .

print(item) .

1 2 4 8 16

Но что, если вы хотите запустить цикл в обратном порядке? Конечно, вы можете снова использовать диапазон:

>>> for i in range(len(x)-1, -1, -1): .

print(x[i]) .

16 8 4 2 1

Но «перевернуть» последовательность — более компактный способ:

>>> for item in x[::-1]: .

print(item) .

16 8 4 2 1

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

>>> for item in reversed(x): .

print(item) .

16 8 4 2 1

Иногда необходимы как элементы последовательности, так и соответствующие им индексы:

>>> for i in range(len(x)): .

print(i, x[i]) .

0 1 1 2 2 4 3 8 4 16

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

>>> for i, item in enumerate(x): .

print(i, item) .

0 1 1 2 2 4 3 8 4 16

Отлично.

Но что, если вам нужно перебрать две или более последовательностей? Конечно, вы можете снова использовать диапазон:

>>> y = 'abcde' >>> for i in range(len(x)): .

print(x[i], y[i]) .

1 a 2 b 4 c 8 d 16 e

В этом случае Python также предлагает лучшее решение.

Вы можете подать заявку молния :

>>> for item in zip(x, y): .

print(item) .

(1, 'a') (2, 'b') (4, 'c') (8, 'd') (16, 'e')

Вы также можете совместить этот метод с распаковкой:

>>> for x_item, y_item in zip(x, y): .

print(x_item, y_item) .

1 a 2 b 4 c 8 d 16 e

Имейте в виду, что диапазон может быть весьма полезным.

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

Итерация по словарю дает его ключи:

>>> z = {'a': 0, 'b': 1} >>> for k in z: .

print(k, z[k]) .

a 0 b 1

Однако вы можете использовать метод .

предметы() и получите ключи и соответствующие им значения:

>>> for k, v in z.items(): .

print(k, v) .

a 0 b 1

Вы также можете использовать методы .

keys() И .

ценности() для перебора ключей и значений соответственно.



Сравнение с нулем

Когда у вас есть числовые данные и вам нужно проверить, равны ли числа нулю, вы можете (но не всегда нужно) использовать операторы сравнения.

== И != :

>>> x = (1, 2, 0, 3, 0, 4) >>> for item in x: .

if item != 0: .

print(item) .

1 2 3 4

Способ, которым предлагает Python, — это интерпретировать значение null как ЛОЖЬ , а все остальные числа имеют вид Истинный :

>>> bool(0) False >>> bool(-1), bool(1), bool(20), bool(28.4) (True, True, True, True)

Имея это в виду, вы можете использовать если предмет вместо если товар! = 0 :

>>> for item in x: .

if item: .

print(item) .

1 2 3 4

Вы можете следовать той же логике и использовать если не предмет вместо если элемент == 0 .



Как избежать изменяемых необязательных аргументов

Python имеет очень гибкую систему предоставления аргументов функциям и методам.

Необязательные аргументы являются частью этого.

Но будьте осторожны: обычно нежелательно использовать изменяемые необязательные аргументы.

Рассмотрим следующий пример:

>>> def f(value, seq=[]): .

seq.append(value) .

return seq

На первый взгляд это выглядит так, если вы не укажете последовательность , е() добавляет ценить в пустой список и возвращает что-то вроде [значение]:

>>> f(value=2) [2]

Выглядит великолепно, не так ли? Нет! Рассмотрим следующие примеры:

>>> f(value=4) [2, 4] >>> f(value=8) [2, 4, 8] >>> f(value=16) [2, 4, 8, 16]

Удивлен? Если да, то вы не одиноки.

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

Возможно, иногда это будет то, что вам нужно.

Однако гораздо более вероятно, что вам придется этого избегать.

Один из способов заключается в следующем:

>>> def f(value, seq=None): .

if seq is None: .

seq = [] .

seq.append(value) .

return seq

Меньшая версия:

>>> def f(value, seq=None): .

if not seq: .

seq = [] .

seq.append(value) .

return seq

Теперь мы получаем другой результат:

>>> f(value=2) [2] >>> f(value=4) [4] >>> f(value=8) [8] >>> f(value=16) [16]

В большинстве случаев это то, что вам нужно.



Избегание классических методов получения и установки

Python позволяет определять методы получения и установки точно так же, как C++ и Java:

>>> class C: .

def get_x(self): .

return self.__x .

def set_x(self, value): .

self.__x = value

Вот как вы можете их использовать:

>>> c = C() >>> c.set_x(2) >>> c.get_x() 2

В некоторых случаях это лучший способ.

Однако зачастую лучше использовать свойства, особенно в простых случаях:

>>> class C: .

@property .

def x(self): .

return self.__x .

@x.setter .

def x(self, value): .

self.__x = value

Свойства считаются более Pythonic, чем классические геттеры и сеттеры.

Их можно использовать так же, как в C#, то есть как обычные атрибуты данных:

>>> c = C() >>> c.x = 2 >>> c.x 2

Итак, в общем, лучше использовать свойства, когда это возможно.



Избегание доступа к защищенным членам класса

В Python нет частных членов класса как таковых.

Однако если вы напишете (_) в начале имени элемента, то доступ к его изменению вне класса будет запрещен.

Например, рассмотрим код:

>>> class C: .

def __init__(self, *args): .

self.x, self._y, self.__z = args .

>>> c = C(1, 2, 4)

Экземпляры класса C имеют три элемента данных: .

x, .

y и .

_Cz. Если имя участника начинается с двойного подчеркивания (дандер), оно становится измененным.

Вот почему вместо .

_z используется элемент .

_Cz. Теперь вы можете получить доступ к .

x или изменить его напрямую:

>>> c.x # OK 1

Вы также можете получить доступ к .

_y или изменить его вне его класса, но это считается дурным тоном:

>>> c._y # Possible, but a bad practice! 2

Вы не можете получить доступ к .

z, поскольку переменная изменена, но вы можете получить доступ или изменить .

_Cz:

>>> c.__z # Error! Traceback (most recent call last): File "", line 1, in AttributeError: 'C' object has no attribute '__z' >>> c._C__z # Possible, but even worse! 4 >>>

Вам следует избегать этого.

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



Использование менеджеров контекста для освобождения ресурсов

Иногда вам нужно написать код для правильного управления ресурсами.

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

Например, вы можете открыть файл и обработать его:

>>> my_file = open('filename.csv', 'w') >>> # do something with `my_file`

Чтобы правильно распоряжаться памятью, после окончания работы необходимо закрыть этот файл:

>>> my_file = open('filename.csv', 'w') >>> # do something with `my_file and` >>> my_file.close()

Делать это таким образом лучше, чем не делать этого вообще.

Но что, если при обработке файла возникнет исключение? Тогда my_file.close() никогда не выполняется.

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

Второй способ означает, что вы помещаете свой код в блок.

с :

>>> with open('filename.csv', 'w') as my_file: .

# do something with `my_file`

Использование блока with означает, что специальные методы .

входить() И .

Выход() вызываются даже в исключительных случаях.

Эти методы должны заботиться о ресурсах.

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



Стилистические советы

Код Python должен быть элегантным, кратким и читаемым.

Основным ресурсом о том, как писать красивый код Python, является Руководство по стилю для кода Python, или PEP 8. Вам следует прочитать его, если вы хотите писать код Python.

выводы

В этой статье даются несколько советов о том, как писать более компактный и читаемый код. Короче говоря, он показывает, как сделать ваш код более Pythonic. Кроме того, PEP 8 содержит Руководство по стилю кода Python, а PEP 20 знакомит с принципами языка Python. Наслаждайтесь написанием полезного и красивого кода! Спасибо за чтение.

Теги: #python #программирование #перевод #phyton #краткость

Вместе с данным постом часто просматривают: