'eval'을 사용하는 것은 왜 나쁜 습관입니까?
노래의 데이터를 쉽게 저장하기 위해 다음 클래스를 사용하고 있습니다.
class Song:
"""The class to store the details of each song"""
attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
def __init__(self):
for att in self.attsToStore:
exec 'self.%s=None'%(att.lower()) in locals()
def setDetail(self, key, val):
if key in self.attsToStore:
exec 'self.%s=val'%(key.lower()) in locals()
나는 이것이 if/else
블록을 쓰는 것보다 훨씬 확장 가능하다고 생각합니다 . 그러나 eval
나쁜 습관으로 간주되어 사용하기에 안전하지 않은 것으로 보입니다. 그렇다면 누구든지 왜 나에게 설명하고 위의 클래스를 정의하는 더 좋은 방법을 보여줄 수 있습니까?
예, 평가를 사용하는 것은 나쁜 습관입니다. 몇 가지 이유를 말하면 다음과 같습니다.
- 거의 항상 더 좋은 방법이 있습니다.
- 매우 위험하고 안전하지 않은
- 디버깅을 어렵게 만듭니다
- 느린
귀하의 경우 대신 setattr 을 사용할 수 있습니다 .
class Song:
"""The class to store the details of each song"""
attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
def __init__(self):
for att in self.attsToStore:
setattr(self, att.lower(), None)
def setDetail(self, key, val):
if key in self.attsToStore:
setattr(self, key.lower(), val)
편집하다:
eval 또는 exec를 사용해야하는 경우가 있습니다. 그러나 그들은 드 rare니다. 귀하의 경우 eval을 사용하는 것은 확실히 나쁜 습관입니다. 나는 eval과 exec가 종종 잘못된 장소에서 사용되기 때문에 나쁜 연습을 강조하고 있습니다.
편집 2 :
OP 사례에서 평가가 '매우 위험하고 안전하지 않다'는 의견에 동의하지 않는 것 같습니다. 이 특정 경우에 해당하지만 일반적으로 그렇지는 않습니다. 질문은 일반적이었고 내가 열거 한 이유는 일반적인 경우에도 마찬가지입니다.
편집 3 : 순서 1과 4를 재정렬
사용 eval
은 약하지만 분명히 나쁜 습관 은 아닙니다 .
"기본 소프트웨어 원칙"을 위반합니다. 소스는 실행 가능한 총계가 아닙니다. 출처 외에도에 대한 논쟁이 있으며,
eval
분명히 이해해야합니다. 이런 이유로, 그것은 최후의 수단입니다.일반적으로 사려 깊은 디자인의 표시입니다. 동적 소스 코드를 작성해야하는 이유는 거의 없습니다. 위임 및 기타 OO 디자인 기술로 거의 모든 작업을 수행 할 수 있습니다.
작은 코드 조각을 비교적 빠르게 컴파일합니다. 더 나은 디자인 패턴을 사용하면 피할 수있는 오버 헤드가 발생합니다.
각주로서, 소외된 소시 오 패스의 손에, 그것은 잘 작동하지 않을 수 있습니다. 그러나 소외된 사회 병리 적 사용자 나 관리자와 대면 할 때는 먼저 해석 된 파이썬을 제공하지 않는 것이 가장 좋습니다. 진정한 악의 손에 파이썬은 책임을 질 수 있습니다. eval
전혀 위험을 증가시키지 않습니다.
이 경우에 그렇습니다. 대신에
exec 'self.Foo=val'
내장 함수를 사용해야합니다 setattr
:
setattr(self, 'Foo', val)
예, 다음과 같습니다.
파이썬을 사용하여 해킹 :
>>> eval(input())
"__import__('os').listdir('.')"
...........
........... #dir listing
...........
The below code will list all tasks running on a Windows machine.
>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"
In Linux:
>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"
It's worth noting that for the specific problem in question, there are several alternatives to using eval
:
The simplest, as noted, is using setattr
:
def __init__(self):
for name in attsToStore:
setattr(self, name, None)
A less obvious approach is updating the object's __dict__
object directly. If all you want to do is initialize the attributes to None
, then this is less straightforward than the above. But consider this:
def __init__(self, **kwargs):
for name in self.attsToStore:
self.__dict__[name] = kwargs.get(name, None)
This allows you to pass keyword arguments to the constructor, e.g.:
s = Song(name='History', artist='The Verve')
It also allows you to make your use of locals()
more explicit, e.g.:
s = Song(**locals())
...and, if you really want to assign None
to the attributes whose names are found in locals()
:
s = Song(**dict([(k, None) for k in locals().keys()]))
Another approach to providing an object with default values for a list of attributes is to define the class's __getattr__
method:
def __getattr__(self, name):
if name in self.attsToStore:
return None
raise NameError, name
This method gets called when the named attribute isn't found in the normal way. This approach somewhat less straightforward than simply setting the attributes in the constructor or updating the __dict__
, but it has the merit of not actually creating the attribute unless it exists, which can pretty substantially reduce the class's memory usage.
The point of all this: There are lots of reasons, in general, to avoid eval
- the security problem of executing code that you don't control, the practical problem of code you can't debug, etc. But an even more important reason is that generally, you don't need to use it. Python exposes so much of its internal mechanisms to the programmer that you rarely really need to write code that writes code.
Other users pointed out how your code can be changed as to not depend on eval
; I'll offer a legitimate use-case for using eval
, one that is found even in CPython: testing.
Here's one example I found in test_unary.py
where a test on whether (+|-|~)b'a'
raises a TypeError
:
def test_bad_types(self):
for op in '+', '-', '~':
self.assertRaises(TypeError, eval, op + "b'a'")
self.assertRaises(TypeError, eval, op + "'a'")
The usage is clearly not bad practice here; you define the input and merely observe behavior. eval
is handy for testing.
Take a look at this search for eval
, performed on the CPython git repository; testing with eval is heavily used.
When eval()
is used to process user-provided input, you enable the user to Drop-to-REPL providing something like this:
"__import__('code').InteractiveConsole(locals=globals()).interact()"
You may get away with it, but normally you don't want vectors for arbitrary code execution in your applications.
In addition to @Nadia Alramli answer, since I am new to Python and was eager to check how using eval
will affect the timings, I tried a small program and below were the observations:
#Difference while using print() with eval() and w/o eval() to print an int = 0.528969s per 100000 evals()
from datetime import datetime
def strOfNos():
s = []
for x in range(100000):
s.append(str(x))
return s
strOfNos()
print(datetime.now())
for x in strOfNos():
print(x) #print(eval(x))
print(datetime.now())
#when using eval(int)
#2018-10-29 12:36:08.206022
#2018-10-29 12:36:10.407911
#diff = 2.201889 s
#when using int only
#2018-10-29 12:37:50.022753
#2018-10-29 12:37:51.090045
#diff = 1.67292
참고URL : https://stackoverflow.com/questions/1832940/why-is-using-eval-a-bad-practice
'Programing' 카테고리의 다른 글
VS2015 빌드가 Dynamic에서 오류 메시지없이 실패 함 (0) | 2020.07.07 |
---|---|
선택한 특정 열을 새 DataFrame에 복사본으로 추출 (0) | 2020.07.07 |
약속 체인을 끊고 체인이 끊어진 (거부 된) 단계에 따라 함수를 호출하십시오. (0) | 2020.07.07 |
소켓 라이브러리에서 recv를 호출 할 때 recv 버퍼의 크기는 얼마입니까? (0) | 2020.07.07 |
Json Array를 일반 Java 목록으로 변환 (0) | 2020.07.07 |