[python] 참조로 변수를 전달하는 방법은 무엇입니까?

파이썬 문서는 매개 변수가 참조 또는 값으로 전달되는지에 대해 명확하지 않은 것으로 보이며 다음 코드는 변경되지 않은 값 ‘Original’을 생성합니다.

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

실제 참조로 변수를 전달하기 위해 할 수있는 일이 있습니까?



답변

인수는 할당에 의해 전달됩니다 . 이것의 근거는 두 가지입니다.

  1. 전달 된 파라미터는 사실이다 기준 개체에 (그러나, 기준이되는 값으로 전달)
  2. 일부 데이터 유형은 변경 가능하지만 다른 유형은 변경 불가능

그래서:

  • 당신이 전달하는 경우 변경 가능한 방법으로 개체를, 방법은 같은 객체에 대한 참조를 가져옵니다 당신은 당신의 마음의 기쁨으로 변이 할 수 있지만,이 방법에 대한 참조를 바인딩하는 경우, 외부 범위는 후에 그것에 대해 아무것도 몰라, 것 완료되었으므로 외부 참조는 여전히 원래 객체를 가리 킵니다.

  • 당신이 전달하는 경우 불변 하는 방법에 오브젝트를, 당신은 여전히 외부 참조를 바인딩 할 수없고 심지어 개체를 변이 수 없습니다.

더 명확하게하기 위해 몇 가지 예를 들어 보겠습니다.

리스트-가변 타입

메소드에 전달 된 목록을 수정 해 봅시다 :

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

산출:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

전달 된 매개 변수 outer_list는 복사본이 아닌에 대한 참조 이므로 변경 목록 메소드를 사용하여 변경하고 외부 범위에 변경 사항을 반영 할 수 있습니다.

이제 매개 변수로 전달 된 참조를 변경하려고 할 때 어떤 일이 발생하는지 봅시다 :

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

산출:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

때문에 the_list매개 변수가 그것에 새 목록을 할당 값에 의해 전달 된 방법 이외의 코드를 볼 수 있다는 아무런 영향을 미치지 않습니다. 는 the_list의 복사본이었다 outer_list참조, 우리는 한 the_list새 목록에 지점을하지만, 변경할 수있는 방법이 없었다 outer_list뾰족한는.

문자열-불변의 타입

불변이므로 문자열의 내용을 변경하기 위해 할 수있는 일은 없습니다

이제 참조를 변경해 봅시다

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

산출:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

다시 말하지만 the_string매개 변수가 값으로 전달되었으므로 새 문자열을 할당하면 메서드 외부의 코드에서 볼 수 없었습니다. 는 the_string의 복사본이었다 outer_string참조, 우리는 한 the_string새로운 문자열로 포인트를하지만, 변경할 수있는 방법이 없었다 outer_string뾰족한는.

나는 이것이 약간의 정리를 바랍니다.

편집 : 이것은 @David가 처음에 “실제 참조로 변수를 전달하기 위해 할 수있는 일이 있습니까?”라는 질문에 대답하지는 않습니다. 그 작업을 해보자.

우리는 어떻게이 문제를 해결합니까?

@Andrea의 답변에서 알 수 있듯이 새로운 값을 반환 할 수 있습니다. 이것은 물건이 전달되는 방식을 바꾸지는 않지만 원하는 정보를 다시 얻을 수있게합니다.

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

반환 값을 사용하지 않으려면 값을 보유하고 함수에 전달하거나 목록과 같은 기존 클래스를 사용하는 클래스를 만들 수 있습니다.

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

비록 약간 성가신 것 같습니다.


답변

파이썬에서 변수가 무엇인지에 대한 오해에서 문제가 발생합니다. 대부분의 전통적인 언어에 익숙하다면 다음 순서로 일어나는 일에 대한 정신적 모델이 있습니다.

a = 1
a = 2

