Всем привет!
В этой статье будут рассмотрены некоторые неочевидные особенности питона, с которыми можно столкнуться на собеседовании. Питон в целом пропагандируется как язык с простым и лаконичным синтаксисом, что верно. И тем не менее, погружаясь в его специфику, можно открыть для себя питон с неожиданной стороны. На собеседованиях периодически дают задачи, требующие подобного знания. Либо можно самому козырнуть, озадачив собеседующего, ибо не каждый разработчик в посведневной работе задумывается о вещах, о которых пойдет речь.
То, о чем будет рассказано, не стоит делать в своих проектах, т.к. это ухудшает читаемость и поддерживаемость кода.
Говорят, что с помощью операторов присваивания, условия и цикла можно написать сколь угодно сложную программу. Это 3 основных кита, на которых базируются программы. В питоне эти операции можно выполнять как явно, о чем знают все, кто пишет на питоне; так и неявно, с чем также сталкиваются многие, но несильно вдумываясь, к каким возможностям это может привести.
В питоне lambda
подразумевает однострочное выражение. Используя неявные операции присваивания, условия и цикла, через lambda
можно представить любую многострочную функцию любой сложности. Выглядить это будет однозначно нечитаемо, но работать будет. Н-р функция вида:
def f():
x = 'string'
if x.endswith('g'):
x = x[:-1]
r = ''
for i in xrange(len(x)):
if x[i] != 'i':
r += x[i]
return r
f() # -> strn
Может быть представлена как самовыполняющая lambda
:
(lambda: ([x for x in ['string']], x.endswith('g') and [x for x in [x[:-1]]], [r for r in ['']], [x[i] != 'i' and [r for r in [r+x[i]]] for i in xrange(len(x))], r)[-1])()
Здесь многострочность имитируется путем создания tuple
-объекта, присваивание осуществляется при итерировании в list comprehension
, условия - через логические выражения and/or
, цикл - также через list comprehension
.
Рассмотрим подробно реализацию присваивания и условия, на циклах останавливаться не буду, т.к. тема list comprehension
широко освещена в интернет-ресурсах.
Присваивание
- явный вариант -
a = 1
- неявный вариант -
[a for a in [1]]
Создается итерируемый объект типа list
, содержащий один элемент. Во время итерирования происходит присвоение значения переменной a
, к которой можно обращаться.
Условие
Сейчас в питоне существует тернарный оператор - b if a else c
. Его можно также сымитировать через операции and/or/not
(и раньше так и делали). Правильными вариантами являются (a and [b] or [c])[0]
или (b, c)[not a]
, неправильным - a and b or c
. Неправильность обусловлена тем, что объекты вида None
, 0
, ''
в логических операциях питон толкует как False
. Н-р:
a = True
b = 0
c = ''
(a and [b] or [c])[0] # -> 0
(b, c)[not a] # -> 0
a and b or c # -> ''
Использовать эти тонкости можно в таких задачах как:
1) Удалить повторяющиеся символы в строке через list comprehension
x = "tteesstt"
a) сравнивать символ с предыдущим в исходной строке
''.join(['' if i and j == x[i-1] else j for i,j in enumerate(x)])
б) хранить предыдущий символ во временной переменной
''.join([('' if i == a else i, [a for a in [i]])[0] for a in [''] for i in x])
''.join([('' if i == a.pop() else i, a.append(i))[0] for a in [['']] for i in x])
в) сравнивать символ с последним в формируемой строке
[(not r.endswith(i) and [r for r in [r+i]], r)[-1] for r in [''] for i in x][-1]
г) через свертку
reduce(lambda a, b: a if a.endswith(b) else a + b, x)
2) Вывести последовательность Фибоначчи через list comprehension
n = 20
[(lambda: (l[-1], l.append(l[-1] + l[-2]))[0])() for l in [[1, 1]] for x in xrange(n)]
[(l[-1], l.append(l[-1] + l[-2]))[0] for l in [[1, 1]] for x in xrange(n)]
[l.append(l[-1] + l[-2]) or l for l in [[1, 1]] for x in xrange(n)][0]
reduce(lambda a, b: a + [a[-1] + a[-2]], xrange(n), [1, 1])
reduce(lambda a, b: a.append(a[-1] + a[-2]) or a, xrange(n), [1, 1])
3) Запустить бесконечный цикл через list comprehension
[a.append(b) for a in [[None]] for b in a]
4) Определить, присутствуют ли в массиве элементы, отличающиеся между собой на единицу
x = [3, 5, 7, 9]
any(i and j - sorted(x)[i-1] == 1 for i, j in enumerate(sorted(x)))
any(i and j - y[i-1] == 1 for y in [sorted(x)] for i, j in enumerate(y))
any(i and j - x[i-1] == 1 for i, j in enumerate((x.sort(), x)[-1]))
Спасибо за внимание. Интересных экспериментов!