Python for 루프에서 반복자와 시퀀스에 동일한 이름을 사용할 수있는 이유는 무엇입니까?


79

이것은 더 개념적인 질문입니다. 최근에 Python에서 코드 조각을 보았습니다 (2.7에서 작동했으며 2.5에서도 실행되었을 수도 있음). for루프가 반복되는 목록과 목록의 항목 모두에 동일한 이름을 사용했습니다. , 이는 저를 나쁜 습관이자 전혀 작동하지 않아야하는 것으로 생각합니다.

예를 들면 :

x = [1,2,3,4,5]
for x in x:
    print x
print x

수율 :

1
2
3
4
5
5

이제, 당신이 모두 당신의 부분에 대해 같은 변수 이름을 사용 할 수있을 것입니다 왜 실패하지만, 루프에서 X에 할당 된 마지막 값이 될 것입니다 인쇄 된 마지막 값이 이해하는 나에게 의미가 for루프가 의도 한대로 작동합니다. 다른 범위에 있습니까? 이와 같은 작업을 가능하게하는 내부에서 무슨 일이 일어나고 있습니까?


1
흥미로운 사고 실험으로 : 인자를 받아 인쇄하고 반환하는 함수 printAndReturn을 정의하십시오. 그러면에서 for i in printAndReturn [1,2,3,4,5] …몇 번 [1,2,3,4,5]인쇄 해야 합니까?
Joshua Taylor

1
아무도 직접 언급하지 않았기 때문에 범위에 대한 메모 : Python에는 함수 수준 범위 지정이 있지만 C의 블록 수준 범위 지정과는 다릅니다. 따라서 for루프 의 내부와 외부 는 동일한 범위를 갖습니다.
Izkata

나는 약간 오해의 소지가 있었기 때문에 질문의 제목을 수정했습니다. 어떤 것이 나쁜 습관이라고해서 그것이 작동하지 않는다는 의미는 아닙니다. 그것은 단지 등 유지 / 읽기가 더 오류가 발생하기 쉬운, 또는 어렵다고 할 수있다
니코

감사합니다. 나는 그것이 나쁜 제목이라는 데 전적으로 동의하며 처음에는 이름을 무엇으로 지정 해야할지 몰랐습니다.
Gustav

이것은 또한 PHP에서 작동 for ($x as $x)IMO 추한 코드를하지만입니다
chiliNUT

답변:


67

dis우리에게 무엇을 말 합니까 :

Python 3.4.1 (default, May 19 2014, 13:10:29)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dis import dis
>>> dis("""x = [1,2,3,4,5]
... for x in x:
...     print(x)
... print(x)""")

  1           0 LOAD_CONST               0 (1)
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 (3)
              9 LOAD_CONST               3 (4)
             12 LOAD_CONST               4 (5)
             15 BUILD_LIST               5
             18 STORE_NAME               0 (x)

  2          21 SETUP_LOOP              24 (to 48)
             24 LOAD_NAME                0 (x)
             27 GET_ITER
        >>   28 FOR_ITER                16 (to 47)
             31 STORE_NAME               0 (x)

  3          34 LOAD_NAME                1 (print)
             37 LOAD_NAME                0 (x)
             40 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             43 POP_TOP
             44 JUMP_ABSOLUTE           28
        >>   47 POP_BLOCK

  4     >>   48 LOAD_NAME                1 (print)
             51 LOAD_NAME                0 (x)
             54 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             57 POP_TOP
             58 LOAD_CONST               5 (None)
             61 RETURN_VALUE

핵심 비트는 섹션 2와 3입니다. x( 24 LOAD_NAME 0 (x)) 에서 값을로드 한 다음 반복자 ( 27 GET_ITER)를 가져와 반복을 시작합니다 ( 28 FOR_ITER). Python 은 반복자를 다시로드하기 위해 다시 돌아 가지 않습니다. .

제외 : 그것은 이미 반복자를 가지고 있기 때문에, 그렇게 할 말이없는 것이고, 같은 Abhijit는 그의 대답에 지적 , 파이썬의 사양의 섹션 7.3 ) 실제로이 동작을 필요로한다.

