python - 함수 - 파이썬 클래스 전역변수




참조로 변수를 전달하는 방법은 무엇입니까? (16)

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. 할당은 이름을 객체에 바인딩합니다.
  3. 인수를 함수에 전달하면 이름 (함수의 매개 변수 이름)을 오브젝트에 바인드합니다.

그게 전부입니다. 이 질문에 대해서는 돌연변이가 부적합합니다.

예:

a = 1

이것은 이름 1을 값 1을 보유하는 정수 유형의 오브젝트에 바인드합니다.

b = x

이것은 이름 x 가 현재 바인드 된 동일한 오브젝트에 이름 b 를 바인드합니다. 나중에 이름 b 는 이름 x 와 더 이상 관련이 없습니다.

파이썬 3 언어 레퍼런스에서 3.1 절과 4.2 절을 보라.

따라서이 질문에 표시된 코드에서 self.Change(self.variable) 문은 var 라는 var 'Original' 값을 가진 객체에 바인딩하고 var = 'Changed' ( 함수 Change 본문에서) 동일한 이름을 다시 할당합니다. 다른 객체 (문자열도 보유하지만 전체적으로 다른 것일 수 있음)에 대한 것입니다.


(편집 - 블레어 총리가 그의 인기있는 답변을 업데이트하여 정확한 결과를 얻었습니다)

나는 블로어 콘래드가 득표 한 가장 많은 표를 얻은 현직 포스트가 그 결과와 관련하여 오해의 소지가 있으며 그 정의에 기초하여 경계선이 틀렸다는 점에 유의하는 것이 중요하다고 생각한다. 사용자가 참조로 전달하거나 값으로 전달할 수있는 많은 언어 (예 : C)가 있지만 Python은 그 중 하나가 아닙니다.

David Cournapeau의 대답은 진정한 대답을 가리키며 정의가 올바르지 않은데 블레어 콘래드의 게시물이 올바른 것처럼 보이는 이유를 설명합니다.

파이썬이 값으로 전달되는 한도 내에서, 일부 데이터 ( "값"또는 "참조")를 전송해야하기 때문에 모든 언어가 값으로 전달됩니다. 그러나 그렇다고해서 C 프로그래머가 Python을 생각할 가치가 있다는 의미는 아닙니다.

그 행동을 원한다면, 블레어 콘래드의 대답은 괜찮습니다. 그러나 파이썬이 왜 가치로 전달되지 않거나 참조로 전달되지 않는지에 대한 자세한 내용을 알고 싶다면 David Cournapeau의 대답을 읽어보십시오.


값에 의한 전달도 아니고 참조에 의한 전달도 아니며 호출 별입니다. 프레드릭 룬트 (Fredrik Lundh)

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

중요한 견적은 다음과 같습니다.

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

예제에서 Change 메서드가 호출되면 namespace 가 만들어집니다. var 는 문자열 객체 'Original' 대해 해당 네임 스페이스 내에서 이름이됩니다. 그 객체는 두 개의 네임 스페이스에 이름을 가지고 있습니다. 다음으로, var = 'Changed' var 를 새로운 문자열 객체에 바인딩하므로 메서드의 네임 스페이스는 'Original' 잊어 버립니다. 마지막으로, 그 네임 스페이스는 잊어 버렸고, 문자열은 'Changed' 와 함께 그것을 잊었습니다.


기술적으로 파이썬은 항상 참조 값에 의한 패스를 사용합니다 . 내 진술을 뒷받침하는 다른 대답 을 반복하겠습니다.

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

참조 값은 대상 객체의 주소로 생각할 수 있습니다. 주소는 사용될 때 자동으로 역 참조됩니다. 이 방법으로 참조 값을 사용하여 작업하면 대상 개체와 직접 작업하는 것 같습니다. 그러나 그 사이에는 언제나 표적으로 건너 뛰기위한 한 단계 더 많은 참조가 있습니다.

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

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

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

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

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


