Fork me on GitHub

трюки с Python3

Объединение строк

В программном коде нередко приходится сталкиваться с конкатенацией строк при помощи знака сложения. Создание строки из списка нескольких подстрок удобнее осуществить при помощи строкового метода join:

a = ["Python", "-", "прекрасный", "язык."]
print(" ".join(a))

Пример посложнее с методом join – конвертирование списка чисел в строку:

numbers = [1, 2, 3, 4, 5]
print(', '.join(map(str, numbers)))
1, 2, 3, 4, 5

Удаление дубликатов в списке

Среди регулярно используемых трюков в Python – преобразование списка во множество и обратно в список для удаления повторяющихся элементов списка:

items = [2, 2, 3, 3, 1]
print(list(set(items)))

items = [2, 2, 3, 3, 1]
print(list(set(items)))
[1, 2, 3]

Но множества – это неупорядоченные последовательности. Часто стоит задача сохранить порядок следования элементов. Для этого удобно воспользоваться типом данных OrderedDict из модуля collections:

items = [2, 2, 3, 3, 1]

from collections import OrderedDict
print(list(OrderedDict.fromkeys(items).keys()))

Назначение переменных и функций по условию

Иногда элементы if настолько просты, что кажется излишним тратить на них строки. В этом случае имеет смысл применить тернарный оператор if/else:

A = Y if X else Z

Интерпретатор выполняет выражение Y, если объект X – истина, и Z, если X – ложь. Не злоупотребляйте этим выражением, если X, Y, Z имеют сложную форму записи.

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

def product(a, b):
return a * b

def summarize(a, b):
return a + b

c = True

print((product if c else summarize)(3, 4))
12

Сортировка словаря по значениям

Распространена практика использования словарей в качестве таблиц для хранения данных. Сортировка данных словаря по значениям ключей, а не самим ключам, нередко ставит в тупик. Задача решается довольно просто при помощи соответствующего аргумента функции сортировки:

d = {'яблоки':40, 'апельсины':80, 'бананы':70}
print(sorted(d, key=d.get))
['яблоки', 'бананы', 'апельсины']

Создание объектов, поддерживающих выражение with

Все знают о том, как, например, открыть файл, или, возможно, как установить блокировку с использованием оператора with. Но можно ли самостоятельно реализовать механизм управления блокировками? Да, это вполне реально. Протокол управления контекстом исполнения реализуется с использованием методов enter и exit:

class Connection:
 def __init__(self):
  ...
 def __enter__(self):
  # Инициализируем соединение...

 def __exit__(self, type, value, traceback):
  # Закрываем соединение...

with Connection() as c:
 # __enter__() executes
 ...
 # conn.__exit__() executes

Это — наиболее распространённый способ реализации возможностей менеджера контекста в Python, но то же самое можно сделать и проще:

from contextlib import contextmanager

@contextmanager
def tag(name):
 print(f"<{name}>")
 yield
 print(f"</{name}>")

with tag("h1"):
 print("This is Title.")

Очистка входных строковых данных

Задача очистки данных, вводимых пользователем, актуальна практически для любой программы. Часто такая обработка входных данных сводится к преобразованию символов в верхний или нижний регистр. Иногда данные можно очистить с помощью регулярного выражения. Но в случаях, когда задача усложняется, можно применить более удачный способ её решения. Например — такой:

user_input = "This\nstring has\tsome whitespaces...\r\n"

character_map = {
 ord('\n') : ' ',
 ord('\t') : ' ',
 ord('\r') : None
}
user_input.translate(character_map)  # This string has some whitespaces... "

Здесь можно видеть, как пробельные символы "\n" и "\t" заменяются на обычные пробелы, и как символ "\r" удаляется из строки полностью. Это — простой пример, но мы можем его расширить, создавая большие таблицы переназначения символов с использованием пакета unicodedata и его функции combining(). Такой подход позволяет убирать из строк всё то, что там не нужно.

Пропуск начала итерируемого объекта

Иногда нужно работать с файлом, который, как заранее известно, начинается с некоторого числа ненужных строк — вроде строк с комментариями. Для того чтобы пропустить эти строки, можно, снова, прибегнуть к возможностям itertools:

