'SyntaxError: unexpected EOF while parsing'에 해당되는 글 1건

  1. 2023.05.21 [Python] eval() 메소드: 동적으로 문자열 표현식을 평가하여 실행 (Evaluate expressions dynamically)

이번 포스티에서는 Python의 내장 함수인 eval() 함수에 대해서 소개하겠습니다. 

 

(1) Python의 eval() 함수 구문 이해 및 문자열 표현식 인풋을 eval() 함수에 사용하기

(2) eval() 함수의 잘못된 사용 예시 (SyntaxError) 

(3) compiled-code-based 인풋을 eval() 함수에 사용하기

 

 

python eval() function

 

(1) Python의 eval() 함수 구문 이해 및 문자열 표현식 인풋을 eval() 함수에 사용하기

 

Python 의 내장함수(built-in function)인 eval() 함수는 임의의 문자열 기반(string-based) 또는 컴파일된 코드 기반 (compiled-code-based) 인풋의 표현식(expressions)을 평가(evaluate)해서 실행해줍니다. 

 

문자열 기반의 표현식을 eval() 함수가 처리하는 순서는 아래와 같습니다. 

 

  (1-a) 문자열 기반 표현식을 파싱한다. (Parse a string-based expression)

  (1-b) 문자열을 파싱한 결과를 바이트코드로 컴파일한다. (Compile it to bytecode)

  (1-c) 파이썬 표현식으로 평가한다. (Evaluate it as a Python expression) 

  (1-d) 평가한 결과를 하나의 값으로 반환한다. (Return the result of the evaluation)

 

 

아래 예시는 문자열 기반 표현식 (string-based expressions) 으로 수학 계산식(math expressions)을 인풋으로 해서 eval() 메소드를 사용해 동적으로 평가하여 실행한 것입니다. 

 

## You can use the built-in Python eval() 
## to dynamically evaluate expressions 
## from a string-based or compiled-code-based input.

##-- eval() for a string-based input
##-- Math expressions
eval("2 + 6")
# 8

eval("10**2")
# 100

eval("sum([1, 2, 3, 4, 5])")
# 15


import math
eval("math.pi * pow(5, 2)")
# 78.53981633974483

 

 

 

eval() 함수는 문자열 표현식에서 아래 예의 x 와 같은 글로벌 변수에 접근해서 표현식을 평가하고 실행할 수 있습니다.  

 

## eval() has access to global names like x
x = 10
eval("x * 5")
# 50

 

 

 

eval() 함수는 문자열의 블리언 표현식 (Boolean expressions)에 대해서도 평가하여 실행할 수 있습니다.

 

아래의 예에서는 순서대로 블리언 표현식 (Boolean expressions)의 

  (a) 비교 연산자 (value comparison operstors: <, >, <=, >=, ==, !=)),

  (b) 논리 연산자 (logical operators: and, or, not),

  (c) 소속 여부 확인 연산자 (membership test operators: in, not in),

  (d) 동일 여부 확인 연산자 (identity operators: is, is not)

을 사용한 문자열 기반 인풋을 eval() 메소드를 통해 평가하고 실행해 보았습니다. 

 

## -- eval() for Boolean expressions
x = 10

## (a) value comparison operators: <, >, <=, >=, ==, !=
eval("x > 5")
# True


## (b) logical operstors: and, or, not
eval("x > 5 and x < 9")
# False


## (c) membership test operators: in, not in
eval("x in {1, 5, 10}")
# True


## (d) identity operators: is, is not
eval("x is 10")
# True

 

 

 

그러면, 그냥 Python 표현식을 쓰면 되지, 왜 굳이 문자열 기반의 표현식을 eval() 함수에 인풋으로 넣어서 쓸까 궁금할 것입니다. 아래의 조건 표현식을 가지는 사용자 정의 함수를 예로 들자면, 사용자 정의함수 myfunc() 를 사용할 때처럼 동적으로 문자열 기반의 조건절 표현식을 바꾸어가면서 쓸 수 있어서 강력하고 편리합니다. 

 

