참조로 Python 함수 호출


82

일부 언어에서는 ref 또는 val 과 같은 특수 예약어를 사용하여 참조 또는 값으로 매개 변수를 전달할 수 있습니다 . Python 함수에 매개 변수를 전달하면 함수를 떠날 때 매개 변수 값이 변경되지 않습니다.이를 수행하는 유일한 방법은 전역 예약어 를 사용하는 것입니다 (또는 현재 이해하고 있음).

예 1 :

k = 2

def foo (n):
     n = n * n     #clarity regarding comment below
     square = n
     return square

j = foo(k)
print j
print k

보여줄 것이다

>>4
>>2

k가 변경되지 않음을 표시

이 예에서 변수 n은 변경되지 않습니다.

예 2 :

n = 0
def foo():
    global n
    n = n * n
    return n

이 예에서는 변수 n이 변경됩니다.

Python에서 함수를 호출 하고 매개 변수 가 전역을 사용하는 대신 또는 참조 매개 변수 임을 Python에 알리는 방법 있습니까?

둘째, A 레벨 Cambridge 시험에서 이제 함수는 단일 값을 반환하는 반면 프로시 저는 둘 이상의 값을 반환한다고 말합니다 . 80 년대에는 함수에 return 문이 있고 절차에는없는 것을 배웠습니다. 이제 이것이 잘못된 이유는 무엇입니까?


5
파이썬의 호출 모델을 이해하기 위해 찾은 최고의 리소스는 effbot에 대한이 기사입니다. effbot.org/zone/call-by-object.htm
Wilduck

1
파이썬 변수, 변경 가능 및 불변 객체에 대해 읽어야합니다. 또한 왜 n변경되는 첫 번째 예 에서는 square계산 결과를 저장하기 위해 새 변수 를 사용하고 있습니다.
Facundo Casco 2012

6
관련없는 두 개의 질문을 하나로 게시하지 마십시오. 함수 대 절차의 정의는 정의의 문제이며 귀하의 정의는 파스칼에 편향된 것 같습니다. Python 전문 용어에는 절차와 같은 것이 없습니다.
Fred Foo 2011

2
또한이 이전 StackOverflow 답변을 살펴보십시오. stackoverflow.com/a/10262945/173292 객체 참조 모델에 의한 호출을 상당히 직관적으로 설명합니다.
Wilduck 2011

1
매우 유용한 설명이 SO의 질문을 참조 : I 참조로 변수를 전달 어떻게
마이클 Ohlrogge에게

답변:


78

Python의 함수 내에서 str또는 같은 불변 객체를 변경할 수는 없지만 다음과 같은 tuple작업을 수행 할 수 있습니다.

def foo(y):
  y[0] = y[0]**2

x = [5]
foo(x)
print x[0]  # prints 25

그러나 배열의 특정 요소를 항상 제곱 할 필요가없는 한 이상한 방법입니다.

Python에서는 둘 이상의 값을 반환 할 수도 있으므로 참조에 의한 전달의 일부 사용 사례가 덜 중요합니다.

def foo(x, y):
   return x**2, y**2

a = 2
b = 3
a, b = foo(a, b)  # a == 4; b == 9

이와 같은 값을 반환하면 튜플로 반환되고 차례로 풀립니다.

편집 : 이것에 대해 생각하는 또 다른 방법은 파이썬에서 참조로 변수를 명시 적으로 전달할 수는 없지만 전달 된 객체의 속성을 수정할 수 있다는 것입니다. 제 예 (및 기타)에서는 목록의 구성원을 수정할 수 있습니다. 그러나 전달 된 변수를 완전히 재 할당 할 수는 없습니다. 예를 들어 다음 두 코드는 비슷한 작업을 수행하지만 결과가 다른 것처럼 보입니다.

def clear_a(x):
  x = []

def clear_b(x):
  while x: x.pop()

z = [1,2,3]
clear_a(z) # z will not be changed
clear_b(z) # z will be emptied

2
프로그램의 결과를 보는 것이 도움이 될 것입니다. 예를 들어 'print x [0]'은 25를 반환합니까? (예,하지만 표시되어야합니다.)
Ray Salemi