x이전에 xPython으로 알려진 목록 내의 각 값을 가리 키 도록 이름 을 덮어 쓰면 x반복 프로토콜을 완료하기 위해 이름을 다시 볼 필요가 없기 때문에 반복자를 찾는 데 문제가 없습니다 .


8
"Python은 이터레이터를 다시로드하기 위해 돌아 가지 않습니다 (이미 이터레이터가 있으므로 그렇게하는 것은 의미가 없습니다)." 이것은 당신이 분해에서 관찰했지만, 그 여부를 언급하지 않는 동작을 설명 경우 여부로; Abhijit의 답변 은 이것이 실제로 지정된 매뉴얼을 인용합니다.
Joshua Taylor

42

예제 코드를 핵심 참조로 사용

x = [1,2,3,4,5]
for x in x:
    print x
print x

섹션 7.3 을 참조하시기 바랍니다 . for 문매뉴얼

발췌 1

표현식 목록은 한 번 평가됩니다. 반복 가능한 객체를 생성해야합니다. expression_list의 결과에 대해 반복기가 생성됩니다.

그것은 무엇을 의미하는 것은 변수이다 x오브젝트의 기호 이름입니다 list: [1,2,3,4,5]반복 가능 객체로 평가된다. 변수, 기호 참조가 그 충성도를 변경하더라도 expression-list 가 다시 평가되지 않기 때문에 이미 평가 및 생성 된 반복 가능한 객체에는 영향을 미치지 않습니다.

노트

  • Python의 모든 것은 객체이며 식별자, 속성 및 메서드가 있습니다.
  • 변수는 지정된 인스턴스에서 단 하나의 객체에 대한 참조 인 기호 이름입니다.
  • 런타임의 변수는 충성도를 변경할 수 있습니다. 즉, 다른 객체를 참조 할 수 있습니다.

발췌 2

그런 다음 스위트는 인덱스 오름차순으로 반복자가 제공하는 각 항목에 대해 한 번씩 실행됩니다.

여기서 스위트는 표현식 목록이 아닌 반복자를 나타냅니다. 따라서 각 반복에 대해 원래 표현식 목록을 참조하는 대신 다음 항목을 생성하기 위해 반복기가 실행됩니다.


5

생각해 보면 이런 식으로 작동하는 것이 필요합니다. for루프 시퀀스에 대한 표현식은 다음과 같을 수 있습니다.

binaryfile = open("file", "rb")
for byte in binaryfile.read(5):
    ...

루프를 통과 할 때마다 시퀀스를 쿼리 할 수 ​​없거나 여기서는 다음 5 바이트 배치에서 두 번째로 읽습니다 . 당연히 파이썬은 루프가 시작되기 전에 어떤 식 으로든 표현식의 결과를 비공개로 저장해야합니다.


다른 범위에 있습니까?

아니오.이를 확인하기 위해 원래 범위 사전 ( locals () )에 대한 참조를 유지하고 실제로 루프 내에서 동일한 변수를 사용하고 있음을 알 수 있습니다.

x = [1,2,3,4,5]
loc = locals()
for x in x:
    print locals() is loc  # True
    print loc["x"]  # 1
    break

이와 같은 작업을 가능하게하는 내부에서 무슨 일이 일어나고 있습니까?

Sean Vieira 는 내부에서 무슨 일이 일어나고 있는지 정확히 보여 주었지만 더 읽기 쉬운 파이썬 코드로 설명하기 위해 for루프는 본질적으로 다음 while루프 와 동일합니다 .

it = iter(x)
while True:
    try:
        x = it.next()
    except StopIteration:
        break
    print x

이것은 이전 버전의 Java에서 볼 수있는 반복에 대한 기존의 인덱싱 접근 방식과 다릅니다. 예를 들면 다음과 같습니다.

for (int index = 0; index < x.length; index++) {
    x = x[index];
    ...
 }

이 접근 방식은 항목 변수와 시퀀스 변수가 동일한 경우 실패 합니다. 첫 번째 항목에 처음으로 다시 할당 된 x후 시퀀스 가 더 이상 다음 인덱스를 조회하는 데 사용할 수 없기 때문 x입니다.

