IT/언어

[python] python의 변수 사용 범위 정리 (1)

개발자 두더지 2020. 8. 18. 18:30
728x90

먼저, 이 글에서 자주 사용될 단어인 글로벌 변수와 로컬 변수에 대한 간략한 정의에 대해 알아보자.

글로벌 변수(전역변수)는 함수 외부에서 선언된 변수로, 프로그램 전체에서 접근할 수 있는 변수입니다. 로컬 변수(지역변수)는 함수 내부에서 선언된 변수로, 함수가 실행되면 만들어지고 함수가 종료되면 소멸하는 변수입니다. 함수 외부에서는 접근할 수 없습니다.

 

1. 글로벌 변수와 로컬 변수

 Python은 함수 내에 변수를 호출할 때, 글로벌로 이미 정의되어있는 경우는 글로벌 변수가 사용된다.

global_var = "Global Varibale"
def get_global():
    local_var = global_var
    return local_var
print(get_global())

>>> Global Variable

 이 때, 호출된 후에 함수내에 대입 처리를 실행하는 경우, 로컬의 scope로써 대우되는 예외가 발생한다.

 (Python은 함수 내에 대입이 이루어지면, 처음 로컬 scope로써 그 변수를 취급한다. )

global_var = "Global Varibale"
def get_global():
    local_var = global_var
    global_var = "Reset Local" # Add
    return local_var
print(get_global())

>>> Traceback (most recent call last):
>>>   File "test.py", line 7, in <module>
>>>     print(get_global())
>>>   File "test.py", line 3, in get_global
>>>     local_var = global_var
>>> UnboundLocalError: local variable 'global_var' referenced before assignment

 글로벌 변수를 사용하고 있다고 생각하여, 글로벌 변수를 덮어쓸 생각으로 대입 처리를 하면 예외가 발생하므로 scope외의 변수를 취급하는 경우에는 주의할 필요가 있다.

 이것을 방지하기 위해서 글로벌 변수에 사용되고 있는 (혹은 사용되고 있는 가능성이 있는 변수명을 지정하는) 경우에는, 함수 정의 직후에 초기화를 하는 것을 추천한다.

common_var = "Global Varibale"
def get_global():
    common_var = ""
    # Intermediate processing
    common_var = "Local Variable"
    return common_var

print(get_global())
print(common_var)

>>> Local Variable
>>> Global Varibale

 또한, 글로벌 변수를 함수내에 변경하는 경우는 'global'로 명시적으로 정의하여 사용하는 것도 가능하다.

common_var = "Global Varibale"
def get_global():
    global common_var
    # Intermediate processing
    common_var = "Local Variable"
    return common_var

print(get_global())
print(common_var)

>>> Local Variable
>>> Local Variable

만약 글로벌 변수가 없을 때 함수 안에서 global을 사용하면 해당 변수는 전역 변수가 된다.

def get_global():
    global common_var
    # Intermediate processing
    common_var = "Local Variable"
    return common_var

print(get_global())
print(common_var)

>>> Local Variable
>>> Local Variable

 

2. 클래스에 정의하는 변수

※ 참고

① 이하의 "글로벌 scope"는 "인스턴스에서 볼 때 글로벌 = 클래스 변수"라는 의미이다.  

② 반대로, "로컬 scope"는 "인스턴스에서 볼 때 닫혀있다 = 인스턴스 변수"라는 의미이다.

③ 위의 설명과 맞추기 위해 문구를 통일하였다.

* class에 정의된 변수를 인스턴스 내에서 다시 대입한다.

class TestClass(object):
    common_var = "Initial Value"
    def get_variable(self):
        return self.common_var
    def set_variable(self, value):
        self.common_var = value

tc1 = TestClass()
tc2 = TestClass()
tc1.set_variable("Set Variable")
print(tc2.common_var)

>>> Initial Value

이 경우, 값을 대입하는 것으로 scope가 인스턴스 내에 한정되고, tc2의 변수는 덮어 쓸 수 없다.

(함수와 같이 메소드내에 대입처리를 하면, 로컬 scope로써 취급된다. 클래스 변수로써 조작하고 싶은 경우에는 클래스를 지정할 필요가 있다.)

그러나, 이것은 Mutable한 변수에다가 대입 처리가 되지 않으면 글로벌 scope로 처리되어 tc2에도 영향을 미친다.

class TestClass(object):
    common_var = []
    def get_variable(self):
        return self.common_var
    def set_variable(self, value):
        self.common_var.append(value)

tc1 = TestClass()
tc2 = TestClass()
tc1.set_variable("Set Variable")
print(tc2.common_var)

>>> ['Set Variable']

 

 

common_var가 '클래스 변수'라는 전제인 경우 뭔가 이상한 동작은 아니지만, 전에 기술한 동작을 생각해 봤을 때 알기 어렵게 작동하고 있다.

그러나, 메소드의 키워드 인수에 대해 Mutable한 값을 초기값으로써 전달한 경우에도 , 초기값으로써 전달되고 있음에도 불구하고 글로벌 scope로 취급된다는  사양이 있다.

(초기값으로써 전달하고 있으므로 전에 기술한 논리로 봤을 때 로컬 scope가 될 덧 같지만, 그렇지 않다.)

class TestClass(object):
    def __init__(self):
        self.common_var = None
    def set_variable(self, values=[]):
        values.append("Set Variable")
        self.common_var = values

tc1 = TestClass()
tc2 = TestClass()
tc1.set_variable()
tc2.set_variable()
print(tc2.common_var)

>>> ['Set Variable', 'Set Variable']

보통의 경우 'tc2.set_variable()'은 한 번밖에 호출되지 않고 있기 때문에 리스트 안에 하나의 요소가 있을 것으로 기대했지만, 'tc1.set_variable()'에 append한 데이터도 함께 포함되고 말았다.

키워드 인수의 평가는 클래스가 호출된 후 한 번밖에 실행되지 않고, 그 후에는 글로벌 scope로써 취급되고 있다는 것을 알 수 있다. 

("평가되지 않은 = 초기화되어 있지 않은"이므로, 전에 말한 이론으로 부터 글로벌 scope로써 취급될 수 있다는 것을 알 수 있다. )

이와 같이 Python은 변수의 scope가 애매하므로, 특히 Mutable한 변수(대입되지 않고 사용된다고 예상되는)의 경우는 초기화를, 클래스 변수를 취급하는 경우는 클래스를 지정하여 조작하는 것을 의식할 필요가 있다. 


참고자료

https://www.codingfactory.net/10401

https://qiita.com/msssgur/items/12992fc816e6adf32cff

 
728x90