Неочевидные возможности python'a, которые можно встретить на собеседовании

Всем привет!

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

То, о чем будет рассказано, не стоит делать в своих проектах, т.к. это ухудшает читаемость и поддерживаемость кода.

Говорят, что с помощью операторов присваивания, условия и цикла можно написать сколь угодно сложную программу. Это 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]))

Спасибо за внимание. Интересных экспериментов!

3 лайка