为什么 `eval` 比 `str2func` 从字符串评估函数更差?
Why is `eval` worse than `str2func` to evaluate a function from a string?
我已经证明 , but I got a lot of comments stating that there are more fundamental reasons to not use eval
. Which fundamental reasons do apply to eval
and do not apply to str2func
在以下情况下:
f='a^x+exp(b)+sin(c*x)+d'
eval
:
y = eval(f)
或(rahnema1 建议)
fHandle = eval(['@(x, a, b, c, d) ' f]);
y = fHandle(x, a, b, c, d);
str2func
:
fHandle = str2func(['@(x, a, b, c, d) ' f]);
y = fHandle(x, a, b, c, d);
除了,为什么第一个选项比第二个选项差?
备注
请注意,我知道最好尽可能避免这两种方法。
注意我把eval
的输出赋给了一个变量,这样就避免了很多棘手的代码被执行
停止试图淡化 eval
的安全风险。 运行 如果您不能完全控制输入,任意代码可能是有害的。如果您完全控制输入,几乎总是一种不涉及动态代码生成的更好方法。
你坚持具体例子:
>> ls TMPFILE
Error using ls (line 35)
ls: cannot access 'TMPFILE': No such file or directory
>> y = eval('system(''touch TMPFILE'')');
>> y
y =
0
>> ls TMPFILE
TMPFILE
touch
是一个非常友好的unix命令;它创建一个空文件。想象一下 rm -rf ~
在相同的上下文中。
让我们用 str2func
试试:
>> y = str2func('system(''touch TMPFILE2'')');
Warning: The input to STR2FUNC "system('touch TMPFILE2')" is not a valid function name. This will generate an error in a future release.
>> ls TMPFILE2
Error using ls (line 35)
ls: cannot access 'TMPFILE2': No such file or directory
>> y
y =
function_handle with value:
@system('touch TMPFILE2')
>> y()
Undefined function or variable 'system('touch TMPFILE2')'.
旁注:虽然 eval
的风险是真实的,而不仅仅是由于迷信,不 使用 eval
并不一定意味着你'安全。我最喜欢的例子,str2num
的偷偷摸摸的恶魔:
>> x = str2num('[system(''touch TMPFILE3'')]')
x =
0
>> ls TMPFILE3
TMPFILE3
因此,即使您没有明确使用 eval
,您使用的便捷函数也可能会 。你应该始终确保这不会发生:使用安全的 str2double
而不是 str2num
,并使用 str2func
而不是 eval
(因为我们在上面看到 str2func
不会执行任意代码)。
首先,性能(尤其是慢 100 倍)应该足以成为不使用某些东西的理由。
但是,您没有抓住重点。您现在问的是 "why not use eval
in the specific example of evaluating a function in a string?"。好吧,答案是因为你有一个名为 str2func
的函数来专门更快更安全地完成这项工作。你不应该使用 eval 的原因是因为在你想使用 eval 的情况下,你的代码逻辑是有缺陷的。
eval
的唯一原因是评估一个任意输入 而不仅仅是 字符串上的一个函数(你为什么要这样做,你已经证明了它有一个特定的功能)。如果你知道你在评估什么,那么你就不需要 eval,你可以为你所期望的编写代码。因此,eval
仅在您接受通用输入时才有用。但是通用输入包括 rm -rf
以删除整个 OS。或者在不太严重的情况下,代码可能会重写对算法的其余部分很重要的变量。很明显为什么你不想让你的代码 运行 任意输入。
动态变量呢? 可以使用 eval
生成。您可能会通过接受任意输入而意外地做到这一点。
但是还有更多的东西。 eval
确实使您的代码不可读。在 运行time.
之前,您不知道代码的作用
我在其核心功能中看到了执行此操作的代码
model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);
代码的作用是什么?有什么选择?没有办法知道,除非你 运行 它,看看它去了哪里。这使得代码变得混乱且难以阅读,当然也难以维护。在代码中显式确实对代码的可维护性有很大好处。
TLDR: 在任何情况下,您可能想要使用 eval,两者之一正在发生:
- 有更好、更具体的函数可以满足您的需求
- 你真的不应该做你想做的事,code/logic需要重组。
TL;DR - eval
可以访问本地定义的函数,而 str2func
不能。在这两个函数中使用的 "right" 函数取决于您的代码结构。
在不进入整个 "why eval
is bad" 讨论的情况下,我将尽量专注于 MATLAB documentation 中在您的问题的上下文中讨论它的相关部分:
考虑一个具有两个函数的文件:
function out = q46213509
f='a^x+exp(b)+sin(c*x)+d';
fHandle = {eval(['@(x, a, b, c, d) ' f]), str2func(['@(x, a, b, c, d) ' f])};
out = [fHandle{1}(1,2,3,4,5) fHandle{2}(1,2,3,4,5)];
end
function e = exp(x)
e = x.^0./factorial(0) - x.^1./factorial(1) + x.^2./factorial(2); % Error is deliberate
end
当您 运行 上面的内容时,结果是:
ans =
8.7432 26.3287
如果您正在使用 类 并定义 your own operators this might get out of hand... Let's say somebody decided to add a file to your MATLAB path,并且 方便地 给它指定您使用的某个函数的名称,或者可重载运算符(即 mpower.m
):
function out = mpower(varargin)
% This function disregards all inputs and prints info about the calling workspace.
disp(struct2cell(evalin('caller','whos')));
end
虽然在某些情况下,MATLAB 会防止重新定义内置函数,但我敢打赌上述情况可能会使 str2func
混淆很多...
我暂时搁置反对这两种方法的论点,尽管它们都是 非常 有效的,认真的 reader 应该尝试理解它们。
我可以看到两个明显的区别。对于这些示例,我们必须假设您已经使用一些变量数据为脚本中的函数构建了输入字符串,并假设它可能是 anything(无论是错误的还是其他原因)。所以我们准备并考虑最坏的情况。
str2func
包括 一些 额外检查以确保您传递的是有效函数,这可能会避免不必要的行为。先不站在"yeah but you could do it this way to avoid that"的立场,看个例子...
% Not assigning an output variable
% STR2FUNC: You've harmlessly assigned ans to some junk function
str2func('delete test.txt')
% EVAL: You've just deleted your super important document
eval('delete test.txt')
% Assigning an output variable
% STR2FUNC: You get a clear warning that this is not a valid function
f = str2func('delete test.txt')
% EVAL: You can a non-descript error "Unexpected MATLAB expression"
f = eval('delete test.txt')
另一个区别是题目的一半左右str2func
documentation。它涉及变量范围,可在 "Examine Differences Between str2func
and eval
".
标题下找到
[Intro to the function]
If you use a character vector representation of an anonymous function, the resulting function handle does not have access to private or local functions.
[Examine Differences Between str2func
and eval
]
When str2func
is used with a character vector representing an anonymous function, it does not have access to the local function [...]. The eval
function does have access to the local function.
总而言之,您 可能有这样的用例,其中每个函数都更可取,具体取决于您所需的错误处理和变量范围 永远不应使用 eval
或 str2func
如其他答案所述,只要有可能。
我已经证明 eval
. Which fundamental reasons do apply to eval
and do not apply to str2func
在以下情况下:
f='a^x+exp(b)+sin(c*x)+d'
eval
:y = eval(f)
或(rahnema1 建议)
fHandle = eval(['@(x, a, b, c, d) ' f]); y = fHandle(x, a, b, c, d);
str2func
:fHandle = str2func(['@(x, a, b, c, d) ' f]); y = fHandle(x, a, b, c, d);
除了
备注
请注意,我知道最好尽可能避免这两种方法。
注意我把
eval
的输出赋给了一个变量,这样就避免了很多棘手的代码被执行
停止试图淡化 eval
的安全风险。 运行 如果您不能完全控制输入,任意代码可能是有害的。如果您完全控制输入,几乎总是一种不涉及动态代码生成的更好方法。
你坚持具体例子:
>> ls TMPFILE
Error using ls (line 35)
ls: cannot access 'TMPFILE': No such file or directory
>> y = eval('system(''touch TMPFILE'')');
>> y
y =
0
>> ls TMPFILE
TMPFILE
touch
是一个非常友好的unix命令;它创建一个空文件。想象一下 rm -rf ~
在相同的上下文中。
让我们用 str2func
试试:
>> y = str2func('system(''touch TMPFILE2'')');
Warning: The input to STR2FUNC "system('touch TMPFILE2')" is not a valid function name. This will generate an error in a future release.
>> ls TMPFILE2
Error using ls (line 35)
ls: cannot access 'TMPFILE2': No such file or directory
>> y
y =
function_handle with value:
@system('touch TMPFILE2')
>> y()
Undefined function or variable 'system('touch TMPFILE2')'.
旁注:虽然 eval
的风险是真实的,而不仅仅是由于迷信,不 使用 eval
并不一定意味着你'安全。我最喜欢的例子,str2num
的偷偷摸摸的恶魔:
>> x = str2num('[system(''touch TMPFILE3'')]')
x =
0
>> ls TMPFILE3
TMPFILE3
因此,即使您没有明确使用 eval
,您使用的便捷函数也可能会 。你应该始终确保这不会发生:使用安全的 str2double
而不是 str2num
,并使用 str2func
而不是 eval
(因为我们在上面看到 str2func
不会执行任意代码)。
首先,性能(尤其是慢 100 倍)应该足以成为不使用某些东西的理由。
但是,您没有抓住重点。您现在问的是 "why not use eval
in the specific example of evaluating a function in a string?"。好吧,答案是因为你有一个名为 str2func
的函数来专门更快更安全地完成这项工作。你不应该使用 eval 的原因是因为在你想使用 eval 的情况下,你的代码逻辑是有缺陷的。
eval
的唯一原因是评估一个任意输入 而不仅仅是 字符串上的一个函数(你为什么要这样做,你已经证明了它有一个特定的功能)。如果你知道你在评估什么,那么你就不需要 eval,你可以为你所期望的编写代码。因此,eval
仅在您接受通用输入时才有用。但是通用输入包括 rm -rf
以删除整个 OS。或者在不太严重的情况下,代码可能会重写对算法的其余部分很重要的变量。很明显为什么你不想让你的代码 运行 任意输入。
动态变量呢? eval
生成。您可能会通过接受任意输入而意外地做到这一点。
但是还有更多的东西。 eval
确实使您的代码不可读。在 运行time.
我在其核心功能中看到了执行此操作的代码
model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);
代码的作用是什么?有什么选择?没有办法知道,除非你 运行 它,看看它去了哪里。这使得代码变得混乱且难以阅读,当然也难以维护。在代码中显式确实对代码的可维护性有很大好处。
TLDR: 在任何情况下,您可能想要使用 eval,两者之一正在发生:
- 有更好、更具体的函数可以满足您的需求
- 你真的不应该做你想做的事,code/logic需要重组。
TL;DR - eval
可以访问本地定义的函数,而 str2func
不能。在这两个函数中使用的 "right" 函数取决于您的代码结构。
在不进入整个 "why eval
is bad" 讨论的情况下,我将尽量专注于 MATLAB documentation 中在您的问题的上下文中讨论它的相关部分:
考虑一个具有两个函数的文件:
function out = q46213509
f='a^x+exp(b)+sin(c*x)+d';
fHandle = {eval(['@(x, a, b, c, d) ' f]), str2func(['@(x, a, b, c, d) ' f])};
out = [fHandle{1}(1,2,3,4,5) fHandle{2}(1,2,3,4,5)];
end
function e = exp(x)
e = x.^0./factorial(0) - x.^1./factorial(1) + x.^2./factorial(2); % Error is deliberate
end
当您 运行 上面的内容时,结果是:
ans =
8.7432 26.3287
如果您正在使用 类 并定义 your own operators this might get out of hand... Let's say somebody decided to add a file to your MATLAB path,并且 方便地 给它指定您使用的某个函数的名称,或者可重载运算符(即 mpower.m
):
function out = mpower(varargin)
% This function disregards all inputs and prints info about the calling workspace.
disp(struct2cell(evalin('caller','whos')));
end
虽然在某些情况下,MATLAB 会防止重新定义内置函数,但我敢打赌上述情况可能会使 str2func
混淆很多...
我暂时搁置反对这两种方法的论点,尽管它们都是 非常 有效的,认真的 reader 应该尝试理解它们。
我可以看到两个明显的区别。对于这些示例,我们必须假设您已经使用一些变量数据为脚本中的函数构建了输入字符串,并假设它可能是 anything(无论是错误的还是其他原因)。所以我们准备并考虑最坏的情况。
str2func
包括 一些 额外检查以确保您传递的是有效函数,这可能会避免不必要的行为。先不站在"yeah but you could do it this way to avoid that"的立场,看个例子...% Not assigning an output variable % STR2FUNC: You've harmlessly assigned ans to some junk function str2func('delete test.txt') % EVAL: You've just deleted your super important document eval('delete test.txt') % Assigning an output variable % STR2FUNC: You get a clear warning that this is not a valid function f = str2func('delete test.txt') % EVAL: You can a non-descript error "Unexpected MATLAB expression" f = eval('delete test.txt')
另一个区别是题目的一半左右
标题下找到str2func
documentation。它涉及变量范围,可在 "Examine Differences Betweenstr2func
andeval
".[Intro to the function]
If you use a character vector representation of an anonymous function, the resulting function handle does not have access to private or local functions.
[Examine Differences Betweenstr2func
andeval
]
Whenstr2func
is used with a character vector representing an anonymous function, it does not have access to the local function [...]. Theeval
function does have access to the local function.
总而言之,您 可能有这样的用例,其中每个函数都更可取,具体取决于您所需的错误处理和变量范围 永远不应使用 eval
或 str2func
如其他答案所述,只要有可能。