11
@alexey clear_a에 대한 참조가 제공 z되고 해당 참조를 x. 그런 다음 즉시 x다른 배열을 참조하도록 변경 됩니다. 원래 z배열은의 범위에서 잊혀지지 clear_a만 실제로 변경되지는 않습니다. 전역 범위로 돌아갈 때 변경되지 않고 계속됩니다. 새로운 배열을 만들거나 다른 방식으로 다른 것을 가리 키지 않고 clear_b참조를 취한 z다음 직접 작동 하는 것과 대조하십시오 x.
dave mankoff

고마워 데이브! 나는 clear_b에서 "새 배열을 생성"시도 y = x, y: y.pop()다음 전화 clear_b(z)처럼 우리가 뭔가가 필요 그래서, z는 여전히 ... 비워있어 y = list(x)(설명 목록 (x)는 X의 사본을 만들려면 여기에 )
알렉세이

run_thread = [True] t= threading.Thread(target=module2.some_method, \ args=(11,1,run_thread)) --do something - and when you want to sop run_thread[0] =False
Alex Punnen 2019

Java와 달리 Python에는 기본 유형이 없습니다. 모든 유형의 인스턴스 객체.
Marco Sulla

181

본질적으로 세 종류의 '함수 호출'이 있습니다.

  • 가치로 전달
  • 참조로 전달
  • 개체 참조로 전달

Python은 PASS-BY-OBJECT-REFERENCE 프로그래밍 언어입니다.

첫째, 변수와 변수 (객체)의 값은 별개의 것임을 이해하는 것이 중요합니다. 변수는 개체를 '가리 킵니다'. 변수는 개체가 아닙니다. 다시:

변수는 객체가 아닙니다

예 : 다음 코드 줄에서 :

>>> x = []

[]은 빈 목록이고, 빈 목록 x을 가리키는 변수이지만 x그 자체는 빈 목록이 아닙니다.

변수 ( x위의 경우)를 상자로, 변수 ( [])의 '값'을 상자 안의 개체로 간주합니다.

객체 참조로 통과 (파이썬의 경우) :

여기에서 "개체 참조는 값으로 전달됩니다."

def append_one(li):
    li.append(1)
x = [0]
append_one(x)
print x

여기서 문 x = [0]x객체를 가리키는 변수 (상자)를 만듭니다 [0].

호출되는 함수에서 새 상자 li가 생성됩니다. 의 내용물은 li상자 내용물과 동일합니다 x. 두 상자 모두 동일한 개체를 포함합니다. 즉, 두 변수 모두 메모리에서 동일한 객체를 가리 킵니다. 따라서에서 가리키는 객체에 대한 변경 사항은에서 가리키는 객체 li에도 반영됩니다 x.

결론적으로, 위 프로그램의 출력은 다음과 같습니다.

[0, 1]

노트 :

변수 li가 함수에서 재 할당 되면 li메모리에있는 별도의 개체를 가리 킵니다. x그러나 이전에 가리키는 메모리의 동일한 개체를 계속 가리 킵니다.

예:

def append_one(li):
    li = [0, 1]
x = [0]
append_one(x)
print x

프로그램의 출력은 다음과 같습니다.

[0]

참조로 통과 :

호출 함수의 상자가 호출 된 함수로 전달됩니다. 암시 적으로 상자의 내용 (변수 값)은 호출 된 함수에 전달됩니다. 따라서 호출 된 함수의 상자 내용에 대한 변경 사항은 호출 함수에 반영됩니다.

가치로 통과 :

호출 된 함수에 새 상자가 생성되고 호출 함수 의 상자 내용 사본 이 새 상자에 저장됩니다.

도움이 되었기를 바랍니다.


3
"객체 참조로 전달"에서와 같이 "참조로 전달"및 "값으로 전달"의 예를 제공 할 수 있습니까?
TJain

@TJain 참조로 전달하고 값으로 전달하는 것은 C ++에서 볼 수 있습니다. 이 답변은 좋은 설명을 제공 : stackoverflow.com/a/430958/5076583
Shobhit 베르

