python pandas 데이터 프레임, 값에 의한 전달 또는 참조에 의한 전달


84

데이터 프레임을 함수에 전달하고 함수 내에서 수정하면 값에 의한 전달 또는 참조에 의한 전달입니까?

다음 코드를 실행합니다.

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
    df = df.drop('b',axis=1)
letgo(a)

의 값은 a함수 호출 후에 변경되지 않습니다. 가치에 의한 전달이라는 의미입니까?

나는 또한 다음을 시도했다

xx = np.array([[1,2], [3,4]])
def letgo2(x):
    x[1,1] = 100
def letgo3(x):
    x = np.array([[3,3],[3,3]])

그것은 밝혀 letgo2()변화를 수행 xx하고 letgo3()있지 않습니다. 왜 이런가요?


답변:


90

짧은 대답은 Python은 항상 값으로 전달하지만 모든 Python 변수는 실제로 일부 객체에 대한 포인터이므로 때때로 참조에 의한 전달처럼 보입니다.

파이썬에서 모든 객체는 변경 가능하거나 변경 불가능합니다. 예를 들어 목록, 딕셔너리, 모듈 및 Pandas 데이터 프레임은 변경 가능하며 정수, 문자열 및 튜플은 변경 불가능합니다. 변경 가능한 객체는 내부적으로 변경 될 수 있지만 (예 : 목록에 요소 추가) 변경 불가능한 객체는 변경할 수 없습니다.

처음에 말했듯이 모든 Python 변수를 객체에 대한 포인터로 생각할 수 있습니다. 함수에 변수를 전달할 때 함수 내의 변수 (포인터)는 항상 전달 된 변수 (포인터)의 복사본입니다. 따라서 내부 변수에 새로운 것을 할당하면 다른 개체를 가리키는 지역 변수. 이것은 변수가 가리키는 원래 객체를 변경 (변형)하지 않으며 외부 변수가 새 객체를 가리 키도록 만들지 않습니다. 이 시점에서 외부 변수는 여전히 원래 개체를 가리 키지 만 내부 변수는 새 개체를 가리 킵니다.

원래 객체를 변경하려면 (변경 가능한 데이터 유형에서만 가능) 로컬 변수에 완전히 새로운 값을 할당 하지 않고 객체를 변경하는 작업을 수행해야 합니다. 이 이유 letgo()letgo3()변경되지 않은 외부 항목을두고 있지만 letgo2()변경합니다 그것.

@ursan이 지적했듯이 letgo()대신 이와 같은 것을 사용 하면 가리키는 원래 객체를 변경 (변형) df하여 전역 a변수 를 통해 표시되는 값을 변경합니다 .

def letgo(df):
    df.drop('b', axis=1, inplace=True)

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)  # will alter a

어떤 경우에는 실제로 직접 할당하지 않고 원래 변수를 완전히 비우고 새 데이터로 다시 채울 수 있습니다. 예를 들어 v, 가리키는 원래 객체 가 변경되어 v나중에 사용할 때 표시되는 데이터가 변경 됩니다.

def letgo3(x):
    x[:] = np.array([[3,3],[3,3]])

v = np.empty((2, 2))
letgo3(v)   # will alter v

내가 직접 할당하는 것이 아니라는 점에 유의하십시오 x. 의 전체 내부 범위에 무언가를 할당하고 x있습니다.

완전히 새로운 개체를 만들고 외부에 표시해야하는 경우 (때로는 pandas의 경우) 두 가지 옵션이 있습니다. 'clean'옵션은 새 객체를 반환하는 것입니다. 예 :

def letgo(df):
    df = df.drop('b',axis=1)
    return df

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)

또 다른 옵션은 함수 외부에 도달하여 전역 변수를 직접 변경하는 것입니다. 이것은 a새로운 객체를 가리 키도록 변경 되고 a나중에 참조하는 모든 함수 는 새로운 객체를 보게됩니다.

def letgo():
    global a
    a = a.drop('b',axis=1)

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()   # will alter a!

전역 변수를 직접 변경하는 것은 일반적으로 나쁜 생각입니다. 코드를 읽는 사람은 누구나 어떻게 a변경 되었는지 파악하기가 어렵 기 때문 입니다. (저는 일반적으로 스크립트의 많은 함수에서 사용하는 공유 매개 변수에 전역 변수를 사용하지만 이러한 전역 변수를 변경하도록 허용하지 않습니다.)


7

문제는 PBV 대 PBR이 아닙니다. 이러한 이름은 Python과 같은 언어에서만 혼동을 유발합니다. C 또는 Fortran과 같이 작동하는 언어 (기본 PBV 및 PBR 언어)를 위해 개발되었습니다. 파이썬이 항상 가치를 전달한다는 것은 사실이지만 깨달음을주는 것은 아닙니다. 여기서 문제는 값 자체가 변경되었는지 또는 새로운 값을 얻었는지 여부입니다. 판다는 일반적으로 후자의 편에서 실수합니다.

