company_banner

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

Автор оригинала: Daniel Diaz
  • Перевод
В наши дни 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 что-то такое, о чём редко рассказывают новичкам?


RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR10

Комментарии 24

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

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

      0
      Или когда нужно множество уникальных элементов. Если такое есть, то нужно завести сет под это.
      +12

      «Не рассказывает»? А документация им это тоже не рассказывает?

        +8

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

          0
          Но статью тоже прочитать надо… Только это какие-то обрывочные знания выходят.
          0
          Что такое «документация»? Гугол, стекоферфлоу и сразу писать код!
          Вы бы ещё книгу (большая толстая газета) посоветовали бы!
            0
            Для Python'а документация не такая уж и толстая (или как сейчас более корректно это называть?), да и достаточно занимательная. Я в своё время освоил Python только прочитав документацию (в то время это был 1.6.1).
            Если статья оказалась интересной, то документация будет как увлекательное детективное чтиво.
          +9

          7-я хитрость на Python: представление больших чисел в удобочитаемом виде

          a=663_456_987 
          print(type(a)) 
          <class 'int'>
          print(a) 
          663456987

          Возможно, ниже еще десяток "хитростей в python" подвезут.

            0

            А так же в Перле, JS и ПХП:

            php > $a = 663_456_987;

            php > print(gettype($a));

            integer

            php > print($a);

            663456987

              0

              Блин, как это работает то?

              >>> a = str(654_987_123)
              >>> type(a)
              <class 'str'>
              >>> print(a)
              654987123
              +2
              С оператором * можно делать unzip:
              a = [1, 2, 3]
              b = [4, 5, 6]
              ab = zip(a, b)     # [(1, 4), (2, 5), (3, 6)]
              a1, b1 = zip(*ab)  # (1, 2, 3), (4, 5, 6)
              a2, b2 = zip(*ab)  # в Python 2 работало, в Python 3 ошибка

              (тут еще следует помнить, что в третьем Питоне zip выдает не список, а генератор, и после произведенной над ним операции zip(*ab) он будет пустым, т. е., например, повторить операцию над тем же объектом не получится)
                +2
                Никто не рассказывает, кроме любого учебника.
                  +1
                  Кликбейт заголовок на хабре?
                  даладно.жпг
                    +2
                    Назначение результатов работы оператора распаковки переменным

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

                    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]. Поэтому давайте с ней разберёмся.


                    Рубрика «Вредные советы». Если вдруг Вам по какой-либо причине захочется воспользоваться оператором * для создания итерируемого объекта другого типа на основе существующего, то нужно просто воспользоваться конструктором нужного типа:

                    var1 = list(string)


                    Вместо какой-то нечитаемой конструкции, проблему которой сам же автор признает:
                    «Возможно, вам покажется непонятной конструкция [*iterable].»

                    Конечно же не понятно, что сделать-то хотели? Конвертировать строку в список? Ну так и пишите! Вместо list может быть tuple, deque, set или другие коллекции, поддерживающие инициализацию из итерируемого объекта.
                      +1
                      … завоёвывает сердца… простотой синтаксиса.

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

                        Вот за это спасибо. Для использования ipdb нужно прописать

                        export PYTHONBREAKPOINT=ipdb.set_trace
                          +3
                          var3 = (*another_str,)

                          Это плохочитаемый код, лучше явно:

                          var3 = tuple(another_str)
                            0

                            Set, под капотом, использует хеш-таблицы, т. е.

                            x = {1, 2, 3}
                            3 in x

                            будет выполняться за O(1), в отличие от

                            x = [1, 2, 3]
                            3 in x

                            которое будет выполняться за O(n)

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

                              sorted(set(lst))


                              А не тот императивный код с созданием списка в отдельную переменную.

                                –1
                                Функция dir() возвращает список атрибутов и методов класса.

                                А функция help() возвращает документацию по атрибутам и методам. Удивительное рядом!

                                  0
                                  Кто же еще расскажет зумерам про интроспекцию? Никто, кроме нас! :)

                                  Документацию то они не читают.
                                  +2

                                  ценность подобных статей в том, что они порой порождают в комментариях более ценные дискуссии и советы ;)

                                    0
                                    КГ/АМ
                                    В книгах по Питону это всё написано, если их, конечно, читать, что и должны делать новички. Кроме места про отладчик, которое нафиг не нужно.
                                      +2
                                      Хитрость для тех, кто не хочет прочитать Лутца первый том

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

                                      Самое читаемое