python

함수 내부에서 외부 변수에 접근하는 방법

전라남도교육지원청 2023. 11. 4. 18:26

기본적으로 함수는 완전히 독립되어야 다루기 편리하다. 함수가 외부 변수에 종속되어버리면 함수를 고칠 때 외부 변수도 함께 고려해야 하고 그렇게 되면 하나의 함수를 수정하는 것이 코드 전체를 아우르는 작업이 될 수 있다. 가독성이 매우 떨어지며 디버깅에도 굉장한 혼란이 올 수 있다. 하지만 파이썬을 처음 다뤄보는 사람으로써, 아직 프로그래밍 언어에 대해 충분히 익숙해지지 않아 함수의 독립성을 완전히 이룰 수 없는 나로써는 함수가 외부의 변수에 접근할 수 있도록 해야하는 경우가 있다.
 

함수 밖에서 선언된 김태진은 교수이지만 함수 안에서 선언된 김태진은 고졸 학부생이다.

예를 들어 깊이 우선 탐색을 실행하는 아래 코드

#1520: 내리막길
M, N = map(int, input().split())
board = []
check = [[False for j in range(N)] for i in range(M)]
d = [[-1,0],[0,1],[1,0],[0,-1]]
for i in range(M):
    board.append(list(map(int, input().split())))

count = 0
def dfs(x, y):
    check[x][y] = True
    if x == M - 1 and y == N - 1:
        global count
        count += 1
        return
    for i in range(4):
        dx = x + d[i][0]
        dy = y + d[i][1]
        if dx < 0 or dy < 0 or dx >= M or dy >= N:
            continue
        if check[dx][dy]:
            continue
        if board[x][y] > board[dx][dy]:
            dfs(dx, dy)
            check[dx][dy] = False

dfs(0,0)
print(count)

이 코드는 백준에서 시간초과를 받은 코드이지만 살펴볼 부분이 있다. 이 코드의 깊이우선탐색에서 해를 찾은 경우 count 변수를 올려주어야 한다. 가장 먼저 떠오른 생각은 해를 찾은 경우 함수 안에서 바로 변수에 접근해 값을 1씩 올려주는 것이다.
 
함수에서 외부 변수에 접근하는 경우를 몇 가지 살펴보자
 

1. 외부 변수에 접근하는 경우

a = 10

def Print():
    print(a)

Print()
print(a)

결과
10
10

외부 정수형 변수는 읽어올 수 있다. Print 함수 안에서 print(a)는 정상적으로 외부 변수 a를 불러와 출력했다.

a = 10

def Write(k):
    a = k

Write(5)
print(a)

결과
10

외부 정수형 변수의 값을 수정할 수 없다. Write 함수 안에서 a = k로 바뀌었으나 함수 내부 a의 주소와 함수 외부 a의 주소는 다르다. 파이썬은 이렇게 함수 내외에 같은 이름의 변수를 선언하고 각각 다루는 것이 가능하다.
 

a = 10

def function(k):
    print(a)
    a = k

function(5)
print(a)

결과
UnboundLocalError: local variable 'a' referenced before assignment

이번에는 함수에 읽고 쓰는 기능을 모두 넣었다. 여기서는 a = k 때문에 함수 내에서 a를 선언하기 전 print(a)를 실행할 수 없었다.
 

a = 10

def function(k):
    a = k
    print(a)

function(5)
print(a)

결과
5
10

이렇게 함수 내에서 외부 변수와 같은 이름의 변수를 사용하는 경우, 함수 내에서 새로운 주소를 지정해 따로 처리하게 된다.
 
이 결과는 float, boolean, str 등에도 똑같이 적용된다.

2. 외부 리스트에 접근하는 경우

a = [1, 2, 3, 4, 5]

def function(k):
    a[k] = 0

function(3)
print(a)

결과
[1, 2, 3, 0, 5]

하지만 리스트에서는 문제 없이 작동한다.
 

a = [1, 2, 3, 4, 5]

def function(k):
    a = [5, 4, 3, 2, 1]
    a[k] = 0
    print(a)

function(3)
print(a)

결과
[5, 4, 3, 0, 1]
[1, 2, 3, 4, 5]

그런데 리스트 안에서 같은 이름을 갖는 새로운 리스트를 만들어버리면 역시 리스트도 새로운 주소를 갖게 된다.


그렇다면 외부 변수에 접근해 값을 바꿀 수 있는 방법은 무엇이 있을까? 간단하게 실행할 수 있는 방법은 세 가지인 것 같다.

1. return으로 데이터 반환하기

a = 10

def function(k):
    return k

print(a)
a = function(3)
print(a)

결과
10
3

정확히 따지면 함수 내에서 데이터에 접근해 수정한 것은 아니지만 이렇게 함수 내부의 값을 외부로 전달할 수 있다.
 

2. global 키워드로 전역 변수 사용하기

상술한 이유로 많은 개발자들이 비추하는 방법이다. 일단 대충 생각해봐도 이 방법으로 개발을 한다면 1000줄 정도만 넘어가도 스파게티 코드가 될 것 같다. 하지만 간단한 문제 해결에 필요한 수십줄 안팎의 코딩에서는 전역 변수 몇 개 쯤 사용해도 큰 문제는 없다(고 생각하고 싶음).

a = 10

def function(k):
    global a
    a = k

print(a)
function(50)
print(a)

결과
10
50

global 키워드를 사용해 변수를 수식해주면 그 변수는 전역 변수로 처리된다. 하지만 해당 함수에서만 전역변수로 취급되기 때문에 외부에 있는 변수를 여러 함수에서 다루고 싶다면 각 함수에서 전역 변수로 수식해주어야 한다.

a = 10

def function(k):
    global a
    a = k

def function2(k):
    a = k

print(a)
function(50)
print(a)
function2(100)
print(a)

결과
10
50
50

function2에서는 a가 전역 변수로 처리되지 않으므로 function2(100)이 외부 변수 a의 값을 변경하지 않았다.
 

3. 리스트 사용하기

a = [10, 20, 30]

def function(index, k):
    a[index] = k

print(a)
function(1, 50)
print(a)

결과
[10, 20, 30]
[10, 50, 30]

함수 외부에 있는 리스트는 별도의 처리 없이 바로 접근해 읽고 쓰기가 모두 가능하다. 함수 내에서 다뤄야 할 변수가 있다면 그 변수를 리스트로 처리하는 것이 나을 수도 있다. 리스트 외에 tuple, dict 등의 자료형에서도 적용되는지는 확인해보지 않았다.


결론

파이썬에서 각 데이터 타입이 갖는 특성이 무엇인지 아직 자세히는 모르지만 실험해본 결과는 이렇다.

  1. 함수 내에서 외부 변수를 읽어올 수 있다.
  2. 함수 내에서 외부 변수와 같은 이름의 변수를 선언하면 별개의 변수로 처리된다.
  3. 리스트는 함수 내에서도 읽고 쓰기가 가능하다.
  4. 하지만 리스트도 함수 내에서 새롭게 선언하면 별개의 리스트로 처리된다.

따라서 함수에서 외부의 변수에 접근하려면

  1. return으로 값을 반환해 외부에 전달한다.
  2. global 키워드로 변수를 전역 변수로 처리한다.
  3. 리스트를 활용한다.

중에서 적절한 방법을 활용하면 되겠다.