python

6 хитростей Python, о которых никто не рассказывает новичкам

  • пятница, 14 мая 2021 г. в 00:41:03
https://habr.com/ru/company/ruvds/blog/556160/
  • Блог компании RUVDS.com
  • Python
  • Программирование


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

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



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

Код примеров можно найти в GitHub-репозитории, ссылку на который автор статьи выдаёт тем, кто подписался на рассылку проекта worldindev.ck.page.

1. Выбор случайного элемента из последовательности элементов


Пакет random, входящий в состав стандартной библиотеки, включает в себя множество полезных функций. Среди них хочется особо отметить функцию random.choice(seq).

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

import random as r
my_list = [1, 2, 3, "go"]
print(r.choice(my_list))

# Элемент, выбранный случайным образом

▍Практический пример


Рассмотрим функцию book_picker(), которая, получив список книг, случайным образом выбирает одну из них, удаляет её из списка, и возвращает результат в виде строки.

# Импортируем только ту функцию, которая нам нужна
from random import choice

def book_picker(books):
    book_choice = choice(books)
    books.remove(book_choice)    
    return f"You picked {book_choice}"

books = ["Harry potter", "Don Quixote", "Learn Python by Daniel Diaz", "Dracula"]

print(book_picker(books)) # Книга, выбранная случайным образом

print(books) # Книги, которые остались в списке

▍Ограничения и исключения


Если попытаться передать функции random.choice(seq) неиндексируемую последовательность, например — словарь, множество или значение числового типа — возникнет ошибка.

# Попытка работы со словарём
import random as r
scores = {"Jhon": 4, "Ben": 3, "Diana": 5}

print(r.choice(scores)) # Ошибка KeyError

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

# Попытка работы с пустой последовательностью
import random as r
empty_list = []

print(r.choice(empty_list)) # Ошибка IndexError

2. Распаковка элементов с помощью *


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

my_list = [1, 2, 3, 5, 7]

for i in my_list:
   print(i, end=" ") # 1 2 3 5 7

Хотя этот код и решает задачу, он, так сказать, не особенно «питонистический». Гораздо проще будет решение, в котором используется оператор распаковки — *:

my_list = [1, 2, 3, 5, 7]

print(*my_list) # 1 2 3 5 7

Обратите внимание на то, что оператор распаковки всегда размещают слева от имени переменной. Этот оператор как бы предлагает Python «разобрать на части» переменную итерируемого типа.

Итерируемой сущностью считается любая последовательность, которую можно перебрать в цикле for. Если нужно узнать о том, является ли некий тип итерируемым — можно воспользоваться функцией iter().

print(iter("This is a string")) # Сообщение об объекте str_iterator

print(iter(["this", "is", "a", "list"])) # Сообщение об объекте list_iterator

print(iter(1))
# Выдаётся сообщение об ошибке
# TypeError: 'int' object is not iterable

▍Назначение результатов работы оператора распаковки переменным


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

string = "Let's learn Python"

# Мы собираемся сохранить результат распаковки строки в var1
var1 = [*string]

print(var1)
# ['L', 'e', 't', "'", 's', ' ', 'l', 'e', 'a', 'r', 'n', ' ', 'P', 'y', 't', 'h', 'o', 'n']

Возможно, вам покажется непонятной конструкция [*iterable]. Поэтому давайте с ней разберёмся.

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

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

another_str = "The * operator"

# Результаты планируется представить в виде списка
var2 = [*another_str]

print(type(var2)) # list

# Результаты планируется представить в виде кортежа
# При описании кортежа в конце ставится запятая
var3 = (*another_str,)

print(type(var3)) # tuple

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

bad_variable = *"Bad String"
# SyntaxError

3. Использование set для оптимизации различных операций


В соответствии с документаций по Python, класс set([iterable]) возвращает новый объект set (множество), созданный на основе итерируемого объекта.

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

▍Практический пример


Вот функция, которая удаляет из списка дубликаты элементов и возвращает отсортированный список:

def eliminate_duplicates(lst):
    """
    Возвращает отсортированный список, не содержащий дубликатов
    """ 
    new_list = list(set(lst)) 

    new_list.sort()    

    return new_list

list1 = [25, 12, 11, 4, 12, 12, 25]

print(eliminate_duplicates(list1))

4. Просмотр атрибутов и методов класса в интерпретаторе Python


Функция dir() возвращает список атрибутов и методов класса. Эту полезную возможность можно использовать для того чтобы получать подобные списки для различных классов при работе в интерпретаторе.

-> $ python 
string = "A string"

print(dir(string))

# ['__add__', .....,'upper', 'zfill']

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

▍Практический пример


Больше всего пользы функция dir() может принести при изучении пакетов сторонних разработчиков. Она позволяет получить необходимые сведения не выходя из терминала:

-> $ python

from django.views import View

print(dir(View))

# ['__class__', '__delattr__', .... 'setup']

5. Операции со срезами


Срезы в Python — это не более чем механизм, предназначенный для работы с определёнными частями последовательностей. Срезы позволяют решать много интересных задач.

▍Инвертирование последовательностей


# Инвертирование списка
lst = ["Fun", "is", "Programming"]

lst = lst[::-1]

print(lst) # ['Programming', 'is', 'Fun']

# Инвертирование строки

string = "Dog running on the park"

string = string[::-1]

print(string) # krap eht no gninnur goD

▍Практический пример


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

def cutoff(seq, index):
    if not len(seq) > index:
        return "Sorry the index is bigger than the sequence"

    return seq[:index]

long_string = "This is a long description of a blog post about Python and technology"

print(cutoff(long_string, 15))
# This is a long 

print(cutoff(long_string, 70))
# Sorry the index is bigger than the sequence

6. Вызов отладчика 10-символьной командой


Функцией breakpoint() можно пользоваться в Python 3.6+. Она инициирует запуск сессии pdb.set_trace().

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

n_odds = 0

for i in range(1, 14, 2):
    # Проверка значения i в каждой итерации
    breakpoint()
    # Если это условие выполняется - значит, нас что-то не устраивает
    if i % 2 == 0:
        n_odds += 1

print(n_odds)



Вызов отладчика

Итоги


Вот что вы узнали из этого материала:

  • Как выбрать случайный элемент из последовательности.
  • Как распаковывать элементы с помощью оператора *.
  • Как пользоваться возможностями множеств для эффективной борьбы с дубликатами элементов.
  • Как просматривать списки методов и атрибутов классов, не выходя из интерпретатора Python.
  • Как пользоваться различными возможностями срезов.
  • Как, пользуясь функцией breakpoint(), вызывать отладчик.

Знаете ли вы о Python что-то такое, о чём редко рассказывают новичкам?