3
나는 이것이 참조에 의한 전달과 어떻게 다른지 여전히 이해하지 못합니다. 이 답변은 동일한 의사 코드가 세 가지 패러다임 모두에 대해 서로 다른 출력을 생성하는 방법에 대한 예가 절실히 필요합니다. 참조에 의한 전달과 값에 의한 전달의 차이점 만 설명하는 다른 답변에 연결하는 것은 도움이되지 않습니다.
Jess Riedel

"파이썬의 모든 변수는 포인터 "이고 "함수 호출의 매개 변수에 할당 된 모든 변수는 새 포인터에 할당됩니다 "라고 말하는 것이 더 간단하지 않습니까? 이것을 깨달으면 파이썬이 자바와 같다는 것을 알게 될 것입니다 : 그것은 단순히 항상 value로 포인터를 전달합니다 . 참조 커크 Strauser의 대답
마르코 술라

26

좋아, 내가 찔러 볼게. 파이썬은 일반적으로 "참조"또는 "값"으로 생각하는 것과는 다른 객체 참조를 통과합니다. 이 예를 보자 :

def foo(x):
    print x

bar = 'some value'
foo(bar)

따라서 값이 'some value'인 문자열 개체를 만들고라는 변수에 "바인딩"합니다 bar. C에서는 다음과 유사합니다.bar '어떤 값'에 대한 포인터 .

를 호출하면 그 자체로 foo(bar)전달되지 않습니다 bar. bar's value :'some value '에 대한 포인터를 전달하고 있습니다. 이 시점에서 동일한 문자열 개체에 대한 두 개의 "포인터"가 있습니다.

이제 다음과 비교하십시오.

def foo(x):
    x = 'another value'
    print x

bar = 'some value'
foo(bar)

여기에 차이점이 있습니다. 라인에서 :

x = 'another value'

