Рубрики
Без рубрики

Писать более идиоматический и пифитонический код

Есть много способов, которыми можно реализовать ту же функцию, алгоритм или функцию. Некоторые из них прямые … Теги с Python, учебником, WebDev, CodeQuality.

Есть много способов, которыми можно реализовать ту же функцию, алгоритм или функцию. Некоторые из них просты, ясно – Лучше , некоторые из них запутаны, неэффективны – хуже Отказ Сообщество Python часто использует такие термины, как Питон или идиоматический При описании кода, который следует за определенным (естественным, правильным) стилем и конвенциями. Это хороший хороший, ясный код, который мы все пытаемся писать каждый день, и в этой статье мы пойдем на несколько советов, конвенций и идиом, которые помогут вам написать немного больше идиоматический и Питон код.

Сравнение личности и равенства

Не только в Python, но на самом деле в любом языке программирования вы можете попасть в ловушку смешивания личности и ценности равенства. В Python у вас есть выбор, используя либо это или == Для сравнения, где это проверяет личность и ==. проверяет значение.

Учитывая, что большую часть времени мы заботимся о ценности, а не идентичности, мы обычно выбираем ==. . Однако есть случаи, где вы всегда должны использовать это Оператор вместо этого. Один из тех сравнение со всеми питонами Singletons.Нет , Правда или Ложь Отказ

Использование нет , правда или ложно Не только о конвенции или улучшенной читаемости, хотя. Это также улучшает производительность, особенно если вы будете использовать х это не вместо х внутренняя петля. Это почему? – Вы можете спросить. Ну, это потому, что это Оператор не может быть перегружен как == (который действительно просто A.__ EQ __ (B) ), поэтому Python может пропустить поиск этих Dunder Методы, необходимые для оценки сравнения с использованием == Отказ

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

Контекстные менеджеры вместо того, чтобы попробовать/наконец

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

# Bad
try:
    page = urlopen(url)
    ...
finally:
    page.close()

# Good
from contextlib import closing

with closing(urlopen(url)) as page:
    ...

Код выше показывает так называемое Контекстный протокол который состоит из 2 методов – __enter__ и __exit__ которые называются, когда входящий и Выход Тело с блок соответственно. Вы, вероятно, уже знаете о с заявление и это использование, но вы можете не знать о contextlib используется выше. Это модуль, который предоставляет инструменты для поворота функций в контекстные менеджеры. Что касается закрытие Функция выше, это просто заставляет звонить в .Close () Способ объекта, в этом случае Страница Отказ

Использование Контекстный протокол Однако не ограничивается управлением ресурсами. Он также может быть использован для подавления исключений ( с подавлением (...) ) или перенаправляющим выходом ( с Redirect_stdout (...) ):

# Bad
import os
try:
    os.remove(path)
except FileNotFoundError:
    pass

# Good
from contextlib import suppress

with suppress(FileNotFoundError) :
    os.remove(path)

Проверка, если параметр был Предоставлена

Время от времени вам может потребоваться определить функцию, которая принимает дополнительные аргументы. Это может быть сделано в Python очень легко и верно знать, как:

def myfunc(x, y=10):  # `y` is optional
    ...

myfunc(5)      # x = 5, y = 10
myfunc(5, 25)  # x = 5, y = 25

В большинстве случаев мы используем необязательные аргументы, позволяющие пользователю нашей функции опускать очевидный аргумент по умолчанию или редко используемую опцию. В некоторых случаях мы можем захотеть изменить поведение нашей функции, основанной не только на значение необязательного аргумента, но и на основе того, был ли аргумент был предоставлен или нет. Одно разумное решение для этого случая может быть использовано Нет По умолчанию (когда это Нет Делай х, когда это не делает Y). Но Что если Нет приемлемая стоимость? Вы могли бы выбрать другую валюту, но есть приятный идиоматический Решение этого:

_no_value = object()

def myfunc(x, y=_no_value):
    if y is _no_value:
        print("Optional parameter wasn't supplied...")

Мы можем решить эту проблему, создавая постоянную – например – называется _no_value который мы устанавливаем значение по умолчанию для необязательного аргумента. Делая это, мы избегаем возможных приемлемых ценностей, потому что мы на самом деле не проверяйте значение вообще – мы проверяем Личность Отказ Другими словами, мы проверяем, будь то y Аргумент относится к тому же точным объектам, что и кто-то назначен _ неважно .

Несколько заданий

Один из приятных особенностей Python, что большинство языков программирования – это несколько заданий. В его простейшей форме выглядит так:

a = b = "something"

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

some_list = ["value1", "value2"]
first, second = some_list

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

Переменная распаковка

Здание на предыдущем примере и немного дальше – мы также можем использовать Звездный выражение Распаковать элементы утекаемости произвольной длины:

first, *middle, last = [1, 2, 3, 4, 5]
# first = 1, middle = [2, 3, 4], last = 5

first, second, *rest =  [1, 2, 3, 4, 5]
# first = 1, middle = 2, rest = [3, 4, 5]

name, address, *_, email = ["John", "Some Street", "Credit Card Number", "Phone Number", "john@gmail.com"]
# name = "John", address = "Some Street", email = "john@gmail.com"

header_row, *table_rows = open("filename").read().split("\n")
# header_row -> first line
# table_rows -> list of remaining lines

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

Есть одно, что нужно знать при использовании Звездный выражение , хотя. Распаковывать с Звездный выражение Всегда создает список, даже если переменная получает нулевые значения от распаковки, что может быть приятно учитывать, что вам не нужно будет делать какую-либо проверку дополнительного типа, но также может быть немного удивительным для получения [] вместо Нет Отказ

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

geographies = {
    "EMEA": ("France", "Germany", "UK", "Sweden"),
    "LA": ("Brazil", "Argentina", "Chile", "Cuba"),

}

