programing

함수가 발신자가 인식하는 일부 인수를 변경할 수 있지만 다른 인수는 변경할 수 없는 이유는 무엇입니까?

kingscode 2022. 10. 19. 21:24
반응형

함수가 발신자가 인식하는 일부 인수를 변경할 수 있지만 다른 인수는 변경할 수 없는 이유는 무엇입니까?

저는 가변 범위에 대한 파이썬의 접근 방식을 이해하려고 합니다.예에서는, 왜 「」, 「」가 되는가.f()을 수 x된 바와 「」 「」 「」)main() ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ,n

def f(n, x):
    n = 2
    x.append(4)
    print('In f():', n, x)

def main():
    n = 1
    x = [0,1,2,3]
    print('Before:', n, x)
    f(n, x)
    print('After: ', n, x)

main()

출력:

Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After:  1 [0, 1, 2, 3, 4]

일부 응답에는 함수 호출의 컨텍스트에 "복사"라는 단어가 포함되어 있습니다.헷갈리네요.

Python은 함수 호출 중에 전달한 객체를 복사하지 않습니다.

함수 파라미터는 이름입니다.함수를 호출하면 Python은 이러한 파라미터를 (발신자 범위의 이름을 통해) 전달되는 모든 객체에 바인드합니다.

오브젝트는 (목록과 같은) 가변이거나 (Python의 정수, 문자열과 같은) 불변일 수 있습니다.변경할 수 있는 가변 객체입니다.이름을 변경할 수 없습니다.다른 오브젝트에 바인드만 하면 됩니다.

이 예는 스코프나 네임스페이스에 관한 것이 아니라 Python에서 객체의 이름 짓기와 바인딩, 그리고 가변성에 관한 것입니다.

def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main()
    n = 2    # put `n` label on `2` balloon
    x.append(4) # call `append` method of whatever object `x` is referring to.
    print('In f():', n, x)
    x = []   # put `x` label on `[]` ballon
    # x = [] has no effect on the original list that is passed into the function

여기 다른 언어의 변수와 Python의 이름 사이의 차이점에 대한 멋진 사진이 있습니다.

당신은 이미 많은 답을 얻었고, 나는 J.F.의 의견에 대체로 동의한다.Sebastian, 단, 숏컷으로서 도움이 될 수 있습니다.

를 참조할 때마다 함수의 범위 내에 새 이름 바인딩을 만듭니다.임의의 값varname범위 내에서 소실됩니다.

언제든지 당신이 메서드를 호출하는 것을 볼 수 있습니다.varname을 변경할 수 "varname" "varname 。list.appendvarname 다음과 같은 (「」, 「」, 「」, 「」)가 됩니다).varnamenames할 수 에 모든 names)에 됩니다.

:global하면 첫 케이스에 됩니다.워드를 지정하면 첫 번째 케이스에 대한 예외가 생성됩니다.

f을 바꾸는 것은 x(목록 인스턴스에 대한 참조는 항상 동일합니다).오히려 이 목록의 내용이 변경됩니다.

두 경우 모두 참조 복사본이 함수에 전달됩니다.함수 안에서

  • n새로운 값이 할당됩니다.함수 내부의 참조만 수정되고 함수 외부의 참조는 수정되지 않습니다.
  • x에는 새로운 값이 할당되지 않습니다.함수 내부와 외부의 참조는 변경되지 않습니다.신,는x이 변경되었습니다.

x함수 내부와 외부에서 동일한 값을 참조하며, 둘 다 수정 내용을 볼 수 있습니다. 면,는n함수의 내부와 외부의 다른 값을 다음에 나타냅니다.n는 함수 내에서 재할당되었습니다.

혼란을 줄이기 위해 변수 이름을 변경합니다.n -> nf 또는 nmain.x -> xf 또는 xmain:

def f(nf, xf):
    nf = 2
    xf.append(4)
    print 'In f():', nf, xf

def main():
    nmain = 1
    xmain = [0,1,2,3]
    print 'Before:', nmain, xmain
    f(nmain, xmain)
    print 'After: ', nmain, xmain

main()

함수 f를 호출하면 Python 런타임은 xmain의 복사본을 만들어 xf에 할당하고 nmain의 복사본을 nf에 할당합니다.

n의 경우 복사되는 값은 1입니다.

x의 경우 복사되는 값은 리터럴리스트가 아닙니다[0, 1, 2, 3 ]그것은 그 리스트에 대한 언급이다.xfxmain은 동일한 목록을 가리키므로 xf를 수정할 때도 xmain을 수정해야 합니다.

