问题
I already showed that the performance of str2func is better, 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 in the following situation:
f='a^x+exp(b)+sin(c*x)+d'
eval
:y = eval(f)
or (suggested by 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);
Why is the first option worse than the second one except for performance reasons?
Remarks
Note that I'm aware that it is good practice to avoid both methods if possible.
Note that I assign the output of
eval
to a variable, which avoids a lot of tricky code from being executed.
回答1:
I'll put aside the arguments against both methods for a moment, although they are all very valid and a conscientious reader should try to understand them.
I can see two clear differences. For these examples we have to assume you've constructed the input string to the functions in your script, using some variable data, and assume it could be anything (by mistake or otherwise). So we prepare and account for the worst case.
str2func
includes some extra checks to make sure what you've passed is a valid function, which might avoid unwanted behaviour. Let's not take the stance of "yeah but you could do it this way to avoid that" and look at an example...% 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')
Another difference is the subject of about half the str2func documentation. It concerns variable scopes, and is found under the heading "Examine Differences Between
str2func
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.
So to conclude, you might have use cases where each function is preferable depending on your desired error handling and variable scoping should never use eval
or str2func
wherever possible, as stated in the other answers.
回答2:
Stop trying to downplay the security risk that is eval
. Running arbitrary code can be harmful if you don't fully control the input. If you fully control the input, there is almost always a better way that doesn't involve dynamic code generation.
You insist on a specific example:
>> 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
is a very friendly unix command; it creates an empty file. Imagine rm -rf ~
in the same context.
Let's try it with 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')'.
Side note: while the risk of eval
is real and not just due to superstition, not using eval
doesn't necessarily mean you're safe. My favourite example, the sneaky devil of str2num
:
>> x = str2num('[system(''touch TMPFILE3'')]')
x =
0
>> ls TMPFILE3
TMPFILE3
So even if you're not explicitly using eval
, it might happen that a convenience function you're using instead does. You should always make sure that this doesn't happen: use the safe str2double
instead of str2num
, and use str2func
instead of eval
(since we saw above that str2func
won't execute arbitrary code).
回答3:
First, performance (especially x100 slower) should be enough reason to not use something.
However, you are missing the point. You are now asking "why not use eval
in the specific example of evaluating a function in a string?". Well, the answer of that is because you have a function called str2func
to specifically do this job faster and more safely. The reason you should not use eval is because in the cases you want to use eval, the logic of your code is flawed.
The only reason to eval
is to evaluate an arbitrary input that is not just a function on a string (why would you do that, you already showed that there is an specific function for it). If you know what you are evaluating, then you don't need eval, you can write code for what you expect. Thus, eval
is only of use when you accept generic inputs. But generic inputs include rm -rf
to delete your whole OS. Or in a less catastrophic case, the code may rewrite over a variable that is important for the rest of the algorithm. It is obvious why you dont want to let your code run arbitrary inputs.
What about dynamic variables? A terrible idea that can be generated using eval
. And you may accidentally make this by accepting arbitrary inputs.
But there are more things. eval
does make your code unreadable. You have no idea what the code does, until runtime.
I have seen code that does this within its core functions
model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);
What does the code do? What are the options? There is no way to know, unless you run it, and see where it goes. This makes the code obfuscated and hard to read, and certainly hard to maintain. Being explicit in code is something that does benefit a lot in maintainability of code.
TLDR: In any case that you may want to use eval, one of the two is happening:
- There is a better, more specific function for what you want to do
- You really should not be doing what you are trying to do, code/logic needs restructuring.
回答4:
TL;DR - eval
has access to locally defined functions while str2func
does not. The "right" function to use among these two depends on the structure of your code.
Without getting into the whole "why eval
is bad" discussion, I shall try to focus on the relevant piece of MATLAB documentation that discusses it in the context of your question:
Consider a file with two functions:
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
When you run the above, it results in :
ans =
8.7432 26.3287
If you're working with classes and you define your own operators this might get out of hand... Let's say somebody decided to add a file to your MATLAB path, and conveniently give it the name of some function you use, or that of an overloadable operator (i.e. mpower.m
):
function out = mpower(varargin)
% This function disregards all inputs and prints info about the calling workspace.
disp(struct2cell(evalin('caller','whos')));
end
While in some cases, MATLAB protects from redefinition of builtin functions, I bet the scenario above might confuse str2func
quite a lot...
来源:https://stackoverflow.com/questions/46213509/why-is-eval-worse-than-str2func-to-evaluate-a-function-from-a-string