Скрытые сокровища Python

Изучая документацию Python для собственного удовольствия. Можно найти трюки. Которые обязательно ускользнут от вашего внимания в программистской рутине. Сайт proglib.io в своем переводе статьи «Hidden Gems of 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 и is_awesome в 10-й и 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

Обратите внимание. Что здесь ни один элемент не имеет длины меньше 3, т. е. оператор 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 есть очень удобный способ борьбы с этим: можно использовать подчеркивание в качестве разделителя для улучшения читабельности. Таким образом 1_000_000 будет интерпретироваться как целочисленное значение:

a = 3250
b = 67_543_423_778 print(type(a))
print(type(b))
print(type(a)==type(b))
<class 'int'>
<class 'int'>
True

4. eval() и exec()

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

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

Вы даже можете передавать динамически созданные строки этим функциям. Например, можно создать 1000 переменных с именами x_0, x_1,…. X_999 без необходимости вручную записывать каждое из этих объявлений переменных в код. Кому-то подобный способ покажется совершенно бессмысленным. Но это не так.

В более широком контексте программирования. А не только в Python. Использование eval/exec дает крутой результат. Потому что они позволяют писать динамический код. Использующий доступную в рантайме информацию для решения проблем. Которых может не быть при компиляции. Exec – это почти встроенный в Python интерпретатор. Если у вас есть трудная задача. Один из способов решения – использовать именно его. Узнать больше об этих инструментах можно из другой статьи.

5. Ellipsis

Ellipsis или ... – это встроенная константа Python. Аналогичная встроенным константам типа None, True. False и т. д. Она используется различными способами. Разберем некоторые.

5.1 Плейсхолдер

Подобно pass. Ellipsis может использоваться в качестве плейсхолдера. Когда код не полностью написан. Но требует некоторого заполнения:

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

5.2 Альтернатива «None»

None применяется. Когда вы хотите обозначить пустой инпут или return. Но иногда ни один из них не подходит. И в этом случае заполнителем может служить многоточие:

# вычисление nth odd
def nth_odd(n): if isinstance(n. Int): return 2 * n - 1 else: return None # вычисление исходных данных n в nth odd
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() вычисляет энное нечетное число. Функция original_num() вычисляет исходное число 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 использует многоточие для среза массива. Следующий код показывает два эквивалентных способа нарезки массива 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]

Таким образом ... сообщает о наличии стольких ‘:‘, сколько необходимо.

Заключение

В статье мы рассмотрели следующие интересные особенности. Которые можно использовать в следующих целях:

  • атрибуты функций – для присваивания атрибутов функциям точно так же. Как и объектам;
  • циклы for-else – для отслеживания того. Был ли цикл выполнен без оператора break;
  • разделители int – для удобочитаемости больших целочисленных значений. Вроде 32_534_478;
  • eval() и exec() – для считывания строковых значений как обычного кода Python с последующим запуском;
  • ellipsis – для применения универсальной встроенной константы.

Python – это не только полезный язык. Но и весьма интересный. Мы все чем-то заняты в повседневной жизни. Но это не должно нам мешать лучше изучить язык ради саморазвития. Удачи! Не останавливайтесь на достигнутом!