Изучая документацию 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 – это не только полезный язык, но и весьма интересный. Мы все чем-то заняты в повседневной жизни, но это не должно нам мешать лучше изучить язык ради саморазвития. Удачи! Не останавливайтесь на достигнутом!
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]