((geo1, (first1, *rest1)),
 (geo2, (first2, *rest2))) = geographies.items()

print(f"In {geo1} there is {first1} and {len(rest1)} more countries.")
# In EMEA there is France and 3 more countries.

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

Подменные значения

На других языках вам понадобится дополнительные переменные и 3 строки кода для обмена 2 переменными. В Python однако, есть лучший способ, как ранее показал несколько заданий:

# Bad
temp = a
a = b
b = temp

# Good
a, b = b, a

Это Super Simple и Super полезно, и это одна из тех функций, которые напоминают вам, насколько великий Python. Помимо замены переменных, это также относится к Musable Iterables (например, списки) и их индексов, которые могут быть широко видны в сортировке:

a[i-1], a[i] = a[i], a[i-1]

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

Списки обработки параллельно

Часто при работе с – например – базы данных или таблицы CSV, вы окажетесь с несколькими списками связанных данных. Это может быть несколько столбцов из таблицы базы данных, несколько связанных наборов данных и т. Д. Независимо от того, какие данные действительно есть, вы, вероятно, захотите работать с ним и обработать его параллельно. Самый простой способ сделать это в Python – использовать Zip :

countries = [...]
population = [...]
for country, pop in zip(countries, population):
    print(f"{country} has population of {pop}.")

Zip Функция принимает переменное количество списков и дает ленивый генератор, который дает кортежи, содержащие элементы из каждого из прилагаемых списков. Это отлично подходит для обработки данных, и это также очень эффективно, потому что – как я уже упоминал – генератор ленивый Таким образом, он не будет загружать целые списки в память, только текущий кортеж элементов.

При использовании этой функции вы можете поступить, чтобы понять, что она не очень велика при работе со списками с различными длинами, так как он собирается доходные значения только до тех пор, пока Кратчайший Из списков исчерпаны, что может не всегда желательно. Если вы предпочитаете потреблять значения до тех пор, пока самый длинный Из списков исчерпаны, вы можете вместо этого использовать iTertools.zip_longest , который заполнит недостающие значения с Нет или FillValue предоставляется в качестве аргумента.

Избегайте карты, фильтра и уменьшите

Python имеет много функциональных концепций программирования и функций, таких как лямбда Выражения, список поступлений, functools модуль и т. Д. Тем не менее, есть несколько, которые нахмурились многими людьми. Это карта , Уменьшить и Фильтр Отказ Хотя то, что плохо об этих функциях? Ну, есть несколько причин, но то, с которым я должен согласиться, так это то, что это обычно чище и понятнее, чтобы записать список пометки карта или Фильтр и в случае Уменьшить Код становится трудно читать при использовании с аргументом нетривиального функционала. Еще одна веская причина не любить эти функции заключается в том, что в идеале должно быть только один правильный способ сделать вещи, поэтому используйте карта , фильтр , Уменьшить или даже лямбда Когда у нас есть список поступлений?

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

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

«Единственная цель« уменьшения »- написать действительно запутанный код, который показывает, насколько вы остываете. Я просто не такой крутой. — Гвидо Ван Россум

Батареи включены

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

Есть много примеров этих «Батареи» по всей стандартной библиотеке, первый модуль, который приходит на ум, мой это Itertools который предоставляет итераторные строительные блоки. Еще один великий – functools с коллекцией функций высшего порядка, и я также должен упомянуть коллекции Модуль с очень полезными типами типа Счетчик , Дейко или NamedTuple. Просто назвать несколько.

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

«Букет» идиома

Когда вы определяете Python класс Вы, скорее всего, объявляете пару атрибутов в его __init__ метод. Вы можете объявить только один или два атрибута, но вы также можете в конечном итоге с чем-то подобным:

class Person:
    def __init__(self, first_name, last_name, age, height, weight, gender, address, ssn):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.height = height
        self.weight = weight
        self.gender = gender
        self.address = address
        self.ssn = ssn

С несколькими атрибутами в класс Это хорошо, чтобы написать их И это не будет беспокою ваш код так много, но если было 10 или около того атрибутов – как в коде выше – бы вы все еще в порядке с написанием их всех? Ну, я бы не сделал бы. Итак, чтобы избежать этого вы можете использовать так называемый “Букет” idiom. :

class Person:
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)

class Person:
    def __init__(self, **kwargs):
        vars(self).update(**kwargs)  # Alternatively use `vars()`

Вышесказанный фрагмент демонстрирует использование Я .__ Dict__ Какой словарь, который хранит все атрибуты класс (если __slots__ объявлен). Здесь мы передаем все аргументы ключевых слов конструктора к Обновление Функция, которая генерирует все атрибуты. Также возможно использовать Варс (Я) Это выглядит немного приятнее на мой взгляд.

Вы можете рассмотреть этот грязный хак, но я думаю, что это нормально использовать его, особенно если у вас есть класс (структура данных) для хранения Букет атрибутов без какой-либо реальной функциональности. Также Рэймонд Хеттинтер говорит Это нормально обновлять словарь экземпляра напрямую, поэтому есть это.

Заключение

Я определенно рекомендую использовать все вышеперечисленные идиомы и советы в вашем Python, и я считаю, что это сделает ваш код больше Питон и идиоматический . Однако нет одного ответа на “Что такое питон?” или «Что идиоматично?» И что работает для меня, может не работать для вас. Итак, используйте идиомы, чтобы сделать ваш код более читабельным, лаконичным и эффективным, а не только потому, что это идиоматический Отказ Таким же образом используйте языковые особенности Python Чтобы улучшить свой код, не просто сделать его больше Питон Отказ

Оригинал: “https://dev.to/martinheinz/writing-more-idiomatic-and-pythonic-code-cbi”