실제로의 내용을 변경하지 않습니다 x. 사실 그것은 불가능합니다. 대신 '다른 값'값을 사용하여 새 문자열 개체를 만듭니다. 그 할당 연산자? " x새 값으로 가리키는 것을 덮어 쓴다"는 말이 아닙니다 . " x대신 새 개체를 가리 키도록 업데이트 "라는 의미입니다. 그 줄 뒤에는 'some value'( bar가리키는)와 'another value'(withx 가리키는 .

이것은 서투 르지 않습니다. 작동 방식을 이해하면 아름답고 우아하고 효율적인 시스템입니다.


3
하지만 어떤 경우 원하는 함수 내 변수의 내용을 변경? 나는 이것을 할 방법이 없다고 생각합니까?
aquirdturtle

1
이름 이 가리키는 객체를 변경할 수 있습니다 ( 예 : 해당 객체가 변경 가능한 경우 목록에 추가). "나를 호출 한 범위의 네임 스페이스를 변경하여 이제 내 인수 목록의 이름이 나를 호출했을 때와 다른 것을 가리 키도록 변경하십시오"라고 말하는 파이썬 의미론은 없습니다.
Kirk Strauser

2
실제로 내가 묻는 것은 파이썬이 동일한 객체에 대한 새 포인터를 생성하지 않고 대신 원래 포인터를 사용하는 간단한 방법이 있는지 여부입니다. 나는 이것을 할 방법이 없다고 생각합니다. 다른 답변에서 참조로 전달하는 작업을 수행하는 일반적인 해결 방법은 더 많은 매개 변수를 반환하는 것입니다. 이 경우 a, b, c, d, e = foo (a, b, c, d, e)가 foo (a, b, c, d, e).
aquirdturtle

2
@aquirdturtle 이것은 새로운 포인터가 생성된다는 사실에 의해 할 수있는 것이 아닙니다. strings, tuples 및 기타 객체는 변경할 수 없기 때문에 새 객체를 반환해야합니다 . a와 같은 변경 가능한 객체를 전달하면 list모든 함수 매개 변수가 전달한 동일한 객체에 대한 포인터이기 때문에 반환 할 필요없이 함수 내에서 변경할 수 있습니다.
Marco Sulla

23

다음 설명이 잘 요약되기를 바랍니다.

여기서 고려해야 할 두 가지 사항이 있습니다. 변수와 객체입니다.

  1. 변수를 전달하는 경우 값으로 전달됩니다. 즉, 함수 내의 변수에 대한 변경 사항은 해당 함수에 국한되어 있으므로 전역 적으로 반영되지 않습니다. 이것은 'C'와 같은 행동에 가깝습니다.

예:

def changeval( myvar ):
   myvar = 20; 
   print "values inside the function: ", myvar
   return

myvar = 10;
changeval( myvar );
print "values outside the function: ", myvar

O / P :

values inside the function:  20 
values outside the function:  10
  1. 목록과 같이 변경 가능한 객체 내에 패킹 된 변수를 전달하는 경우 객체가 다시 할당되지 않는 한 객체에 대한 변경 사항이 전역 적으로 반영됩니다.

예:

def changelist( mylist ):
   mylist2=['a'];
   mylist.append(mylist2);
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist

O / P :

values inside the function:  [1, 2, 3, ['a']]
values outside the function:  [1, 2, 3, ['a']]
  1. 이제 개체가 다시 할당되는 경우를 고려하십시오. 이 경우 객체는 이것이 발생하는 기능에 로컬 인 새 메모리 위치를 참조하므로 전역 적으로 반영되지 않습니다.

예:

def changelist( mylist ):
   mylist=['a'];
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist

O / P :

values inside the function:  ['a']
values outside the function:  [1, 2, 3]

2
이것이 최고의 요약입니다! ^^^ 여기에 최고의 요약 ^^^ 모두가이 답변을 읽었습니다! 1. 간단한 변수를 함수로 재 할당된다 3. 패스 오브젝트 참조 수정 2. 패스 오브젝트 참조 통과
gaoithe

3
이 대답은 매우 혼란 스럽습니다. Python은 다른 종류의 값을 다르게 전달하지 않습니다. Python의 모든 함수 인수는 할당을 통해 값을 수신하고 할당이 언어의 다른 곳에서 작동하는 방식과 정확히 동일하게 작동합니다. ( "네덜란드 사람이 아니라면 처음에는 그 방법이 분명하지 않을 수도 있지만.")
Daniel Pryden

9

Python은 값에 의한 전달도 참조에 의한 전달도 아닙니다. 그것은 더의의 "개체 참조가 값에 의해 전달된다" 와 같이 여기 :

  1. 이것이 가치에 의한 전달이 아닌 이유입니다. 때문에

    def append(list):
        list.append(1)
    
    list = [0]
    reassign(list)
    append(list)
    

값에 의한 전달이 함수가 부모 범위를 전혀 변경하지 못하도록 허용하므로 일종의 참조가 명확하게 전달되었음을 나타내는 [0,1]을 반환 합니다. .

참조에 의한 통과처럼 보입니다, 응? 아니.

  1. 이것이 참조에 의한 통과가 아닌 이유입니다. 때문에

    def reassign(list):
      list = [0, 1]
    
    list = [0]
    reassign(list)
    print list
    

목록이 재 할당 될 때 원래 참조가 삭제되었음을 나타내는 [0]을 반환합니다. 참조에 의한 전달은 [0,1]을 반환했을 것입니다. 입니다.

자세한 내용은 여기 를 참조 하십시오. .

함수가 범위 밖에서 조작하지 않도록하려면 새 개체를 만드는 입력 매개 변수의 복사본을 만들어야합니다.

from copy import copy

def append(list):
  list2 = copy(list)
  list2.append(1)
  print list2

list = [0]
append(list)
print list

4

기술적으로 파이썬은 값으로 인자를 전달하지 않습니다 : 모두 참조로. 하지만 ... 파이썬에는 두 가지 유형의 객체가 있습니다 : 불변 및 변경 가능하므로 다음과 같은 일이 발생합니다.

  • 변경 불가능한 인수는 값으로 효과적으로 전달됩니다 . 문자열, 정수, 튜플은 모두 변경 불가능한 객체 유형입니다. 기술적으로는 (모든 매개 변수와 마찬가지로) "참조로 전달"되지만 함수 내에서 제자리에서 변경할 수 없기 때문에 마치 값으로 전달 된 것처럼 보이거나 작동합니다.

  • 가변 인수는 효과적으로 참조로 전달됩니다 . 목록 또는 사전은 포인터에 의해 전달됩니다. (append 또는 del)과 같은 함수 내부의 모든 변경 사항은 원래 개체에 영향을줍니다.

이것이 파이썬이 디자인 된 방식입니다. 복사본이없고 모두 참조로 전달됩니다. 복사본을 명시 적으로 전달할 수 있습니다.

def sort(array):
    # do sort
    return array

data = [1, 2, 3]
sort(data[:]) # here you passed a copy

마지막으로 어떤 함수가 자체 범위를 가지고 있는지 언급하고 싶습니다.

def do_any_stuff_to_these_objects(a, b): 
    a = a * 2 
    del b['last_name']

number = 1 # immutable
hashmap = {'first_name' : 'john', 'last_name': 'legend'} # mutable
do_any_stuff_to_these_objects(number, hashmap) 
print(number) # 1 , oh  it should be 2 ! no a is changed inisde the function scope
print(hashmap) # {'first_name': 'john'}

1
"불변 인수는 값에 의해 효과적으로 전달됩니다 : 문자열, 정수, 튜플은 모두 참조로 전달됩니다."이 문장은 자기 모순
eral

@eral, 전체 컨텍스트를 이해하면 자체적으로 모순되지 않습니다. 나는 이것을 더 명확하게하기 위해 그것을 편집했다.
Watki02

2

그래서 이것은 약간 미묘한 요점입니다. 왜냐하면 파이썬은 변수를 값으로 만 전달하지만 파이썬의 모든 변수는 참조이기 때문입니다. 함수 호출로 값을 변경할 수 있도록하려면 변경 가능한 객체가 필요합니다. 예를 들면 :

l = [0]

def set_3(x):
    x[0] = 3

set_3(l)
print(l[0])

위의 코드에서 함수는 List 객체 (변경 가능)의 내용을 수정하므로 출력은 0 대신 3입니다.

이 답변은 파이썬에서 '값별'이 의미하는 바를 설명하기 위해서만 작성합니다. 위의 코드는 잘못된 스타일이며 값을 변경하려면 MPX가 제안하는 것처럼 클래스를 작성하고 해당 클래스 내에서 메서드를 호출해야합니다.


이것은 질문의 절반에 대답하므로 l의 값을 변경하지 않고 변경된 새 목록을 반환하도록 동일한 set_3 (x) 함수를 어떻게 작성합니까?
Timothy Lawman 2012

새 목록을 만들고 수정하십시오.y = [a for a in x]; y[0] = 3; return y
Isaac

모든 것은 목록과 복사 목록을 포함하며 어떤 방식으로 대답 할 수 있지만 int를 목록으로 변환하지 않고 어떻게 할 수 있습니까? 아니면 이것이 최선입니까?
Timothy Lawman

int와 같은 기본 인수를 수정할 수 없습니다. 일종의 용기에 넣어야합니다. 내 예는 목록에 넣었습니다. 수업이나 사전에 넣을 수도 있습니다. 하지만 실제로는 .NET과 같은 함수 외부에서 다시 할당해야합니다 x = foo(x).
Isaac

0

주어진 대답은

def set_4(x):
   y = []
   for i in x:
       y.append(i)
   y[0] = 4
   return y

l = [0]

def set_3(x):
     x[0] = 3

set_3(l)
print(l[0])

질문에서 말하는대로하는 한 가장 좋은 대답입니다. 하지만 VB 나 Pascal에 비해 매우 서투른 방법 인 것 같습니다.

어색 할뿐만 아니라 원래 매개 변수를 목록으로 변경하는 등 수동으로 원래 매개 변수를 변경하는 작업이 포함됩니다 . "이 매개 변수를 값으로 사용"또는 "이 매개 변수를 사용합니다. 참고로 ". 간단한 대답은 이것에 대한 예약어없지만 훌륭한 해결 방법 일 수 있습니까?


0

변수가 상자이고 가리키는 값이 상자 내부의 "사물"임을 고려하십시오.

1. 참조로 전달 : 함수는 동일한 상자를 공유하므로 내부도 공유합니다.

2. 값으로 전달 : 함수는 그 안에있는 모든 것의 복사본을 포함하여 이전 상자의 복제 본인 새 상자를 만듭니다. 예 : Java- 함수는 상자의 복사본을 만들고 그 안에는 기본 요소 / 객체에 대한 참조 일 수 있습니다. (새 상자와 원본의 복사 된 참조는 여전히 동일한 개체를 가리키고 있습니다. 여기서 참조는 가리키는 개체가 아니라 상자 내부의 것입니다.)

3. 객체 참조로 전달 : 함수는 상자를 생성하지만 초기 상자가 둘러싸고 있던 것과 동일한 것을 포함합니다. 그래서 파이썬에서 :

a) 해당 상자 내부의 항목이 변경 가능한 경우 경우 변경 사항은 원래 상자 (예 : 목록)에 다시 반영됩니다.

