program tip

for 루프에서 임의의 대상 표현식이 허용되는 이유는 무엇입니까?

radiobox 2020. 12. 9. 08:01
반응형

for 루프에서 임의의 대상 표현식이 허용되는 이유는 무엇입니까?


실수로 다음과 같은 코드를 작성했습니다.

foo = [42]
k = {'c': 'd'}

for k['z'] in foo:  # Huh??
    print k

그러나 놀랍게도 이것은 구문 오류가 아닙니다. 대신 {'c': 'd', 'z': 42}.

생각 엔 코드는 문자 그대로 다음과 같이 번역된다는 것입니다.

i = iter(foo)
while True:
    try:
        k['z'] = i.next()  # literally translated to assignment; modifies k!
        print k
    except StopIteration:
        break

하지만 ... 왜 이것이 언어에서 허용됩니까? for-stmt의 대상 표현식 에는 단일 식별자와 식별자 튜플 만 허용되어야합니다 . 이것이 이상한 문제가 아니라 실제로 유용한 상황이 있습니까?


for루프는 작업을해야 바닐라 할당의 LHS에 작동하는 무슨 그래서 할당의 표준 규칙을 따릅니다 for:

각 항목은 할당에 대한 표준 규칙을 사용하여 대상 목록에 차례로 할당됩니다.

for구문은 샘플 코드의 경우 다음과 같은 대상에 할당하기위한 기본 메커니즘을 간단히 호출합니다 STORE_SUBSCR.

>>> foo = [42]
>>> k = {'c': 'd'}
>>> dis.dis('for k["e"] in foo: pass')
  1           0 SETUP_LOOP              16 (to 18)
              2 LOAD_NAME                0 (foo)
              4 GET_ITER
        >>    6 FOR_ITER                 8 (to 16)
              8 LOAD_NAME                1 (k)
             10 LOAD_CONST               0 ('e')
             12 STORE_SUBSCR <--------------------
             14 JUMP_ABSOLUTE            6
        >>   16 POP_BLOCK
        >>   18 LOAD_CONST               1 (None)
             20 RETURN_VALUE

하지만 놀랍게도 이것은 구문 오류가 아닙니다.

분명히 다음과 같은 정규 과제에서 작동하는 것은 무엇이든간에 :

전체 슬라이스 할당 :

>>> for [][:] in []:
...    pass
... 
>>>

목록 구독

>>> for [2][0] in [42]:
...    pass
... 
>>> 

딕셔너리 구독 등은 유효한 후보 대상이 될 수 있으며 유일한 예외는 체인 할당입니다 . 비록 나는 비밀리에 체인을 수행하기 위해 더러운 구문을 만들 수 있다고 생각합니다.


단일 식별자와 식별자 튜플 만 기대합니다.

사전 키에 대한 좋은 사용 사례를 대상으로 생각할 수 없습니다. 또한 for에서 대상으로 사용하는 것보다 루프 본문에서 사전 키 할당을 수행하는 것이 더 읽기 쉽습니다.

그러나 정규 할당에서 매우 유용한 확장 압축 해제 (Python 3)는 for 루프에서도 똑같이 편리합니다.

>>> lst = [[1, '', '', 3], [3, '', '', 6]]
>>> for x, *y, z in lst:
...    print(x,y,z)
... 
1 ['', ''] 3
3 ['', ''] 6

여기에서 다른 대상에 할당하는 해당 메커니즘도 소환됩니다. 여러 STORE_NAME초 :

>>> dis.dis('for x, *y, z in lst: pass')
  1           0 SETUP_LOOP              20 (to 22)
              2 LOAD_NAME                0 (lst)
              4 GET_ITER
        >>    6 FOR_ITER                12 (to 20)
              8 EXTENDED_ARG             1
             10 UNPACK_EX              257
             12 STORE_NAME               1 (x) <-----
             14 STORE_NAME               2 (y) <-----
             16 STORE_NAME               3 (z) <-----
             18 JUMP_ABSOLUTE            6
        >>   20 POP_BLOCK
        >>   22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

a for가 연속적으로 실행되는 간신히 간단한 대 입문 임을 보여줍니다 .


다음 코드가 이해가 되겠죠?

foo = [42]
for x in foo:
    print x

for루프는 목록을 반복 것입니다 foo및 이름으로 각 개체를 할당 x차례의 현재 네임 스페이스에. 결과는 단일 반복 및 단일 인쇄입니다 42.

In place of x in your code, you have k['z']. k['z'] is a valid storage name. Like x in my example, it doesn't yet exist. It is, in effect, k.z in the global namespace. The loop creates k.z or k['z'] and assigns the the values it finds in foo to it in the same way it would create x and assign the values to it in my example. If you had more values in foo...

foo = [42, 51, "bill", "ted"]
k = {'c': 'd'}
for k['z'] in foo:
    print k

would result in:

{'c': 'd', 'z': 42}
{'c': 'd', 'z': 51}
{'c': 'd', 'z': 'bill'}
{'c': 'd', 'z': 'ted'}

You wrote perfectly valid accidental code. It's not even strange code. You just usually don't think of dictionary entries as variables.

Even if the code isn't strange, how can allowing such an assignment be useful?

key_list = ['home', 'car', 'bike', 'locker']
loc_list = ['under couch', 'on counter', 'in garage', 'in locker'] 
chain = {}
for index, chain[key_list[index]] in enumerate(loc_list):
    pass

Probably not the best way to do that, but puts two equal length lists together into a dictionary. I'm sure there are other things more experienced programmers have used dictionary key assignment in for loops for. Maybe...


Every name is just a dictionary key*.

for x in blah:

is precisely

for vars()['x'] in blah:

* (though that dictionary needn't be implemented as an actual dict object, in case of some optimizations, such as in function scopes).


Is there any situation in which this is actually useful?

Indeed. Ever wanted to get rid of itertools.combinations?

def combinations (pool, repeat):        
    def combinations_recurse (acc, pool, index = 0):
        if index < len(acc):
            for acc[index] in pool:
                yield from combinations_recurse(acc, pool, index + 1)
        else:
            yield acc

    yield from combinations_recurse([pool[0]] * repeat, pool)

for comb in combinations([0, 1], 3):
    print(comb)

참고URL : https://stackoverflow.com/questions/44317993/why-are-arbitrary-target-expressions-allowed-in-for-loops

반응형