그러나 다음과 같은 내용을 작성해야 할 경우:

    xf = ["foo", "bar"]
    xf.append(4)

xmain이 변경되지 않았음을 알 수 있습니다.이는 xf = ["foo", "bar"] 라인에서 새 목록을 가리키도록 xf를 변경해야 하기 때문입니다.이 새 목록을 변경해도 xmain이 가리키는 목록에는 영향을 주지 않습니다.

도움이 되길 바랍니다. :-)

함수가 완전히 다른 변수로 재작성되어 그 변수로 콜 ID를 호출하면 요점이 잘 나타납니다.처음에는 이것을 이해하지 못하고, 좋은 설명과 함께 jfs의 투고를 읽었기 때문에, 나는 나를 이해하려고 노력했다.

def f(y, z):
    y = 2
    z.append(4)
    print ('In f():             ', id(y), id(z))

def main():
    n = 1
    x = [0,1,2,3]
    print ('Before in main:', n, x,id(n),id(x))
    f(n, x)
    print ('After in main:', n, x,id(n),id(x))

main()
Before in main: 1 [0, 1, 2, 3]   94635800628352 139808499830024
In f():                          94635800628384 139808499830024
After in main: 1 [0, 1, 2, 3, 4] 94635800628352 139808499830024

z와 x의 ID는 동일합니다.기사에서 말한 것과 같은 기본 구조에 대한 태그만 다를 뿐입니다.

리스트는 변하기 쉬운 물건이기 때문입니다.x를 [0,1,2,3]의 값으로 설정하는 것이 아니라 [0,1,2,3] 객체에 대한 레이블을 정의하는 것입니다.

함수 f()는 다음과 같이 선언해야 합니다.

def f(n, x=None):
    if x is None:
        x = []
    ...

일반적인 이해로는 객체 변수(리스트나 딕트 등)는 함수를 통해 수정할 수 있습니다.이 경우 파라미터를 재할당하지 못할 것으로 생각됩니다.즉, 콜 가능한 함수 내에서 참조에 의해 할당됩니다.

그것은 많은 다른 언어들과 일치한다.

다음 간단한 스크립트를 실행하여 작동 방식을 확인합니다.

def func1(x, l1):
    x = 5
    l1.append("nonsense")

y = 10
list1 = ["meaning"]
func1(y, list1)
print(y)
print(list1)

n은 int(불변)이며 복사가 함수에 전달되므로 함수에서 복사를 변경합니다.

X는 목록(변환 가능)이며 포인터의 복사본이 함수에 전달되므로 x.append(4)는 목록의 내용을 변경합니다.단, 함수에서 x=[0,1,2,3,4]라고 하셨는데, x의 내용은 기본적으로 변경되지 않습니다.

Python은 참조 값으로 복사됩니다.오브젝트는 메모리의 필드를 점유하고 참조는 그 오브젝트에 관련지어지지만 그 자체는 메모리의 필드를 점유합니다.이름/값은 참조와 관련지어집니다.python 함수에서는 항상 참조의 값을 복사하기 때문에 코드에서는 n이 새로운 이름으로 복사되고, 그것을 할당하면 발신자 스택에 새로운 공간이 생깁니다.다만, 리스트의 경우는, 이름도 카피되고 있습니다만, 같은 메모리를 참조하고 있습니다(목록에 새로운 값을 할당하지 않기 때문입니다).저건 비단뱀의 마법이야!

Python은 올바른 방법으로 생각하면 순수하게 pass-by-value 언어입니다.python 변수는 객체의 위치를 메모리에 저장합니다.Python 변수는 개체 자체를 저장하지 않습니다.변수를 함수에 전달하면 변수가 가리키는 개체의 주소 복사본이 전달됩니다.

이 두 가지 기능을 대조합니다.

def foo(x):
    x[0] = 5

def goo(x):
    x = []

이제 셸에 입력해서

>>> cow = [3,4,5]
>>> foo(cow)
>>> cow
[5,4,5]

이걸 구에 비유해봐.

>>> cow = [3,4,5]
>>> goo(cow)
>>> goo
[3,4,5]

첫 번째 경우, 우리는 소의 주소를 foo에게 전달하고 그곳에 있는 물체의 상태를 수정합니다.오브젝트가 변경됩니다.

두 번째 경우에는 소의 주소 사본을 goo에게 전달합니다.그런 다음 goo는 복사본을 변경합니다.효과: 없음.