string_from_file = """
// Author: ...
// License: ...
//
// Date: ...

Actual content...
"""

import itertools

for line in itertools.dropwhile(lambda line: line.startswith("//"), string_from_file.split("\n")):
 print(line)

Этот код выдаёт лишь строки, находящиеся после блока комментариев, расположенного в начале файла. Такой подход может быть полезен тогда, когда нужно отбросить лишь элементы (в нашем случае — строки) в начале итерируемого объекта, но при этом точное их количество неизвестно.

Функции, поддерживающие только именованные аргументы (kwargs)

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

def test(*, a, b):
 pass

test("value for a", "value for b")  # TypeError: test() takes 0 positional arguments...
test(a="value", b="value 2")  # А так - работает...

Запрос пароля у пользователя во время выполнения программы

Множеству инструментов командной строки или скриптов для работы требуется имя пользователя и пароль. Если вам придётся писать подобную программу — вы, возможно, сочтёте полезным модуль getpass:

import getpass

user = getpass.getuser()
password = getpass.getpass()

# Выполнить некие действия...

Этот очень простой пакет позволяет запрашивать у пользователя его пароль, а также получать имя пользователя, извлекая имя, под которым он вошёл в систему. Правда, при работе с паролями стоит знать о том, что не все системы поддерживают скрытие паролей. Python постарается вас об этом уведомить. Если это произойдёт — вы увидите соответствующее предупреждение в командной строке.

Отладка программы в командной строке

Если вы — из тех, кто не хочет пользоваться IDE и пишет код в Vim или Emacs, тогда вы, возможно, попадали в ситуацию, когда вам пригодился бы отладчик, вроде тех, что есть в IDE. И знаете что? У вас такой отладчик уже есть. Для того чтобы им воспользоваться, достаточно запустить программу с помощью конструкции вида python3.8 -i. Флаг -i позволяет, после завершения программы, запустить интерактивную оболочку. С её помощью можно исследовать переменные и вызывать функции. Это интересная возможность, но как насчёт настоящего отладчика (pdb)?

Работа с 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.

Использование функции enumerate()

Если вам нужно адекватным образом отслеживать «индекс элемента» в for-цикле Python, то для этого может подойти функция enumerate(), которая позволяет «пересчитать» итерируемый объект. Её можно использовать не только для обработки списков, но и для работы с другими типами данных — со строками, кортежами, словарями.

Эта функция принимает два аргумента: итерируемый объект и необязательное начальное значение счётчика.

Если начальное значение счётчика enumerate() не передаётся — оно, по умолчанию, устанавливается в 0. Функция создаёт объект, генерирующий кортежи, состоящие из индекса элемента и самого этого элемента.

scores = [54,67,48,99,27]
for i, score in enumerate(scores):
   print(i, score)

Такой код получился гораздо чище кода из предыдущего примера. Мы ушли от работы со списком индексов, мы перебираем сами значения, получая к ним прямой доступ в цикле for, и видим значения, с которыми работаем, в объявлении цикла.

Вот одна приятная возможность, которая понравится тем, кому нужно выводить нумерованные списки так, чтобы номер первого элемента был бы не 0, в соответствии с его индексом, а 1. Обычно для того, чтобы это сделать, приходится менять выводимый номер элемента. Например — так: i + 1. При использовании же функции enumerate() достаточно передать ей, в качестве второго аргумента, то число, с которого нужно начинать нумерацию. В нашем случае — 1:

scores = [54,67,48,99,27]
for i, score in enumerate(scores, 1):
   print(i, score)
"""
1 54
2 67
3 48
4 99
5 27
"""

Использование next

Функция enumerate возвращает итератор. Используя функцию next можно получить кортежи.

a = [2, 5, 3, 4, 1, 5]
b = enumerate(a)
c = next(b)
print(c)
print(type(c))
print(next(b))

(0, 2)
<class 'tuple'>
(1, 5)

Если при очередном вызове next, следующий элемент будет отсутствовать, возникает исключение StopIteration.

social