다른 답변은 다소 길고 복잡하므로 Python이 변수와 매개 변수를 처리하는 방법을 설명하는 간단한 다이어그램을 만들었습니다.


문제는 파이썬에서 어떤 변수가 오해하는지에 대한 것입니다. 대부분의 전통적인 언어에 익숙하다면 다음과 같은 순서로 어떤 일이 일어날지를 생각할 수 있습니다.

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'

여기에 정말 좋은 답변이 있습니다.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

이 경우 Change 메서드의 var 이라는 변수에 self.variable 에 대한 참조가 지정되고 즉시 var 문자열을 할당합니다. 더 이상 self.variable 가리키지 않습니다. 다음 코드 스 니펫은 varself.variable 이 경우에는 목록)이 가리키는 데이터 구조를 수정하면 어떻게 될지 보여줍니다.

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['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를 함수 매개 변수에 할당하는 것을 의미합니다. 이 변수는 재 할당 만 할 수 있지만 원래 변수 값은 변경되지 않습니다.


파이썬의 pass-by-assignment scheme은 C ++의 reference parameters 옵션과 완전히 같지는 않지만 실제로는 C 언어 (와 다른 것들)의 인자 전달 모델과 매우 유사하다.

  • 변경할 수없는 인수는 효과적으로 " 값으로 "전달 됩니다 . 정수 및 문자열과 같은 개체는 복사하는 대신 개체 참조에 의해 전달되지만 아무렇게나 변경할 수없는 개체를 변경할 수 없기 때문에 효과는 복사본을 만드는 것과 같습니다.
  • 불변의 인수는 실제로 " 포인터에 의해 "전달 됩니다 . 목록 및 사전과 같은 객체도 C가 배열을 포인터로 전달하는 것과 유사한 객체 참조에 의해 전달됩니다. C 배열과 마찬가지로 함수에서 변경 가능한 객체를 제 위치에서 변경할 수 있습니다 .

참조에 의한 전달은 파이썬에 잘 들어 맞지 않고 거의 사용되지 않아야합니다. 실제로 현재 로컬 변수에 할당 된 객체를 얻거나 호출 된 함수 내부에서 로컬 변수를 재 할당하기 위해 실제로 작동 할 수있는 몇 가지 해결 방법이 있습니다.

기본 아이디어는 해당 액세스를 수행 할 수있는 함수를 가지며 다른 함수로 객체로 전달되거나 클래스에 저장 될 수 있습니다.

한 가지 방법은 래퍼 함수에서 ( global전역 변수의 경우) 또는 nonlocal(함수의 지역 변수의 경우) 사용하는 것입니다.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

같은 생각은 del변수 를 읽거나 eting하는 데 효과적입니다 .

그냥 읽으 lambda: x려는 경우, 호출 할 때 x의 현재 값을 반환하는 호출 가능 코드를 반환 하는 더 간단한 방법도 있습니다 . 이는 먼 과거의 언어에서 사용 된 "이름으로 호출"과 다소 비슷합니다.

변수에 액세스하기 위해 3 개의 래퍼를 전달하는 것은 약간 어렵 기 때문에 프록시 속성이있는 클래스로 래핑 될 수 있습니다.

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Python의 "reflection"지원은 해당 범위에서 명시 적으로 함수를 정의하지 않고 주어진 범위에서 이름 / 변수를 재 할당 할 수있는 객체를 얻을 수있게합니다.

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

여기서 ByRef클래스는 사전 액세스를 래핑합니다. 따라서 속성 액세스 wrapped는 전달 된 사전의 항목 액세스로 변환됩니다. 내장 locals변수 의 결과 와 지역 변수의 이름을 전달함으로써 지역 변수에 접근하게된다. 3.5 절의 파이썬 문서는 사전을 바꾸는 것이 효과가 없을 수도 있지만 저에게는 효과적이라고 조언합니다.


파이썬이 값과 참조를 처리하는 방식을 감안할 때 임의의 인스턴스 속성을 참조 할 수있는 유일한 방법은 이름을 사용하는 것입니다.

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

    def change(self, var):
        self.__dict__[var] = 'Changed'

실제 코드에서는 dict 조회에 오류 검사를 추가합니다.


다음 메소드를 사용하여 Fortran 코드 몇 개를 Python으로 신속하게 변환했습니다. 원래 질문이 제기되었으므로 참조로 전달되는 것은 아니지만 어떤 경우에는 간단한 해결 방법입니다.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

다음은 pass by object파이썬에서 사용 된 개념에 대한 간단한 설명입니다 .
객체를 함수에 전달할 때마다 객체 자체가 전달됩니다 (Python의 객체는 실제로이 객체에 대한 참조가 아닌 다른 프로그래밍 언어에서 값이라고 부릅니다). 즉, 전화 할 때 :

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

실제 객체 - [0, 1] (다른 프로그래밍 언어에서 값이라고도 함)이 전달됩니다. 그래서 실제로 함수 change_me는 다음과 같은 것을 시도 할 것입니다 :

[0, 1] = [1, 2, 3]

함수에 전달 된 객체는 분명히 변경되지 않습니다. 함수가 다음과 같이 보일 경우 :

def change_me(list):
   list.append(2)

그러면 호출 결과는 다음과 같습니다.

[0, 1].append(2)

분명히 객체를 변경할 것입니다. 이 대답은 그것을 잘 설명합니다.


여기에 대한 답변에 대한 많은 통찰력이 있지만 여기에 명시 적으로 명확하게 언급되지 않은 추가 점이 있다고 생각합니다. 파이썬 문서에서 인용하기 https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

"Python에서 함수 내에서만 참조되는 변수는 암시 적으로 전역 변수입니다. 변수가 함수 본문 내에서 새 값으로 지정되면 해당 변수는 로컬 변수로 간주됩니다. 변수가 함수 내에 새로운 값이 할당 된 경우, 변수는 암시 적으로 지역 변수이고 명시 적으로 '전역 변수'로 선언해야합니다. 처음에는 약간 놀랍지 만 잠시 생각해 보면 설명되어 있습니다. 한편으로는 할당 된 변수에 대해 전역 변수를 요구하면 의도하지 않은 부작용을 막을 수 있습니다. 반면에 전역 참조가 필요하다면 전역 적으로 사용하는 것이 좋습니다. 내장 함수 나 가져온 모듈의 구성 요소에 대한 모든 참조를 전역으로 선언해야합니다.이 혼란은 부작용을 확인하기위한 국제 선언의 유용성을 무너 뜨릴 것입니다. "

가변 객체를 함수에 전달하는 경우에도 여전히 적용됩니다. 그리고 나에게는 객체에 할당하고 함수에서 객체를 조작하는 사이의 동작의 차이에 대한 이유를 명확하게 설명합니다.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

제공 :

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

따라서 전역 변수로 선언되지 않은 전역 변수에 할당하면 새 로컬 객체가 만들어지고 원래 객체에 대한 링크가 끊어집니다.


이 것들이 파이썬에서 어떻게 작동하는지에 대한 모든 위대한 설명을 제외하고, 나는이 문제에 대한 간단한 제안을 보지 못했다. 객체와 인스턴스를 생성하는 것처럼 보이기 때문에 인스턴스 변수를 처리하고 변경하는 비법은 다음과 같습니다.

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

    def Change(self):
        self.variable = 'Changed'

인스턴스 메소드에서는 보통 self인스턴스 속성에 액세스 하기 위해 참조 합니다. 인스턴스 속성을 설정 __init__하고 인스턴스 메소드에서 인스턴스 속성을 읽거나 변경하는 것은 일반적 입니다. 그것은 또한 왜 당신 self이 첫 번째 주장을 전달하는지에 대한 것 def Change입니다.

또 다른 해결책은 다음과 같이 정적 메서드를 만드는 것입니다.

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

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var




pass-by-reference