a그 값을 저장하는 메모리 위치 라고 생각 하고 값 1을 저장하도록 업데이트됩니다 2. 그것이 파이썬에서 일하는 방식이 아닙니다. 오히려 a값이있는 객체에 대한 참조로 1시작한 다음 값 이있는 객체에 대한 참조로 다시 할당됩니다 2. 이 두 개체는 a더 이상 첫 번째 개체를 참조하지 않더라도 계속 공존 할 수 있습니다 . 실제로 그것들은 프로그램 내에서 다른 많은 참조에 의해 공유 될 수 있습니다.

매개 변수를 사용하여 함수를 호출하면 전달 된 객체를 참조하는 새 참조가 작성됩니다. 이는 함수 호출에 사용 된 참조와 별개이므로 해당 참조를 업데이트하여 참조를 참조 할 수있는 방법이 없습니다. 새로운 객체. 귀하의 예에서 :

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable문자열 객체에 대한 참조 'Original'입니다. 호출 Change하면 var객체에 대한 두 번째 참조 를 만듭니다. 함수 내에서 var다른 문자열 객체에 대한 참조 를 다시 할당 'Changed'하지만 참조 self.variable는 분리되어 있으며 변경되지 않습니다.

이를 해결하는 유일한 방법은 변경 가능한 객체를 전달하는 것입니다. 두 참조 모두 동일한 객체를 참조하므로 객체에 대한 모든 변경 사항이 두 곳에 모두 반영됩니다.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'


답변

다른 답변은 다소 길고 복잡하다는 것을 알았 으므로이 간단한 다이어그램을 작성하여 Python이 변수 및 매개 변수를 처리하는 방식을 설명했습니다.
여기에 이미지 설명을 입력하십시오


답변

값으로 전달 또는 참조로 전달되지 않으며 개체 별 호출입니다. Fredrik Lundh의 이것을보십시오 :

http://effbot.org/zone/call-by-object.htm

중요한 인용문은 다음과 같습니다.

“… 변수 [이름]은 객체 가 아닙니다 . 다른 변수로 표시하거나 객체가 참조 할 수 없습니다.”

예를 들어, Change메소드가 호출되면 네임 스페이스 가 작성됩니다. 및 var문자열 객체에 대해, 해당 네임 스페이스 내에서 이름이됩니다 'Original'. 그런 다음 해당 개체는 두 개의 네임 스페이스에 이름을 갖습니다. 그런 다음 새 문자열 객체에 var = 'Changed'바인딩 var하므로 메서드의 네임 스페이스가 잊어 버립니다 'Original'. 마지막으로, 그 네임 스페이스는 잊혀지고 문자열도 'Changed'함께 잊어 버립니다 .


답변

참조 / 가치 대신 할당 으로 전달 되는 것을 생각하십시오 . 그렇게하면 정규 과제 동안 어떤 일이 발생하는지 이해하는 한 항상 명확합니다.

따라서리스트를 함수 / 메소드에 전달할 때리스트는 매개 변수 이름에 할당됩니다. 목록에 추가하면 목록이 수정됩니다. 함수 에서 목록을 다시 할당하면 다음과 같은 이유로 원래 목록이 변경되지 않습니다.

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

불변 유형은 수정할 수 없으므로 값으로 전달되는 것처럼 보입니다 . int를 함수에 전달하면 int를 함수의 매개 변수에 할당하는 것을 의미합니다. 다시 할당 할 수는 있지만 원래 변수 값은 변경되지 않습니다.


답변

Effbot (일명 Fredrik Lundh)은 Python의 변수 전달 스타일을 객체 별 호출로 설명했습니다. http://effbot.org/zone/call-by-object.htm