## suppose you need to implement a conditional statement, 
## but you want to change the condition on the fly, dynamically. 
def myfunc(a, b, condition):
    if eval(condition):
        return a + b
    return a - b
    
    
myfunc(5, 10, "a > b")
# -5


myfunc(5, 10, "a <= b")
# 15


myfunc(5, 10, "a is b")
# -5

 

 

 

(2) eval() 함수의 잘못된 사용 예시 (SyntaxError) 

 

(2-1) 만약 eval() 함수의 인풋으로 표현식(expressions) 이 아니라, if, while 또는 for 와 같은 키워드를 사용해서 만든 코드 블락으로 이루어진 명령문(statement)을 사용한다면 "SyntaxError: invalid syntax" 에러가 발생합니다. 

 

## if you pass a compound statement to eval(), 
## then you'll get a SyntaxError. 
x = 10
eval("if x>5: print(x)")
# File "<string>", line 1
#     if x>5: print(x)
#     ^
# SyntaxError: invalid syntax

 

 

 

(2-2) eval() 함수에 할당 연산(assignment operations: =) 을 사용하면 "SyntaxError: invalid syntax" 에러가 발생합니다. 

 

## Assignment operations aren't allowed with eval(). 
eval("x = 10")
# File "<string>", line 1
#     x = 10
#       ^
# SyntaxError: invalid syntax

 

 

 

(2-3) Python 구문의 규칙을 위배하면 "SyntaxError: unexpedted EOF while parsing" 에러가 발생합니다. 

(아래 예에서는 "1 + 2 -" 에서 문자열 마지막에 - 부호가 잘못 들어갔음) 

 

## If an expression violates Python syntax, then SyntaxError
eval("1 + 2 -")
# File "<string>", line 1
#     1 + 2 -
#           ^
# SyntaxError: unexpected EOF while parsing

 

 

 

(3) compiled-code-based 인풋을 eval() 함수에 사용하기

 

eval() 함수의 인풋으로 위의 문자열 기반 객체 대신 compiled-code-based 객체를 사용할 수도 있습니다. compiled-code-based 객체를 eval() 함수의 인풋으로 사용하면 아래의 두 단계를 거칩니다. 

 

  (3-a) 컴파일된 코드를 평가한다. (Evaluate the compiled code)

  (3-b) 평가 결과를 반환한다. (Return the result of the evaluation)

 

위의 (1)번에서 문자열 기반의 표현식을 eval() 함수의 인풋으로 사용했을 때 대비 compiled-code 객체를 eval() 함수의 인풋으로 사용할 경우 파싱하고 컴파일 하는 단계가 없고, 바로 컴파일된 코드를 평가하고 반환하는 단계로 넘어가므로 똑같은 표현식을 여러번 평가해야 하는 경우에 속도 향상을 기대할 수 있습니다. 

 

 

Python의 eval() 함수에 compiled code 객체를 인풋으로 사용하려면, 

compiled_code_object = compile(source, filename, mode) 의 구문을 사용해서 컴파일 해주면 됩니다. 

 - source 에는 문자열 표현식(string-based expression)을 넣어줍니다. 

 - filename 에는 문자열 기반 표현식을 사용할 경우 "<string>" 을 써줍니다. 

 - mode 에는 컴파일된 코드를 eval() 함수로 처리하길 원할 경우 "eval" 을 써줍니다. 

 

##-- eval() for compiled-code-based input
compiled_code = compile("(2 + 3) * 10", "<string>", "eval")
eval(compiled_code)
# 50

 

 

 

 

[ Reference ]

* Real Python site: "Python eval(): Evaluate Expressions Dynamically
: https://realpython.com/python-eval-function/

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다 .

행복한 데이터 과학자 되세요! :-)

 

728x90
반응형
Posted by Rfriend
,