그러나 전자의 접근 방식을 사용하면 첫 번째 줄 ( it = iter(x)) 은 그때부터 다음 항목을 제공 할 실제로 책임이 있는 반복기 객체 를 요청합니다 . x원래 가리키는 시퀀스는 더 이상 직접 액세스 할 필요가 없습니다.


4

변수 (x)와 그것이 가리키는 객체 (목록)의 차이입니다. for 루프가 시작되면 Python은 x가 가리키는 객체에 대한 내부 참조를 가져옵니다. x가 주어진 시간에 참조하는 것이 아니라 객체를 사용합니다.

x를 재 할당해도 for 루프는 변경되지 않습니다. x가 변경 가능한 객체 (예 : 목록)를 가리키고 해당 객체를 변경 (예 : 요소 삭제)하면 결과를 예측할 수 없습니다.


3

기본적으로 for 루프는 list를 가져 와서 x임시 변수로 저장 한 다음 해당 임시 변수의 각 값에 a 를 다시 할당 x합니다. 따라서 x이제는 목록의 마지막 값입니다.

>>> x = [1, 2, 3]
>>> [x for x in x]
[1, 2, 3]
>>> x
3
>>> 

다음과 같이 :

>>> def foo(bar):
...     return bar
... 
>>> x = [1, 2, 3]
>>> for x in foo(x):
...     print x
... 
1
2
3
>>> 

이 예에서, x저장된다 foo()같은 bar정도로되지만, x재 할당되고, 그것은 여전히 (ED)에 존재하는 foo()우리가 우리의 트리거를 사용할 수 for루프.


사실, 마지막 예에서는 x재 할당 되지 않는다고 생각 합니다. 에서 로컬 변수 bar가 생성되고 foo값이 할당됩니다 x. foo그런 다음 for조건에 사용되는 개체의 형식으로 해당 값을 반환합니다 . 따라서 x두 번째 예 에서는 변수 가 다시 할당되지 않았습니다. 나는 첫 번째 것에 동의합니다.
Tonio 2014-07-11

@Tonio x는 여전히 반복 변수이므로 각 루프에 대해 새 값을 사용합니다. 루프 후는 두 경우 모두 x동일합니다 3.
Peter Gibson

@PeterGibson 당신이 절대적으로 맞습니다.
Tonio 2014-07-11

이 루프 내부의 "새 변수가"인 경우에 루프가 후, 다음 방법 올 x유지 3하고 not [1,2,3]`?
Joshua Taylor

@JoshuaTaylor 파이썬에서 루프 인덱스 변수는 for 루프가 발생한 블록으로 어휘 적으로 범위가 지정됩니다.
HennyH

1

x더 이상 원래 x목록을 참조 하지 않으므로 혼동이 없습니다. 기본적으로 Python은 원래 x목록을 반복하고 있음을 기억 하지만 반복 값 (0,1,2 등)을 name에 할당하기 시작 x하면 더 이상 원래 x목록을 참조하지 않습니다 . 이름이 반복 값에 다시 할당됩니다.

In [1]: x = range(5)

In [2]: x
Out[2]: [0, 1, 2, 3, 4]

In [3]: id(x)
Out[3]: 4371091680

In [4]: for x in x:
   ...:     print id(x), x
   ...:     
140470424504688 0
140470424504664 1
140470424504640 2
140470424504616 3
140470424504592 4

In [5]: id(x)
Out[5]: 140470424504592

2
범위 목록의 복사본을 많이 만들지는 않습니다 (목록을 변경하면 반복에서 정의되지 않은 동작이 계속 생성되므로). x범위 목록 참조를 중지하고 대신 새 반복 값이 할당됩니다. 범위 목록은 그대로 유지됩니다. 당신의 값을 보면 x루프 후, 그것은 것입니다4
피터 깁슨

"X 더 이상 원래의 X를 의미 없다" x라고 결코 x; x시퀀스를 참조합니다. 그런 다음에 언급 1, 다음에 2
여호수아 테일러
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.