Có một phương pháp được tiêu chuẩn hóa để trao đổi hai biến trong Python không?


345

Trong Python, tôi đã thấy hai giá trị biến đổi được hoán đổi bằng cú pháp này:

left, right = right, left

Đây có được coi là cách tiêu chuẩn để hoán đổi hai giá trị biến hay có một số phương tiện khác theo đó hai biến theo quy ước thường được hoán đổi?


1
@eyquem: chỉ đơn giản là liệu ngôn ngữ đánh giá được xác định bởi ngôn ngữ cho phân công tuple / danh sách. Python không, hầu hết các ngôn ngữ cũ không.
smci

Hrmm C ++ có hoán đổi (a [i], [k]) tại sao chúng ta không thể có cái gì đó như thế này cho Python.
Nils

Câu trả lời:


389

Python đánh giá các biểu thức từ trái sang phải. Lưu ý rằng trong khi đánh giá một bài tập, phía bên phải được đánh giá trước phía bên trái.

http://docs.python.org/3/reference/expressions.html#evalu-order

Điều đó có nghĩa như sau cho biểu thức a,b = b,a:

  • phía bên tay phải b,ađược ước tính, nghĩa là một bộ hai yếu tố được tạo ra trong bộ nhớ. Hai phần tử là các đối tượng được chỉ định bởi các định danh ba, đã tồn tại trước khi lệnh được mã hóa trong khi thực hiện chương trình
  • Ngay sau khi tạo ra bộ dữ liệu này, không có sự phân công nào của đối tượng bộ dữ liệu này vẫn được thực hiện, nhưng không thành vấn đề, Python thực sự biết nó ở đâu
  • sau đó, phía bên trái được ước tính, nghĩa là bộ dữ liệu được gán cho phía bên trái
  • vì phía bên trái bao gồm hai mã định danh, bộ dữ liệu được giải nén để định danh đầu tiên ađược gán cho phần tử đầu tiên của bộ dữ liệu (là đối tượng chính thức b trước khi hoán đổi vì nó có tên b)
    và Mã định danh thứ hai bđược gán cho thành phần thứ hai của bộ dữ liệu (là đối tượng trước đây trước khi hoán đổi vì định danh của nó là a)

Cơ chế này đã trao đổi hiệu quả các đối tượng được gán cho định danh ab

Vì vậy, để trả lời câu hỏi của bạn: CÓ, đó là cách tiêu chuẩn để trao đổi hai số nhận dạng trên hai đối tượng.
Nhân tiện, các đối tượng không phải là biến, chúng là đối tượng.


1
Theo tôi hiểu, hoán đổi hai biến theo cách này KHÔNG sử dụng thêm bộ nhớ, chỉ là bộ nhớ cho 2 biến, tôi có đúng không?
Catbuilts

1
@Catbuilts Xây dựng bộ dữ liệu sẽ chiếm thêm một số bộ nhớ (có thể nhiều hơn phiên bản 3 biến của hoán đổi sẽ chiếm), nhưng vì những thứ duy nhất được hoán đổi là địa chỉ bộ nhớ, nên nó sẽ không còn nhiều bộ nhớ nữa ý nghĩa (có thể thêm 24 byte).
Brilliand

@Brilliand: Thks. Bạn có bất kỳ tài liệu cho chủ đề này. Nó khá thú vị và tôi muốn có một bài đọc xa hơn. Cảm ơn.
Catbuilts

1
@Catbuilts Tôi không chắc, nhưng nó có thể giúp đọc được cách hoạt động của con trỏ C ++. Trong khi Python cố gắng tự động làm mọi thứ theo cách tốt nhất cho bạn, C ++ thực sự cung cấp cho bạn tất cả các tùy chọn để thực hiện mọi cách theo đúng và sai, vì vậy đó là điểm khởi đầu tốt để tìm hiểu những nhược điểm của cách tiếp cận mà Python thực hiện . Cũng cần nhớ rằng có hệ điều hành "64 bit" có nghĩa là việc lưu trữ địa chỉ bộ nhớ chiếm 64 bit bộ nhớ - đó là một phần trong đó tôi lấy số "24 byte" của mình.
Brilliand