객체는 힙에 할당되며 포인터는 어디에서나 전달 될 수 있습니다.

  • 과 같은 할당을 수행 x = 1000하면 현재 네임 스페이스의 “x”문자열을 1000을 포함하는 정수 객체에 대한 포인터에 매핑하는 사전 항목이 생성됩니다.

  • 로 “x”를 업데이트 x = 2000하면 새 정수 객체가 만들어지고 새 객체를 가리 키도록 사전이 업데이트됩니다. 오래 된 천 개체는 변경되지 않습니다 (그리고 다른 것이 개체를 참조하는지 여부에 따라 살아 있거나 없을 수 있습니다).

  • 와 같은 새 할당을 수행하면 y = x“x”에 대한 항목과 동일한 객체를 가리키는 새 사전 항목 “y”가 생성됩니다.

  • 문자열 및 정수와 같은 객체는 변경할 수 없습니다 . 이것은 객체가 생성 된 후에 객체를 변경할 수있는 메소드가 없음을 의미합니다. 예를 들어, 정수 객체가 천 번 생성되면 절대 변경되지 않습니다. 수학은 새로운 정수 객체를 생성하여 수행됩니다.

  • 리스트와 같은 객체는 변경 가능 합니다. 이것은 객체의 내용이 객체를 가리키는 것으로 변경 될 수 있음을 의미합니다. 예를 들어 x = []; y = x; x.append(10); print y인쇄 [10]합니다. 빈 목록이 작성되었습니다. “x”와 “y”는 모두 동일한 목록을 가리 킵니다. APPEND의 방법 (데이터베이스에 레코드를 추가하는 등의) 변이 (업데이트) 목록 객체와 결과가 모두 “X”와 “Y”를 볼 수는 (단지 데이터베이스 업데이트로 해당 데이터베이스에 대한 모든 연결을 볼 것).

그것이 당신을 위해 문제를 분명히하기를 바랍니다.


답변

기술적으로 파이썬은 항상 pass by reference values를 사용합니다 . 본인의 진술을 뒷받침하기 위해 다른 답변 을 반복하겠습니다 .

파이썬은 항상 참조로 전달 값을 사용합니다. 예외는 없습니다. 모든 변수 할당은 참조 값을 복사하는 것을 의미합니다. 예외 없음. 모든 변수는 참조 값에 바인딩 된 이름입니다. 항상.

참조 값을 대상 개체의 주소로 생각할 수 있습니다. 주소는 사용될 때 자동으로 역 참조됩니다. 이런 식으로 참조 값으로 작업하면 대상 객체로 직접 작업하는 것처럼 보입니다. 그러나 목표를 향해 한 걸음 더 나아가는 것 사이에는 항상 참조가 있습니다.

다음은 파이썬이 참조로 전달을 사용한다는 것을 증명하는 예입니다.

인수 전달의 예를 보여줍니다.

인수가 값으로 전달되면 외부를 lst수정할 수 없습니다. 녹색은 대상 객체이며 (검정은 내부에 저장된 값, 빨간색은 객체 유형), 노란색은 참조 값이 내부에있는 메모리이며 화살표로 표시됩니다. 파란색 실선 화살표는 점선 파선 화살표 경로를 통해 함수에 전달 된 참조 값입니다. 못생긴 짙은 노란색은 내부 사전입니다. (실제로 녹색 타원으로 그릴 수도 있습니다. 색상과 모양은 내부라고 말합니다.)

id()내장 함수를 사용하여 참조 값이 무엇인지 (즉, 대상 객체의 주소)를 알 수 있습니다.

컴파일 된 언어에서 변수는 유형의 값을 캡처 할 수있는 메모리 공간입니다. Python에서 변수는 대상 객체에 대한 참조 값을 보유하는 참조 변수에 바인딩 된 이름 (내부적으로 문자열로 캡처 됨)입니다. 변수의 이름은 내부 사전의 키이며 해당 사전 항목의 값 부분은 참조 값을 대상에 저장합니다.

파이썬에서는 참조 값이 숨겨져 있습니다. 참조 값을 저장하기위한 명시적인 사용자 유형이 없습니다. 그러나 모든 컨테이너가 요소를 대상 객체에 대한 참조로도 저장하기 때문에 목록 요소 (또는 다른 적합한 컨테이너 유형의 요소)를 참조 변수로 사용할 수 있습니다. 즉, 요소는 실제로 컨테이너 안에 포함되지 않으며 요소에 대한 참조 만 있습니다.