는 이것을 핑크하우스 원칙이라고 부릅니다.주소를 복사해서 그 주소의 집을 분홍색으로 칠하라고 하면, 결국 분홍색 집이 된다.만약 당신이 당신의 주소를 복사해서 화가에게 새 주소로 바꾸라고 말한다면, 당신의 집 주소는 바뀌지 않습니다.

그 설명은 많은 혼란을 없앤다.Python은 값별로 저장된 주소 변수를 전달합니다.

주엘이 말했듯이=의 기능과 .timeout 메서드의 기능 간의 차이도 고려해야 합니다.

  1. 기본적으로 n과 x를 정의할 때는 2개의 객체, 즉 1과 [1, 2, 3]을 가리키도록 지시합니다.이것이 =가 하는 일입니다. 변수가 무엇을 가리켜야 하는지 알려줍니다.

  2. 함수 f(n,x)를 호출할 때 두 개의 새로운 로컬 변수 nf와 xf가 n과 x와 같은 두 개체를 가리키도록 지시합니다.

  3. "something"="anything_new"를 사용하면 "something"이 가리키는 내용이 변경됩니다..append를 사용하면 개체 자체를 변경할 수 있습니다.

  4. 같은 이름을 지정한 경우에도 main()의 n과 f()의 n은 같은 엔티티가 아닙니다.원래는 같은 오브젝트만을 가리키고 있습니다(x도 마찬가지입니다).그들 중 한 명이 지적하는 것에 대한 변화는 다른 한 사람에게 영향을 미치지 않을 것이다.그러나 개체 자체를 변경하면 두 변수 모두 현재 수정된 동일한 개체를 가리키기 때문에 두 변수에 모두 영향을 미칩니다.

새로운 함수를 정의하지 않고 메서드 .tm과 =의 차이를 설명하겠습니다.

비교하다

    m = [1,2,3]
    n = m   # this tells n to point at the same object as m does at the moment
    m = [1,2,3,4] # writing m = m + [4] would also do the same
    print('n = ', n,'m = ',m)

로.

    m = [1,2,3]
    n = m
    m.append(4)
    print('n = ', n,'m = ',m)

첫 번째 코드에서는 n = [1, 2, 3] m = [1, 2, 3, 4]로 인쇄됩니다. 세 번째 줄에서는 오브젝트 [1, 2, 3, 4]를 변경하지 않고 m에게 ('='를 사용하여) 새로운 다른 오브젝트를 가리키라고 지시한 반면 n은 원래 오브젝트를 가리키고 있습니다.

두 번째 코드에서는 n = [1, 2, 3, 4] m = [1, 2, 3, 4]로 출력됩니다.이는 여기서 m과 n이 모두 코드 전체에서 동일한 개체를 가리키고 있지만 .append 메서드를 사용하여 개체 자체를 수정했기 때문입니다.두 번째 코드의 결과는 세 번째 줄에 m.append(4) 또는 n.append(4)를 적더라도 동일합니다.

그것을 이해하게 되면, 남은 혼란은 f() 함수 내부의 n과 x와 main()의 n이 같지 않다는 것을 이해하는 것 뿐입니다.이것들은 f()를 호출했을 때 처음 같은 오브젝트를 가리킵니다.

함수 내부에서 명령 n = 2를 전달하면 메모리 공간이 발견되고 2로 레이블이 지정됩니다.그러나 method append를 호출하면 기본적으로 위치 x(값이 무엇이든)로 다시 참조하고 해당 작업에 대해 몇 가지 작업을 수행합니다.

다시 편집하게 해주세요.이러한 컨셉은 python을 try error와 인터넷(주로 stackoverflow)을 통해 배운 경험입니다.실수도 있고 도움도 있어요.

Python 변수는 이름, 메모리 주소 및 값의 관련 링크로 참조를 사용합니다.

B = A실제로 A라는 닉네임을 만들고 A와 B라는 두 개의 이름을 갖게 되었습니다.B 출 A b A b A b A b b b b b b 。하다같은 값을 새로 작성하는 대신 이것을 참조라고 부릅니다.그리고 이 생각은 두 개의 골칫거리로 이어질 것이다.

할 때

A = [1]
B = A   # Now B is an alias of A

A.append(2)  # Now the value of A had been changes
print(B)
>>> [1, 2]  
# B is still an alias of A
# Which means when we call B, the real name we are calling is A

# When we do something to B,  the real name of our object is A
B.append(3)
print(A)
>>> [1, 2, 3]

이것은 인수를 함수에 넘길 때 발생하는 현상입니다.

def test(B):
    print('My name is B')
    print(f'My value is {B}') 
    print(' I am just a nickname,  My real name is A')
    B.append(2)