http://nedbatchelder.com/text/names.html은 파이썬의 이름 체계가 무엇인지 아주 잘 설명합니다.


1
Python에서 전달 및 할당의 의미는 Java에서와 똑같으며 사용자가 말하는 것과 동일한 것을 Java에도 동일하게 적용 할 수 있습니다. 그러나 StackOverflow와 인터넷의 다른 곳에서 사람들은 Java가이 문제가 발생할 때마다 항상 가치를 전달한다는 사실을 사용자에게 "깨달음"이라고 생각합니다.
newacct

7

@Mike Graham의 답변에 추가하려면 다음과 같이 읽어보십시오.

귀하의 경우 기억해야 할 중요한 것은 이름 의 차이 입니다. a, df, xx, x, 모두 이름 ,하지만 그들은 동일하거나 서로 다른 참조 귀하의 예제의 다른 지점에서 :

  • 첫 번째 예에서는 인수를 설정하지 않는 한 새 값을 반환 하므로 다른 값으로 리 letgo 바인드 df 합니다 ( doc 참조 ). 즉 ,의 값을 참조 하던 이름 ( 함수 로컬 ) 이 이제 새 값, 여기서 반환 값을 참조하고 있음을 의미 합니다. 참조하는 값 은 여전히 ​​존재하며 변경되지 않았습니다.df.dropDataFrameinplace = Truedfletgoadf.dropa

  • 두 번째 예에서, letgo2 변이는 x , 그 리 바인딩없이 어떤 이유 xx에 의해 변경된다 letgo2. 이전 예와 달리 여기서 로컬 이름은 x항상 이름 xx이 참조 하는 값을 참조하고 해당 값 을 제자리에서 변경하므로 xx참조 하는 값 이 변경되었습니다.

  • 세 번째 예에서, letgo3 바인드 x 신규로 np.array. 이로 인해 이전에의 값을 참조했던 x로컬 이름 이 이제 다른 값인 new를 참조하게됩니다 . 참조하는 값 은 변경되지 않았습니다.letgo3xxnp.arrayxx


3

파이썬은 값으로 전달되거나 참조로 전달되지 않습니다. 그것은 할당에 의한 것입니다.

지원 참조, Python FAQ : https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

IOW :

  1. 변경 불가능한 값을 전달하는 경우 이름을 새 객체에 다시 바인딩하기 때문에 변경 사항이 호출자의 값을 변경하지 않습니다.
  2. 변경 가능한 값을 전달하는 경우 호출 된 함수의 변경 사항은 해당 이름을 새 객체에 리 바인드하지 않는 한 호출자의 값도 변경합니다. 변수를 다시 할당하여 새 개체를 만들면 해당 변경 사항과 이후의 이름 변경 사항이 호출자에게 표시되지 않습니다.

따라서 목록을 전달하고 0 번째 값을 변경하면 해당 변경 사항이 호출자와 호출자 모두에게 표시됩니다. 그러나 새 목록으로 목록을 다시 할당하면이 변경 사항이 손실됩니다. 당신이 목록을 슬라이스와 교체한다면 새로운 목록, 그 변화는 모두 호출 및 호출자에서 볼 수 있습니다.

예 :

def change_it(list_):
    # This change would be seen in the caller if we left it alone
    list_[0] = 28

    # This change is also seen in the caller, and replaces the above
    # change
    list_[:] = [1, 2]

    # This change is not seen in the caller.
    # If this were pass by reference, this change too would be seen in
    # caller.
    list_ = [3, 4]

thing = [10, 20]
change_it(thing)
# here, thing is [1, 2]

당신이 C 팬이라면, 값에 대한 포인터가 아닌 값에 대한 포인터가 아니라 값으로 포인터를 전달하는 것으로 생각할 수 있습니다.

HTH.


0

드롭에 대한 문서는 다음과 같습니다.

요청 된 축의 레이블이 제거 된 새 개체를 반환합니다.

따라서 새 데이터 프레임이 생성됩니다. 원본은 변경되지 않았습니다.

그러나 파이썬의 모든 객체는 데이터 프레임이 참조로 함수에 전달됩니다.


하지만 df함수 내부에 할당 했는데 참조 된 값이 새 개체로 변경되었음을 의미하지 않습니까?
nos

로컬 이름에 할당해도 다른 범위에서 이름이 바인딩 된 개체는 변경되지 않습니다.
Mike Graham

0

함수의 시작 부분에서 'a'를 전역으로 만들어야합니다. 그렇지 않으면 지역 변수이고 기본 코드에서 'a'를 변경하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.