b) 사물이 불변 인 경우 (파이썬 문자열 및 숫자 유형과 같이), 함수 내부의 상자는 값을 변경하려고 할 때까지 동일한 것을 보유합니다. 일단 변경되면 함수의 상자에있는 것은 원래의 것과 비교하여 완전히 새로운 것입니다. 따라서 해당 상자의 id ()는 이제 상자가 포함하는 새 항목의 ID를 제공합니다 .


0
class demoClass:
    x = 4
    y = 3
foo1 = demoClass()
foo1.x = 2
foo2 = demoClass()
foo2.y = 5
def mySquare(myObj):
    myObj.x = myObj.x**2
    myObj.y = myObj.y**2
print('foo1.x =', foo1.x)
print('foo1.y =', foo1.y)
print('foo2.x =', foo2.x)
print('foo2.y =', foo2.y)
mySquare(foo1)
mySquare(foo2)
print('After square:')
print('foo1.x =', foo1.x)
print('foo1.y =', foo1.y)
print('foo2.x =', foo2.x)
print('foo2.y =', foo2.y)

-2

파이썬에서 참조 또는 값으로 전달하는 것은 전달하는 실제 객체와 관련이 있습니다. 따라서 예를 들어 목록을 전달하는 경우 목록이 변경 가능한 객체이기 때문에 실제로 참조로 전달합니다. 따라서 함수에 대한 포인터를 전달하고 함수 본문에서 객체 (목록)를 수정할 수 있습니다.

