Python/2.7 information

파이썬의 변수에 관하여.

qkqhxla1 2016. 9. 17. 15:30

파이썬으로 코딩을 하다 보면 이런 경우가 있다.


앞의 TSP문제를 조금 바꿨다. http://qkqhxla1.tistory.com/677


아래의 코드에서 난 global_ret이라는 전역변수에 tsp의 경로 정보를 담고 싶다.


그래서 경로를 다 돌았으면 리턴되기 전에 경로를 전역변수인 global_ret에 저장해놨다.


그러고 마지막에 global_ret을 출력해보면 함수 안에서 경로 정보를 할당했으니 경로 정보가 있을거라고


예측할수있다. 그런데... 뭔가 이상하다.

아래의 출력부를 보면 final_path = 으로 path값이 공백이 아니라는걸 확인하기위해 출력해봤다.


공백이 아닌걸 확인후 바로 아랫줄에 global_ret에 path를 할당해줬다. 그런데 맨 마지막에 global_ret


을 출력해보면 공백 리스트가 나타난다. 분명히 공백 리스트가 없는걸 print로 출력해서 확인해줬고,


할당도 제대로 해줬는데 왜 이런걸까.?

# -*- encoding: cp949 -*-
INF = 999999999
def TSP(city, visited, path):
    global dp
    global global_ret #global한 global_ret 변수를 쓰겠다.
    if visited == ((1<<n)-1): 
        if arr[city][0] != 0: 
            print 'final_path = {}'.format(path) #출력 확인부분 
            global_ret = path #할당부분
            return arr[city][0] 
        return INF 
    ret = dp[city][visited]
    if ret != INF: return ret 
 
    for i in xrange(n): 
        if (visited & (1<<i)) or arr[city][i]==0: continue 
        path.append(i)
        ret = min(ret, TSP(i, visited | (1<<i), path) + arr[city][i]) 
        path.remove(i)
    dp[city][visited] = ret 
    return ret
 
n = input()
arr = [map(int,raw_input().split())for i in xrange(n)] 
dp = [[INF for j in xrange(65536)]for i in xrange(16)] 
global_ret = '' #전역변수 global_ret
print TSP(0,1,[]) 
print 'global_ret = {}'.format(global_ret)

이유를 찾아봤다.


http://stackoverflow.com/questions/17686596/function-changes-list-values-and-not-variable-values-in-python


첫번째 답변의 일부.

Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the = operation doesn't "assign a value to a variable," but rather "binds a name to an object."

파이썬 변수는 객체에 대한 포인터나 참조를 포함합니다. 변수는 다 객체이며, 변수를 할당하는것은 다른 객체로 바꾼다는겁니다. 이건 변수에 새 값을 할당하는게 아니라 변수의 참조나 포인터를 다른 객체로 바꾸는겁니다. 이러한 이유로 개발자들이 파이썬은 변수가 없고, 이름이 있다고합니다. 그리고 =연산자는 변수에 값을 할당하는게 아니라 이름을 객체에 연결시키는겁니다. 


If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:] for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:] if he wants a copy made.

위에서 설명한 현상을 겪기 싫으면 복사본을 만들어두세요. 그러면 복사본을 사용하기 때문에 제대로 동작할겁니다. ~~ 복사는 리스트의 경우 x = x[:]처럼 사용할수 있습니다.


[:]문법으로 복사해서 전달하면 된다. tsp를 호출하는 부분에서 복사본을 전달한다. 위의 코드에서 전달부만 이렇게 바꿔준다.


ret = min(ret, TSP(i, visited | (1<<i), path[:]) + arr[city][i]) 


그러면 global_ret이 제대로 출력되는걸 확인할수 있다. (아니면 import copy; copy.copy(path)로 깊은복사를 해줘도 된다.)