Comments 25
Вот хоть убей не знаю, зачем вообще кому-то может потребоваться eval, тем более — в Питоне. Только по дурости и/или лености.
Почти во всех скриптовых языках есть eval. Это миллионная статья про то почему eval опасен. Но он не так страшен как его малюют и у него есть хорошее применение.
В eval нельзя никогда исполнять то что получено из внешнего источника.

Eval хорош в единственной вещи: метапрограммирование + оптимизация. Самый быстрый mysql драйвер под node.js написан с применением eval. Eval позволяет скомпилировать один раз функцию под конкретную окружающую среду. В случае драйвера mysql генерируется функция для прямого мэппинга отдачи объектов.
function(data){
  return {id: data[0], name: data[1], date: new Date(data[2])};
}

В случае написания высоконагруженных приложений рендереры могут стать узким местом, потому почти все библиотеки форматирования даты, заточенные на скорость используют eval. coolDateFormater('Y-m-d') -> вернёт функцию, которая примет на вход дату, а внутри не будет разбирать формат, а сразу возьмёт из даты год, месяц, день и отдаст: Год +'-'+ месяц +'-'+ день. Рендереры не всегда про визуальную часть, в моделях зачастую данные перегоняются в свой формат и на 1к таких операций это уже может дать эффект, а на 10к стать заметным невооруженным глазом.
Лично у меня есть паттерн observable оптимизированный через eval. Вместо обхода списка всех подписчиков — на каждое подписывание перегенерируется функция, которая вызывает всех подписчиков в нужном скоупе с переданными параметрами. Дало ускорение ~30%. На богатых на эвенты моделях такой подход сказался положительно. (Хотя кого я обманываю, я люблю оптимизировать и эта задача была сделана just for fun. Так как скорость решения оказалась выше обычной реализации — стал использовать эту).
UFO landed and left these words here
Там на самом деле надо было заходить к оптимизации с другой стороны, что бы когда добавляешь пачку объектов — выключать рассылку эвентов. Эта оптимизация была сделана потом. Но до этого момента было видно что ворочается чуть менее безнадёжно).
Например, в стандартной библиотеке collections.named_tuple реализован именно через eval (формалньно — через exec, но невелика разница). hg.python.org/cpython/file/ab5e2b0fba15/Lib/collections/__init__.py#l239
Наверное, более чистым способом было бы аккуратно собрать AST-дерево и скомпилировать его, благо возможности для того есть. Но это сложнее, и, возможно, даже медленнее.
зачем вообще кому-то может потребоваться eval,

Например (схематично):

sub create_handler
{
my ( $type ) = @_;
eval «use Engine::$type;»;
return err_log( «USE Engine::$type error: $@» ) if $@;
my $obj;
eval "\$obj = Engine::$type->new();";
return err_log( «Engine::$type->new() error: $@» ) if $@;
$obj;
}
Из решений без eval там упомянут только Module::Load. Который всё равно нужно оборачивать в eval, потому что он умирает (die) при ошибках :)
P.S. Да и то, если заглянуть в потроха Module::Load, то обнаружится, что фактически этот модуль — обёртка для того же eval { require $file } :-)
Совершенно не того же. eval с блоком в Perl есть и используется вместо try/catch и абсолютно безопасен. Опасен только eval со строкой. В вашем примере, что бы не находилось в переменной $file будет вызван require с одним аргументом и больше ничего.
eval — очень удобный способ фильтрации чего угодно по сложным пользовательским выражениям (найти все ноутбуки в продаже, от 10 000 до 20 000, но не acer, либо можно acer, но если до 12 000 и выпущен в этом году и памяти больше 8G, либо любой ноут с USB 3.0 если меньше 10 000, и при этом не учитывать предложения фирм, которые в моем черном списке).

Обычный интерфейс «с галочками» как на яндекс-маркете, во-первых очень сложно реализуется (особенно, если нам его надо для многих видов товара сделать), во-вторых — гораздо менее гибкий (попробуйте-ка вышеописанное выражение выразить в виде «галочек» на форме фильтра в маркете)

ниже дал ссылку на свой пост с обсуждением этой проблемы и безопасным решением
В TCL есть замечательная штука — safe interp. Которая как раз решает подобные проблемы. Почему другие языки не возьмут на вооружение эту штуку?
Пробовали, не получается. Есть у меня подозрение, что с TCL оно работает только в силу малараспространённости TCL'я. Причём NSA, скорее всего, умеет вскрывать сервера, использующие safe interp, а остальным оно просто не нужно.
Вы неправы. В TCL оно сделано строго по принципу «запрещено всё, что не разрешено», т.е. по белым спискам (в отличие от того, что например описано в статье выше — там чёрные списки). То есть, взломать конечно можно, если разрешили неправильно (скажем, сделали алиас небезопасной функции в интерп, а проверку при вызове алиаса выполнили недостаточно тщательно), но это не проблема языка или интерпретатора, а явно ошибка в программе, некорректное использование или некорректный (слишком обширный) белый список.
Сообщений же на тему «escape safe interp» в случае с корректным белым списком не обнаруживается, несмотря на то, что в силу структуры самого языка возможностей «сбежать» там должно быть хоть отбавляй.
Да, его проблема в том, что он умеет только загрузить примитивные конструкции. Как json.loads и подобные.
Некропостну со ссылкой на свой пост по этой теме — evalidate: безопасная обработка пользовательских выражений. Тоже очень долго мучался с безопасностью eval'а (при том что от всей его мощности мне нужно всего-то полпроцента), кончилось написанием своего модуля для обработки только безопасных пользовательских выражений.

REPL как правило доверенный — а потому проблема безопасного eval в этом случае не стоит

Only those users with full accounts are able to leave comments. Log in, please.