문자열을 전달할 때이 전달은 값으로 수행되므로 새 문자열 객체가 생성되고 함수가 종료되면 해당 객체가 소멸됩니다. 그래서 그것은 모두 변경 가능하고 불변하는 객체와 관련이 있습니다.


1
그것은 완전히 잘못된 것입니다. 파이썬은 항상 "객체 참조"(변수 참조와 다름)를 통과합니다.
Kirk Strauser 2012

나는 참조에 의한 전달과 불변의 객체가 가치에 의한 전달을 의미했다. 내가 말하는 것은 완벽하게 타당하다. 아마 내가 말하는 것을 이해하지 못했을 것이다
jkalivas

1
나는 그것을 잡았고 그것은 틀렸다. 객체가 변경 가능하든 불변하든 관계가 없습니다. 항상 같은 방식으로 이루어집니다.
Kirk Strauser 2012

-2

파이썬은 이미 ref ..

예를 들어 보겠습니다.

  def foo(var):
      print(hex(id(var)))


  x = 1 # any value
  print(hex(id(x))) # I think the id() give the ref... 
  foo(x)

산출

  0x50d43700 #with you might give another hex number deppend on your memory 
  0x50d43700

아니요, 사실이 아닙니다def foo(var): print(id(var)) var = 1234 print(id(var)) x = 123 print(id(x)) # I think the id() give the ref... foo(x) print(id(x))
Zohaib Ijaz 2017-08-23
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.