Именование среза с использованием функции slice
Работа с множеством значений, которые заданы индексами, может быстро обернуться беспорядком — как в плане поддержки, так и в плане читабельности кода. Один из вариантов улучшения ситуации заключается в использовании констант для значений, задаваемых индексами. Но есть и более удачный способ написания качественного кода:
# ID First Name Last Name line_record = "2 John Smith" ID = slice(0, 8) FIRST_NAME = slice(9, 21) LAST_NAME = slice(22, 27) name = f"{line_record[FIRST_NAME].strip()} {line_record[LAST_NAME].strip()}" # name == "John Smith"
В этом примере можно видеть, что, дав срезам имена с помощью функции slice, и использовав эти имена при получении фрагментов строки, мы смогли избавиться от запутанных индексов. Узнать подробности об объекте slice можно с помощью его атрибутов .start, .stop и .step.
Запрос пароля у пользователя во время выполнения программы
Множеству инструментов командной строки или скриптов для работы требуется имя пользователя и пароль. Если вам придётся писать подобную программу — вы, возможно, сочтёте полезным модуль getpass:
import getpass user = getpass.getuser() password = getpass.getpass() # Выполнить некие действия...
Этот очень простой пакет позволяет запрашивать у пользователя его пароль, а также получать имя пользователя, извлекая имя, под которым он вошёл в систему. Правда, при работе с паролями стоит знать о том, что не все системы поддерживают скрытие паролей. Python постарается вас об этом уведомить. Если это произойдёт — вы увидите соответствующее предупреждение в командной строке.
Нахождение близких соответствий в строках
Теперь поговорим о немного более таинственной возможности стандартной библиотеки Python. Предположим, вы попали в ситуацию, когда вам понадобилось, задействуя концепцию наподобие расстояния Левенштейна, найти в списке слова, похожие на некую входную строку. Решить эту задачу можно с помощью модуля difflib.
import difflib difflib.get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'], n=2) # returns ['apple', 'ape']
Метод difflib.get_close_matches ищет наилучшие, «достаточно хорошие» совпадения. Первый аргумент этого метода задаёт искомую строку, второй аргумент задаёт список, в котором выполняется поиск. Этому методу можно передать необязательный аргумент n, который задаёт максимальное число возвращаемых совпадений. Ещё этот метод поддерживает необязательный именованный аргумент cutoff (по умолчанию он установлен в значение 0.6), который позволяет задавать пороговое значение для оценки совпадений.
Работа с IP-адресами
Если вам приходится писать на Python программы для работы с сетью — это значит, что вам может очень пригодиться модуль ipaddress. Одним из вариантов его использование является генерирование списка IP-адресов из диапазона адресов, заданных в формате CIDR (Classless Inter-Domain Routing, бесклассовая адресация).
import ipaddress net = ipaddress.ip_network('74.125.227.0/29') # Подходит и для работы с IPv6 # IPv4Network('74.125.227.0/29') for addr in net: print(addr) # 74.125.227.0 # 74.125.227.1 # 74.125.227.2 # 74.125.227.3 # ...
Ещё одна полезная возможность этого модуля — проверка IP-адреса на предмет принадлежности его к некоей сети:
ip = ipaddress.ip_address("74.125.227.3") ip in net # True ip = ipaddress.ip_address("74.125.227.12") ip in net # False
У модуля ipaddress есть и много других интересных возможностей, о которых я тут не рассказываю. Почитать подробности о нём можно здесь. Правда, пользуясь этим модулем, учитывайте ограничения, касающиеся его совместной работы с другими модулями, имеющими отношение к сетевому программированию. Например, нельзя использовать экземпляры IPv4Network в виде строк адреса. Подобные объекты для этого сначала надо конвертировать в строки с помощью str.
Отладка программы в командной строке
Если вы — из тех, кто не хочет пользоваться IDE и пишет код в Vim или Emacs, тогда вы, возможно, попадали в ситуацию, когда вам пригодился бы отладчик, вроде тех, что есть в IDE. И знаете что? У вас такой отладчик уже есть. Для того чтобы им воспользоваться, достаточно запустить программу с помощью конструкции вида python3.8 -i. Флаг -i позволяет, после завершения программы, запустить интерактивную оболочку. С её помощью можно исследовать переменные и вызывать функции. Это интересная возможность, но как насчёт настоящего отладчика (pdb)? Давайте поэкспериментируем со следующей простой программой, код которой находится в файле script.py:
def func(): return 0 / 0 func()
Запустим её командой python3.8 -i script.py и получим следующее:
# Скрипт дал сбой... Traceback (most recent call last): File "script.py", line 4, in <module> func() File "script.py", line 2, in func return 0 / 0 ZeroDivisionError: division by zero >>> import pdb >>> pdb.pm() # Запускаем отладчик после завершения программы > script.py(2)func() -> return 0 / 0 (Pdb)
Мы видим место программы, в котором произошёл сбой. Зададим точку останова:
def func(): breakpoint() # import pdb; pdb.set_trace() return 0 / 0 func()
Снова запустим скрипт.
script.py(3)func()
-> return 0 / 0
(Pdb) # начинаем здесь
(Pdb) step
ZeroDivisionError: division by zero
> script.py(3)func()
-> return 0 / 0
(Pdb)
В большинстве ситуаций для отладки скриптов достаточно команды print и результатов трассировки, но иногда для того, чтобы разобраться со сложным сбоем, нужно покопаться в программе и вникнуть в суть происходящего. В подобных случаях в коде задают точки останова и исследуют программу. Например — смотрят аргументы функций, вычисляют выражения, проверяют значения переменных, или, как показано выше, просто занимаются пошаговым выполнением кода. Pdb представляет собой полнофункциональную Python-оболочку. В этой оболочке можно выполнить практически всё, что угодно. В ходе работы вам пригодятся некоторые специфические команды отладчика, справку по которым можно найти здесь.
Объявление нескольких конструкторов в классе
Перегрузка функций — это одна из возможностей, весьма широко используемых в различных языках программирования, но не в Python. И даже хотя в Python нельзя перегрузить обычную функцию, мы можем пользоваться чем-то вроде перегрузки конструкторов с использованием методов класса:
import datetime class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day @classmethod def today(cls): t = datetime.datetime.now() return cls(t.year, t.month, t.day) d = Date.today() print(f"{d.day}/{d.month}/{d.year}") # 14/9/2019
В подобной ситуации вы, вместо использования методов класса, можете склониться к тому, чтобы поместить всю логику альтернативных конструкторов в __init__ и решить задачу с использованием *args, **kwargs и множества выражений if. В результате может получиться рабочий код, но этот код будет тяжело читать и поддерживать. Тут я порекомендовал бы поместить минимум логики в __init__ и выполнить все операции в отдельных методах/конструкторах. При таком подходе в нашем распоряжении окажется чистый код, с которым удобно будет работать и автору этого кода, и тому, кто этим кодом будет пользоваться.
Кэширование результатов вызова функций с помощью декоратора
Доводилось ли вам писать функции, которые выполняли какие-нибудь длительные операции чтения-записи, или достаточно медленные рекурсивные вычисления? Думали ли вы при этом о том, что таким функциям не повредило бы кэширование результатов? Кэшировать (мемоизировать) результаты вызова функции можно с помощью декоратора lru_cache из модуля functools:
from functools import lru_cache import requests @lru_cache(maxsize=32) def get_with_cache(url): try: r = requests.get(url) return r.text except: return "Not Found" for url in ["https://google.com/", "https://martinheinz.dev/", "https://reddit.com/", "https://google.com/", "https://dev.to/martinheinz", "https://google.com/"]: get_with_cache(url) print(get_with_cache.cache_info()) # CacheInfo(hits=2, misses=4, maxsize=32, currsize=4)
В этом примере мы выполняем GET-запросы, результаты которых кэшируются (кэшировано может быть до 32 результатов). Тут можно увидеть и то, что мы получаем сведения о кэше функции, воспользовавшись методом cache_info. Декоратор, кроме того, даёт в наше распоряжение метод clear_cache, применяемый для инвалидации кэша. Тут мне ещё хотелось бы отметить то, что кэширование нельзя использовать с функциями, у которых есть побочные эффекты, или с функциями, создающими мутабельные объекты при каждом вызове.
Нахождение элементов, которые встречаются в итерируемом объекте чаще всего
Нахождение в списке таких элементов, которые встречаются в нём чаще других, это весьма распространённая задача. Решить её можно, например, воспользовавшись циклом for и словарём, в котором будут собраны сведения о количестве одинаковых элементов. Но такой подход — это пустая трата времени. Дело в том, что решать подобные задачи можно с помощью класса Counter из модуля collections:
from collections import Counter cheese = ["gouda", "brie", "feta", "cream cheese", "feta", "cheddar", "parmesan", "parmesan", "cheddar", "mozzarella", "cheddar", "gouda", "parmesan", "camembert", "emmental", "camembert", "parmesan"] cheese_count = Counter(cheese) print(cheese_count.most_common(3)) # Вывод: [('parmesan', 4), ('cheddar', 3), ('gouda', 2)]
Внутренние механизмы класса Counter основаны на словаре, хранящем соответствия элементов и количества их вхождений в список. Поэтому соответствующий объект можно использовать как обычный объект dict:
print(cheese_count["mozzarella"]) # Вывод: 1 cheese_count["mozzarella"] += 1 print(cheese_count["mozzarella"]) # Вывод: 2
Кроме того, при работе с Counter в нашем распоряжении оказывается метод update(more_words), используемый для добавления к счётчику новых элементов. Ещё одна полезная возможность Counter заключается в том, что он позволяет использовать математические операции (сложение и вычитание) при работе с экземплярами этого класса.
Итоги
Думаю, что большая часть из приведённых сегодня советов вполне может использоваться теми, кто пишет на Python, практически ежедневно. Надеюсь, вы нашли среди них что-то такое, что пригодится именно вам.
Уважаемые читатели! Знаете какие-нибудь интересные приёмы Python-программирования? Если так — просим ими поделиться.
ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/485646/


Добавить комментарий