Вчера мы предложили вам довольно хитрую задачу, которая требовала внимательного подхода. Несмотря
Вчера мы предложили вам довольно хитрую задачу, которая требовала внимательного подхода. Несмотря на её видимую простоту, код оказался обманчиво сложным. Проголосовало 23 человека, и только 4 из них справились правильно. Разберем всё подробно.
Задача:
Вы работаете над обработкой данных из внешнего источника. В процессе обработки требуется отфильтровать данные на основе сложного условия. На просторах StackOverflow вы нашли следующий код для проверки логики.
Внимательно изучите код и ответьте: что выведет этот код?
Код задачи:
from typing import Any
def filter_data(
data: list[Any], condition: Any
) -> list[Any]:
def matches_condition(item):
nonlocal condition
if callable(condition):
condition = item
return False
return condition == item
return [
item
for item in data
if matches_condition(item)
]
data = [1, 2, 3, 4, 5]
condition = lambda x: x > 3
result = filter_data(data, condition)
print(result)
Разбор:
На первый взгляд задача кажется достаточно простой: нужно отфильтровать список data, основываясь на переданном условии condition. Однако несколько деталей делают этот код действительно коварным.
1. Что делает функция `matches_condition`?
- Проверяет, является ли condition вызываемым объектом (например, функцией).
- Если condition — вызываемый объект, оно перезаписывается текущим элементом item, а функция возвращает False.
- Если condition — не вызываемый объект, то возвращается результат сравнения condition == item.
2. Что происходит внутри списка?
Список создается с помощью вхождения:
[item for item in data if matches_condition(item)]
Для каждого элемента item из data вызывается matches_condition.
3. Перезапись переменной `condition`:
При первом вызове matches_condition, condition — это функция (lambda). Для первого элемента списка (1) происходит следующее:
- condition заменяется на 1.
- matches_condition возвращает False.
4. Дальнейшие итерации:
Начиная со второго элемента списка, condition уже не является функцией. Теперь это число, и на каждой итерации проверяется равенство condition == item.
Таким образом:
- На первом шаге 1 != 2, проверка завершается.
- На втором шаге 1 != 3.
- И так далее.
Что здесь происходит?
1. `condition` на старте содержит lambda-функцию, определяющую условие: x > 3.
2. `nonlocal condition` позволяет изменять значение переменной condition, объявленной в функции-родителе, прямо во время работы функции.
3. Логика проверки в `matches_condition`:
- Если condition — функция (то есть callable), она заменяется на текущий элемент item, а элемент исключается из итогового списка (возвращается False).
- Если condition уже не функция, проверяется равенство condition == item.
4. Итоговый процесс фильтрации:
Каждый элемент списка проверяется с помощью matches_condition. Поскольку condition меняется уже на первом шаге, последующие проверки будут отличаться от ожидаемого поведения.
Верный ответ: []
Почему ответ — пустой список?
Ключевую роль играет изменение переменной `condition` на первом шаге. Изначально это была лямбда-функция, которая после первой итерации заменяется на число. В итоге фильтрация ломается: condition больше не проверяет изначальное условие x > 3, а просто сравнивает элементы с числом 1. Так как ни один элемент не равен 1, результатом становится пустой список.
Всегда обращайте внимание на использование замыканий, изменяемых переменных и ключевых слов, таких как nonlocal. Они могут существенно повлиять на выполнение программы.
Задача:
Вы работаете над обработкой данных из внешнего источника. В процессе обработки требуется отфильтровать данные на основе сложного условия. На просторах StackOverflow вы нашли следующий код для проверки логики.
Внимательно изучите код и ответьте: что выведет этот код?
Код задачи:
from typing import Any
def filter_data(
data: list[Any], condition: Any
) -> list[Any]:
def matches_condition(item):
nonlocal condition
if callable(condition):
condition = item
return False
return condition == item
return [
item
for item in data
if matches_condition(item)
]
data = [1, 2, 3, 4, 5]
condition = lambda x: x > 3
result = filter_data(data, condition)
print(result)
Разбор:
На первый взгляд задача кажется достаточно простой: нужно отфильтровать список data, основываясь на переданном условии condition. Однако несколько деталей делают этот код действительно коварным.
1. Что делает функция `matches_condition`?
- Проверяет, является ли condition вызываемым объектом (например, функцией).
- Если condition — вызываемый объект, оно перезаписывается текущим элементом item, а функция возвращает False.
- Если condition — не вызываемый объект, то возвращается результат сравнения condition == item.
2. Что происходит внутри списка?
Список создается с помощью вхождения:
[item for item in data if matches_condition(item)]
Для каждого элемента item из data вызывается matches_condition.
3. Перезапись переменной `condition`:
При первом вызове matches_condition, condition — это функция (lambda). Для первого элемента списка (1) происходит следующее:
- condition заменяется на 1.
- matches_condition возвращает False.
4. Дальнейшие итерации:
Начиная со второго элемента списка, condition уже не является функцией. Теперь это число, и на каждой итерации проверяется равенство condition == item.
Таким образом:
- На первом шаге 1 != 2, проверка завершается.
- На втором шаге 1 != 3.
- И так далее.
Что здесь происходит?
1. `condition` на старте содержит lambda-функцию, определяющую условие: x > 3.
2. `nonlocal condition` позволяет изменять значение переменной condition, объявленной в функции-родителе, прямо во время работы функции.
3. Логика проверки в `matches_condition`:
- Если condition — функция (то есть callable), она заменяется на текущий элемент item, а элемент исключается из итогового списка (возвращается False).
- Если condition уже не функция, проверяется равенство condition == item.
4. Итоговый процесс фильтрации:
Каждый элемент списка проверяется с помощью matches_condition. Поскольку condition меняется уже на первом шаге, последующие проверки будут отличаться от ожидаемого поведения.
Верный ответ: []
Почему ответ — пустой список?
Ключевую роль играет изменение переменной `condition` на первом шаге. Изначально это была лямбда-функция, которая после первой итерации заменяется на число. В итоге фильтрация ломается: condition больше не проверяет изначальное условие x > 3, а просто сравнивает элементы с числом 1. Так как ни один элемент не равен 1, результатом становится пустой список.
Всегда обращайте внимание на использование замыканий, изменяемых переменных и ключевых слов, таких как nonlocal. Они могут существенно повлиять на выполнение программы.
Канал источник:@press_any_button