A = [1]
test(A) 
print(A)
>>> [1, 2]

A를 함수의 인수로 전달하는데, 그 함수의 이 인수의 이름은 B입니다.이름이 다른 같은 거.
할 때B.append , , , , 을 하고 요.A.append인수를 함수에 전달할 때는 변수를 전달하는 것이 아니라 에일리어스를 전달합니다.

여기 두 가지 문제가 있습니다.

  1. 등호는 항상 새로운 이름을 만듭니다.
A = [1]
B = A
B.append(2)
A = A[0]  # Now the A is a brand new name, and has nothing todo with the old A from now on.

B.append(3)
print(A)
>>> 1
# the relation of A and B is removed when we assign the name A to something else
# Now B is a independent variable of hisown.

등호(Equal sign)는 완전히 새로운 이름을 명시하는 것입니다.

이것은 나의 복잡한 부분이었다

 A = [1, 2, 3]

# No equal sign, we are working on the origial object,
A.append(4)
>>> [1, 2, 3, 4]

# This would create a new A
A = A + [4]  
>>> [1, 2, 3, 4]

및 기능

def test(B):
    B = [1, 2, 3]   # B is a new name now, not an alias of A anymore
    B.append(4)  # so this operation won't effect A
    
A = [1, 2, 3]
test(A)
print(A)
>>> [1, 2, 3]

# ---------------------------

def test(B):
    B.append(4)  # B is a nickname of A, we are doing A
    
A = [1, 2, 3]
test(A)
print(A)
>>> [1, 2, 3, 4]

첫 번째 문제는

  1. 왼쪽과 방정식은 항상 완전히 새로운 이름, 새로운 변수입니다.

  2. B = A

두 번째 문제는 절대 변하지 않는 것이 있다는 것입니다.원본을 수정할 수 없고, 새로운 것을 만들 수 있을 뿐입니다.

이것이 우리가 불변이라고 부르는 것이다.

A= 123받아쓰다

B = A주소 및 값을 A에서 B로 복사하고, B에 대한 모든 조작은 A의 값의 동일한 주소에 영향을 줍니다.

문자열, 숫자, 튜플에 관한 한.가치와 주소의 쌍은 절대 바뀔 수 없습니다.스트링을 어떤 주소에 붙이면 바로 잠겨버렸고, 모든 변경의 결과는 다른 주소로 보내집니다.

A = 'string''string나 메서드 에서는 "string"과 같은 을 가진 .현재로서는 다음과 같은 구문을 사용하여 문자열을 수정하는 함수나 메서드는 포함되어 있지 않습니다.list.append이 코드는 주소의 원래 값을 변경하기 때문입니다.

문자열, 숫자 또는 태플의 값과 주소는 보호, 잠금, 불변입니다.

할 수 입니다.A = B.method새 문자열 값을 저장할 새 이름을 만들어야 합니다.

아직도 헷갈리면 이 논의를 연장해 주세요.이 토론은 내가 변하기 쉬운/불변한/반복하기/논쟁/변수/이름을 알아내는 데 도움이 되고, 이것이 누군가에게도 도움이 될 수 있기를 바란다.

##############################

내 대답을 수없이 수정했고 내가 아무 말도 할 필요가 없다는 걸 깨달았어. 비단뱀은 이미 스스로 설명했어.

a = 'string'
a.replace('t', '_')
print(a)
>>> 'string'

a = a.replace('t', '_')
print(a)
>>> 's_ring'

b = 100
b + 1
print(b)
>>> 100

b = b + 1
print(b)
>>> 101

def test_id(arg):
    c = id(arg)
    arg = 123
    d = id(arg)
    return

a = 'test ids'
b = id(a)
test_id(a)
e = id(a)

# b = c  = e != d
# this function do change original value
del change_like_mutable(arg):
    arg.append(1)
    arg.insert(0, 9)
    arg.remove(2)
    return
 
test_1 = [1, 2, 3]
change_like_mutable(test_1)



# this function doesn't 
def wont_change_like_str(arg):
     arg = [1, 2, 3]
     return


test_2 = [1, 1, 1]
wont_change_like_str(test_2)
print("Doesn't change like a imutable", test_2)

이 데빌은 참조 / value / or not / instance, 이름 공간 또는 변수 / 목록 또는 str, IT IS THE SYNACTS, EQUAL SIGNALTH, EQUAL SIGN이 아닙니다.

언급URL : https://stackoverflow.com/questions/575196/why-can-a-function-modify-some-arguments-as-perceived-by-the-caller-but-not-oth

반응형