[python] ‘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
전혀 위험을 증가시키지 않습니다.
답변
답변
예, 다음과 같습니다.
파이썬을 사용하여 해킹 :
>>> eval(input())
"__import__('os').listdir('.')"
...........
........... #dir listing
...........
아래 코드는 Windows 컴퓨터에서 실행되는 모든 작업을 나열합니다.
>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"
리눅스에서 :
>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"
답변
문제의 특정 문제에 대해 eval
다음 을 사용하는 몇 가지 대안이 있음을 주목할 가치가 있습니다 .
가장 간단한 방법은 setattr
다음과 같습니다.
def __init__(self):
for name in attsToStore:
setattr(self, name, None)
덜 명백한 접근 방식은 객체의 __dict__
객체를 직접 업데이트하는 것 입니다. 속성을로 초기화하기 만하면 None
위의 것보다 덜 간단합니다. 그러나 이것을 고려하십시오 :
def __init__(self, **kwargs):
for name in self.attsToStore:
self.__dict__[name] = kwargs.get(name, None)
이를 통해 키워드 인수를 생성자에 전달할 수 있습니다. 예 :
s = Song(name='History', artist='The Verve')
또한 다음과 같이 locals()
보다 명시 적으로 사용할 수 있습니다 .
s = Song(**locals())
… 그리고 None
이름이 다음과 같은 속성 에 할당하려는 경우 locals()
:
s = Song(**dict([(k, None) for k in locals().keys()]))
속성 목록에 대한 기본값을 객체에 제공하는 또 다른 방법은 클래스의 __getattr__
메소드 를 정의하는 것입니다.
def __getattr__(self, name):
if name in self.attsToStore:
return None
raise NameError, name
명명 된 속성을 일반적인 방식으로 찾지 못하면이 메소드가 호출됩니다. 이 접근 방식은 생성자에서 속성을 설정하거나 업데이트하는 것보다 다소 덜 간단합니다.__dict__
하지 않지만 실제로 존재하지 않는 한 실제로 속성을 만들지 않는 장점이 있습니다. 이는 클래스의 메모리 사용량을 상당히 줄일 수 있습니다.
이 모든 것의 요점 : 일반적으로 피해야 할 많은 이유 eval
가 있습니다. 제어 할 수없는 코드를 실행하는 보안 문제, 디버깅 할 수없는 코드의 실제 문제 등. 그러나 훨씬 더 중요한 이유 일반적으로 사용하지 않아도됩니다. 파이썬은 내부 메커니즘의 많은 부분을 프로그래머에게 노출 시키므로 코드를 작성하는 코드를 거의 작성할 필요가 없습니다.
답변
다른 사용자들은 코드에 의존하지 않기 위해 코드를 어떻게 변경할 수 있는지 지적했다 eval
. eval
CPython에서도 찾아 볼 수있는 합법적 인 사용 사례를 제공 할 것입니다 : testing .
여기에 내가 발견 한 예입니다 test_unary.py
여부에 대한 테스트가 어디 (+|-|~)b'a'
제기는 TypeError
:
def test_bad_types(self):
for op in '+', '-', '~':
self.assertRaises(TypeError, eval, op + "b'a'")
self.assertRaises(TypeError, eval, op + "'a'")
사용법은 분명히 나쁜 습관이 아닙니다. 입력을 정의하고 동작을 관찰하기 만합니다. eval
테스트에 편리합니다.
eval
CPython git 저장소에서 수행 된 이 검색 을 살펴보십시오 . eval 테스트는 많이 사용됩니다.
답변
eval()
를 사용하여 사용자 제공 입력을 처리 할 때 사용자는 다음과 같은 것을 제공 하여 REP (drop-to-REPL) 를 수행 할 수 있습니다.
"__import__('code').InteractiveConsole(locals=globals()).interact()"
그것으로 벗어날 수도 있지만 일반적으로 응용 프로그램에서 임의의 코드 실행 을 위한 벡터를 원하지 않습니다 .