파이썬 스와핑 이해: a, b = b, a가 항상 b, a = a, b와 같지 않은 이유는 무엇입니까?
알고 , 두 은 우리모두알다피두목, 가교피는방톤법은인적환입니다.a그리고.b이라
a, b = b, a
그리고 그것은 동등해야 합니다.
b, a = a, b
그러나 오늘 일부 코드를 작업하던 중 다음 두 스왑이 서로 다른 결과를 제공한다는 것을 우연히 발견했습니다.
nums = [1, 2, 4, 3]
i = 2
nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
print(nums)
# [1, 2, 4, 3]
nums = [1, 2, 4, 3]
i = 2
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
print(nums)
# [1, 2, 3, 4]
이것은 저에게 소름끼치는 일입니다.여기서 무슨 일이 있었는지 누가 설명해 줄 수 있나요?저는 파이썬 스왑에서 두 가지 과제가 동시에 독립적으로 발생한다고 생각했습니다.
출처: python.org
대상 목록에 개체를 할당하고 선택적으로 괄호 또는 대괄호로 묶는 것은 다음과 같이 재귀적으로 정의됩니다.
...
- 기타: 개체는 대상 목록에 있는 대상과 동일한 항목 수를 가진 반복 가능한 개체여야 하며, 해당 항목은 왼쪽에서 오른쪽으로 해당 대상에 할당됩니다.
그래서 저는 당신의 임무가
nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
와 거의 동등합니다.
tmp = nums[nums[i]-1], nums[i]
nums[i] = tmp[0]
nums[nums[i] - 1] = tmp[1]
(물론, 더 나은 오류 확인과 함께)
반면에 다른 하나는
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
는 것과 같은
tmp = nums[i], nums[nums[i]-1]
nums[nums[i] - 1] = tmp[0]
nums[i] = tmp[1]
따라서 두 경우 모두 오른쪽이 먼저 평가됩니다.하지만 왼쪽의 두 부분을 순서대로 평가하고, 평가 후 바로 과제를 수행합니다.결정적으로, 이것은 왼쪽의 두 번째 임기는 첫 번째 과제가 이미 끝난 후에만 평가된다는 것을 의미합니다.그래서 업데이트를 하면,nums[i]에 에음그, 에다음처.nums[nums[i] - 1].nums[i]둘째.
이것은 평가가 특히 왼쪽에 있기 때문입니다.=왼쪽에서 오른쪽으로 발생:
nums[i], nums[nums[i]-1] =
첫째번▁firstnums[i]할당된 다음 해당 값은 할당된 인덱스를 결정하는 데 사용됩니다.nums[nums[i]-1]
이렇게 할당할 때:
nums[nums[i]-1], nums[i] =
의 nums[nums[i]-1]는 의이 값에따 달니다집라라전의 이전 합니다.nums[i] 할당된 이후로nums[i]아직도 나중에...
이 작업은 다음 규칙에 따라 수행됩니다.
- 우측이 먼저 평가됩니다.
- 그런 다음 왼쪽의 각 값은 왼쪽에서 오른쪽으로 새 값을 얻습니다.
래서로.nums = [1, 2, 4, 3] 번째 의 코드는 입니다.
nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2]
다음과 같습니다.
nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2]
nums[2], nums[nums[2]-1] = nums[3], nums[2]
nums[2], nums[nums[2]-1] = 3, 4
이제 오른쪽을 평가하면 할당은 다음과 같습니다.
nums[2] = 3
nums[nums[2]-1] = 4
nums[2] = 3
nums[3-1] = 4
nums[2] = 3
nums[2] = 4
이는 다음을 제공합니다.
print(nums)
# [1, 2, 4, 3]
두 번째 경우에는 다음과 같은 이점이 있습니다.
nums[nums[2]-1], nums[2] = nums[2], nums[nums[2]-1]
nums[nums[2]-1], nums[2] = nums[2], nums[3]
nums[nums[2]-1], nums[2] = 4, 3
nums[nums[2]-1] = 4
nums[2] = 3
nums[4-1] = 4
nums[2] = 3
nums[3] = 4
nums[2] = 3
print(nums)
# [1, 2, 3, 4]
당신의 표현의 왼쪽에서 당신은 nums[i]를 읽고 쓰고 있습니다. 저는 python gaurant가 압축 풀기 작업을 왼쪽에서 오른쪽 순서로 처리한다고 가정해 보겠습니다. 첫 번째 예는 다음과 같습니다.
t = nums[nums[i]-1], nums[i] # t = (3,4)
nums[i] = t[0] # nums = [1,2,3,3]
n = nums[i]-1 # n = 2
nums[n] = t[1] # nums = [1,2,4,3]
두 번째 예는 다음과 같습니다.
t = nums[i], nums[nums[i]-1] # t = (4,3)
n = nums[i]-1 # n = 3
nums[n] = t[0] # nums = [1,2,4,4]
nums[i] = t[0] # nums = [1,2,3,4]
당신이 얻은 것과 일치합니다.
평가 순서를 이해하기 위해 설정 시 출력되는 'Variable' 클래스를 만들어 'Value'로 설정했습니다.
class Variable:
def __init__(self, name, value):
self._name = name
self._value = value
@property
def value(self):
print(self._name, 'get', self._value)
return self._value
@value.setter
def value(self):
print(self._name, 'set', self._value)
self._value = value
a = Variable('a', 1)
b = Variable('b', 2)
a.value, b.value = b.value, a.value
실행 결과가 다음과 같은 경우:
b get 2
a get 1
a set 2
b set 1
이것은 오른쪽이 먼저 평가되고(왼쪽에서 오른쪽으로) 왼쪽이 평가된다는 것을 보여줍니다(다시 왼쪽에서 오른쪽으로).
OP의 예와 관련하여: 오른쪽은 두 경우 모두 동일한 값으로 평가됩니다.왼쪽 첫 번째 기간이 설정되고 이는 두 번째 기간의 평가에 영향을 미칩니다.그것은 결코 동시에 독립적으로 평가되지 않았습니다. 여러분이 이것이 사용되는 것을 보는 대부분의 경우, 용어들은 서로에 의존하지 않습니다.목록에서 값을 설정한 다음 해당 목록에서 값을 가져와 동일한 목록의 인덱스로 사용하는 것은 일반적으로 중요하지 않으며 이해하기 어렵다면 이해할 수 있습니다.for 루프에서 목록의 길이를 변경하는 것이 나쁜 것처럼, 이것은 같은 냄새를 가지고 있습니다. (하지만, 스크래치 패드로 달려가는 것에서 짐작하셨겠지만, 자극적인 질문입니다.)
CPython에서 코드 스니펫을 분석하는 한 가지 방법은 시뮬레이션된 스택 머신의 바이트 코드를 분해하는 것입니다.
>>> import dis
>>> dis.dis("nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]")
1 0 LOAD_NAME 0 (nums)
2 LOAD_NAME 0 (nums)
4 LOAD_NAME 1 (i)
6 BINARY_SUBSCR
8 LOAD_CONST 0 (1)
10 BINARY_SUBTRACT
12 BINARY_SUBSCR
14 LOAD_NAME 0 (nums)
16 LOAD_NAME 1 (i)
18 BINARY_SUBSCR
20 ROT_TWO
22 LOAD_NAME 0 (nums)
24 LOAD_NAME 1 (i)
26 STORE_SUBSCR
28 LOAD_NAME 0 (nums)
30 LOAD_NAME 0 (nums)
32 LOAD_NAME 1 (i)
34 BINARY_SUBSCR
36 LOAD_CONST 0 (1)
38 BINARY_SUBTRACT
40 STORE_SUBSCR
42 LOAD_CONST 1 (None)
44 RETURN_VALUE
이것을 읽기 쉽게 하기 위해 빈 줄을 추가했습니다.두 개의 가져오기 식은 바이트 0-13 및 14-19로 계산됩니다.BINARY_SUBSCR은 스택의 상위 두 값(개체 및 첨자)을 개체에서 가져온 값으로 바꿉니다.처음 계산된 값이 첫 번째 경계가 되도록 두 개의 페치된 값이 스왑됩니다.두 저장소 작업은 바이트 22-27 및 28-41로 수행됩니다. STORE_SUBSCR은 스택의 상위 3개 값, 저장할 값, 개체 및 첨자를 사용하여 제거합니다.(반복 없음 부분은 항상 끝에 추가됩니다.)질문의 중요한 부분은 스토어에 대한 계산이 개별적이고 독립적인 배치로 순차적으로 수행된다는 것입니다.
CPython 계산의 Python에서 가장 가까운 설명은 스택 변수의 도입을 필요로 합니다.
stack = []
stack.append(nums[nums[i]-1])
stack.append(nums[i])
stack.reverse()
nums[i] = stack.pop()
nums[nums[i]-1] = stack.pop()
다음은 역문에 대한 분해입니다.
>>> dis.dis("nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]")
1 0 LOAD_NAME 0 (nums)
2 LOAD_NAME 1 (i)
4 BINARY_SUBSCR
6 LOAD_NAME 0 (nums)
8 LOAD_NAME 0 (nums)
10 LOAD_NAME 1 (i)
12 BINARY_SUBSCR
14 LOAD_CONST 0 (1)
16 BINARY_SUBTRACT
18 BINARY_SUBSCR
20 ROT_TWO
22 LOAD_NAME 0 (nums)
24 LOAD_NAME 0 (nums)
26 LOAD_NAME 1 (i)
28 BINARY_SUBSCR
30 LOAD_CONST 0 (1)
32 BINARY_SUBTRACT
34 STORE_SUBSCR
36 LOAD_NAME 0 (nums)
38 LOAD_NAME 1 (i)
40 STORE_SUBSCR
42 LOAD_CONST 1 (None)
44 RETURN_VALUE
목록의 내용이 목록에 대한 목록 인덱스 범위에 있을 때만 이러한 현상이 발생하는 것 같습니다.예를 들어 다음과 같은 경우:
nums = [10, 20, 40, 30]
코드가 실패하고 다음 오류가 발생합니다.
>>> nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
그래서 확실히, 고트카.목록의 내용을 해당 목록의 색인으로 사용하지 않습니다.
티에리는 좋은 대답을 했습니다, 좀 더 분명히 말씀드리죠.다음과 같은 경우에 주의하십시오.nums = [1, 2, 4, 3],
다음 코드에서:
nums[nums[i]-1], nums[i]
- 나는 2살이고,
- nums[nums[i]-1]은 nums[4-1], 즉 nums[3], (값은 3)입니다.
- nums[i]는 nums[2], (값은 4)입니다.
- 결과: (3, 4)
다음 코드에서:
nums[i], nums[nums[i]-1]
- nums[i] is nums[2]는 3이 됩니다, (=>[1, 2, 3, 3])
- 그러나 nums[nums[i]-1은 nums[4-1]이 아니라 nums[3-1]이므로 nums[2] 역시 (다시) 4가 됩니다(=>[1, 2, 4, 3]).
스와프와 관련된 좋은 질문은 다음을 사용하는 것이었습니다.
nums[i], nums[i-1] = nums[i-1], nums[i]?
사용해 보십시오.
>>> print(nums)
>>> [1, 2, 4, 3]
>>> nums[i], nums[i-1] = nums[i-1], nums[i]
>>> print(nums)
>>> [1, 4, 2, 3]
ChD
언급URL : https://stackoverflow.com/questions/68152730/understand-python-swapping-why-is-a-b-b-a-not-always-equivalent-to-b-a-a
'programing' 카테고리의 다른 글
| 데이터와 함께 mongodb 데이터베이스 복사/클론 (0) | 2023.05.01 |
|---|---|
| 확인란과 해당 레이블을 일관되게 교차 브라우저에 정렬하는 방법 (0) | 2023.05.01 |
| PSQL의 DECTION 및 NUMERICAL 데이터 유형 간의 차이 (0) | 2023.05.01 |
| NSString에서 모든 공백 제거 (0) | 2023.04.26 |
| FileUpload 서버 컨트롤을 사용하지 않고 ASP.net 에서 파일 업로드 (0) | 2023.04.26 |