Why is `eval` worse than `str2func` to evaluate a function from a string? [closed]

☆樱花仙子☆ 提交于 2019-12-04 04:28:23

问题


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'
  1. eval:

    y = eval(f)
    

    or (suggested by rahnema1)

    fHandle = eval(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
  2. 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.

  1. 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')
    
  2. 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 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.


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:

  1. There is a better, more specific function for what you want to do
  2. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!