Giải thích tuyệt vời. Chỉ cần thêm rằng đây là lý do tại sao bạn cũng có thể sử dụng phương pháp này để sắp xếp lại bất kỳ số lượng "biến" nào, vd a, b, c = c, a, b.
alexlomba87


38

Tôi biết ba cách để trao đổi biến, nhưng a, b = b, alà cách đơn giản nhất. Có

XOR (cho số nguyên)

x = x ^ y
y = y ^ x
x = x ^ y

Hoặc chính xác,

x ^= y
y ^= x
x ^= y

Biến tạm thời

w = x
x = y
y = w
del w

Trao đổi tuple

x, y = y, x

1
Là đơn giản nhất và duy nhất không bị xáo trộn.
Jorge Leitao

17
XOR không trao đổi "biến". Nó hoán đổi các biến số nguyên. (Hoặc một vài loại khác thực hiện đúng toán tử XOR) Hơn nữa, vì theo câu trả lời của Rogalski, Hoán đổi Tuple được tối ưu hóa trong trình thông dịch, thực sự không có gì chống lại nó. Ngắn gọn, rõ ràng và nhanh chóng.
Rawler

Vấn đề XOR có thể tránh được bằng cách sử dụng toán tử + - nhưng tôi vẫn cảm thấy tốt nhất là a, b = b, a codex = x + yy = xy x = xycode
ashish

22

Tôi sẽ không nói đó là một cách tiêu chuẩn để trao đổi vì nó sẽ gây ra một số lỗi không mong muốn.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]sẽ được sửa đổi đầu tiên và sau đó ảnh hưởng đến biến thứ hai nums[nums[i] - 1].


2
Bạn có vấn đề trong hầu hết mọi ngôn ngữ lập trình, đó là không an toàn khi sử dụng trao đổi (a, b), nếu a phụ thuộc vào b hoặc ngược lại. Ví dụ, hoán đổi (a, b) có thể được mở rộng để: var c=a, a=b, b=c. Và sau đó, bài tập cuối cùng sẽ sử dụng giá trị mới của ađể đánh giá địa chỉ của b.
Kai Petzke

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Sẽ giải quyết vấn đề.
Bill Cheng

1
Điều này không giải quyết vấn đề, nhưng tại sao?
Jackson Kelley

2
@JacksonKelley Đánh giá bên tay phải là an toàn. Trong nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Vấn đề là khi python thực hiện gán bên trái, nums[i]bị thay đổi, điều này gây ra nums[nums[i] - 1]thay đổi bất ngờ. Bạn có thể tưởng tượng lúc đầu bạn muốn nums[1],nums[2] = nums[2],nums[1], nhưng sau khi nums[1] = nums[2]chạy, bạn không còn nữa nums[2] = nums[1], thay vào đó bạn có nums[888] = nums[1].
guo

5

Không hoạt động cho các mảng nhiều chiều, bởi vì các tài liệu tham khảo được sử dụng ở đây.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Xem thêm Hoán đổi các lát của Numpy


Đây thực sự là một tính năng đặc biệt (hoặc lỗi) của thư viện numpy.
Kai Petzke

-1

Để giải quyết các vấn đề được giải thích bởi Eyquem , bạn có thể sử dụng copymô-đun để trả về một tuple chứa các bản sao (đảo ngược) của các giá trị, thông qua một hàm:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Chức năng tương tự như một lambda:

swapper = lambda x, y: (copy(y), copy(x))

Sau đó, gán chúng cho các tên mong muốn, như thế này:

x, y = swapper(y, x)

LƯU Ý: nếu bạn muốn, bạn có thể nhập / sử dụng deepcopythay vì copy.


vấn đề bạn đang cố gắng giải quyết bằng cách sao chép là gì?
Hanan Shteingart

Những người đã thảo luận trong bài viết của Eyquem .
LogicalBranch

1
nhưng thực tế không có vấn đề gì, thực tế anh ta nói "CÓ, đó là cách tiêu chuẩn để hoán đổi hai định danh"
Hanan Shteingart

-2

Bạn có thể kết hợp các giao dịch tupleXOR : x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

hoặc là

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Sử dụng lambda :

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Đầu ra:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.