Сокрытые драгоценности Python

?v=1

Особенности Python,  о которых я даже не подозревал

В последнее время у меня появилось новое хобби — чтение документации Python просто для удовольствия! Когда вы читаете на досуге, то, как правило, замечаете интересные «лакомые кусочки», которые в противном случае пропустили бы. Итак, вот перечень «кусочков», которые заставили меня сказать:

О! Вы можете сделать это на Python?

1. Атрибуты функций

Подобно тому, как вы можете устанавливать атрибуты классов  и объектов, вы также можете устанавливать атрибуты функций.

def func(x):
    intermediate_var = x**2 + x + 1

    if intermediate_var % 2:
        y = intermediate_var ** 3
    else:
        y = intermediate_var **3 + 1

    # setting attributes here
    func.optional_return = intermediate_var
    func.is_awesome = 'Yes, my function is awesome.'

    return y

y = func(3)
print('Final answer is', y)

# Accessing function attributes
print('Show calculations -->', func.optional_return)
print('Is my function awesome? -->', func.is_awesome)

Мы установили атрибуты «optional_return» в строке 10 и «is_awesome» в строке 11. Мы получили доступ к этим атрибутам вне функции позднее в строках 19 и 20. Результат кода:

Final answer is 2197
Show calculations --> 13
Is my function awesome? --> Yes, my function is awesome.

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

2. Цикл for-else

В Python вы можете добавить условие else в цикл for. Условие else будет срабатывать только в том случае, если во время выполнения в теле цикла не встретится оператор break.

my_list = ['some', 'list', 'containing', 'five', 'elements']

min_len = 3

for element in my_list:
    if len(element) < min_len:
        print(f'Caught an element shorter than {min_len} letters')
        break
else:
    print(f'All elements at least {min_len} letters long')

All elements at least 3 letters long

Обратите внимание, что else имеет отступ на уровне for, а не на уровне if. Здесь ни один элемент не имеет длины короче трёх. Таким образом, никогда не будет встречен оператор break. Следовательно, условие else будет запущено (после выполнения цикла for) и выведется результат, указанный выше.

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

my_list = ['some', 'list', 'containing', 'five', 'elements']

min_len = 3

no_break = True
for element in my_list:
    if len(element) < min_len:
        print(f'Caught an element shorter than {min_len} letters')
        no_break = False
        break

if no_break:
    print(f'All elements at least {min_len} letters long')

Думаю, это полезно знать.

3. Разделители для int

Трудно визуально различить целые числа подобные 10000000 и 100000000 (они даже разные числа?). Мы не можем использовать запятые здесь, в Python, подобному тому, как мы используем их в английском языке, потому что Python интерпретирует это как кортеж из нескольких целых чисел.

У Python есть очень удобный способ справиться с этим: мы можем использовать подчеркивание как разделитель для улучшения читабельности. Таким образом, 1_000_000 будет интерпретироваться как целое число.

a = 3250
b = 67_543_423_778

print(type(a))
print(type(b))
print(type(a)==type(b))



True

4. eval () и exec ()

В Python есть возможность динамически считывать строку и обрабатывать её как часть Python кода. Это достигается использованием функций eval () и exec () («eval» для вычисления выражений; и «exec» для выполнения операторов).

a = 3

b = eval('a + 2')
print('b =', b)

exec('c = a ** 2')
print('c is', c)

b = 5
c is 9

В третьей строке функция eval () считывает входную строку как выражение Python, оценивает её и присваивает результат переменной b. В строке 6 функция exec () считывает входную строку как оператор Python и исполняет её.

Вы даже можете динамически передать созданные строки этим функциям. Например, вы можете создать 1000 переменных с именами х_0, х_1, …, х_999 без записи каждого объявления этих переменных вручную в коде. Может показаться, что это совершенно бессмысленно, но это не так.

В целом, в более широком контексте программирования (не только в отношении Python) использование eval/exec невероятно сильно, поскольку позволяет вам писать динамический код, который использует информацию, доступную во время выполнения, для решения проблем, которые не могут быть даже выражены во время компиляции. […] exec — это буквально интерпретатор Python, встроенный в Python, поэтому, если у вас есть особенно сложная проблема для разрешения, один из способов её решить — написать программу для *написания программы для её решения*, затем использовать exec для запуска этой второй программы.

Вы можете  прочитать об этом в замечательном объяснении Стивена Д«Апрано.

5. Многоточие (Ellipsis)

Многоточие (или »…») — это встроенная константа Python, аналогичная таким встроенным константам как None, True, False и т.д. Её можно использовать по-разному, например, следующими способами (но, разумеется, не ограничиваясь ими):

5.1. Замена для ненаписанного кода

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

def some_function():
    ...
    
def another_function():
    pass

5.2. Альтернатива NONE

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

# calculate nth odd number
def nth_odd(n):
    if isinstance(n, int):
        return 2 * n - 1
    else:
        return None


# calculate the original n of nth odd number
def original_num(m=...):
    if m is ...:
        print('This function needs some input')
    elif m is None:
        print('Non integer input provided to nth_odd() function')
    elif isinstance(m, int):
        if m % 2:
            print(f'{m} is {int((m + 1)/2)}th odd number')
        else:
            print(f'{m} is not an odd number')


original_num()

a = nth_odd(n='some string')
original_num(a)

b = nth_odd(5)
original_num(b)

original_num(16)

Функция  nth_odd () вычисляет n-ое нечетное число, c учетом n. Функция original_num () вычисляет исходное число n, учитывая n-ое нечетное число. Здесь None — один из ожидаемых входных параметров функции original_num (), так что мы не можем использовать его как замену по умолчанию для аргумента m. Результат кода:

This function needs some input
Non integer input provided to nth_odd () function
9 is 5th odd number
16 is not an odd number

5.3.  Нарезка массива в NumPy

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

import numpy as np

a = np.arange(16).reshape(2,2,2,2)

print(a[..., 0].flatten())
print(a[:, :, :, 0].flatten())

[ 0 2 4 6 8 10 12 14]
[ 0 2 4 6 8 10 12 14]

Таким образом,»…» показывает, что существует столько  »:», сколько необходимо.

Логическое значение Многоточия

В отличие от None (Логическое значение которого False), логическое значение Многоточия —  True.

TL; DR

Итак, я обнаружил следующие интересные особенности.

Атрибуты Функций: присвоение атрибутов функциям, как и объектам.

Цикл for-else: отслеживание, был ли цикл выполнен без оператора break.

Разделители для int: 32_534_478 — это int.

eval () и exec (): читайте строки как код Python и запустите его.

Многоточие: универсальная встроенная константа.

Напутствие

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

© Habrahabr.ru