มีวิธีที่ได้มาตรฐานเพื่อแลกเปลี่ยนสองตัวแปรใน Python หรือไม่


345

ใน Python ฉันเห็นค่าตัวแปรสองค่าที่สลับกันโดยใช้ไวยากรณ์นี้:

left, right = right, left

สิ่งนี้ถือว่าเป็นวิธีมาตรฐานในการสลับสองค่าตัวแปรหรือมีวิธีอื่นที่ตัวแปรสองตัวที่ใช้โดยทั่วไปมักสลับกันหรือไม่


1
@ eyquem: เป็นเพียงการลงมาว่าการเรียงลำดับของการประเมินผลนั้นถูกกำหนดโดยภาษาสำหรับการมอบหมาย tuple / list หรือไม่ Python ทำภาษาที่เก่ากว่าส่วนใหญ่ทำไม่ได้
smci

Hrmm C ++ มี swap (a [i], a [k]) ทำไมเราไม่สามารถมีอะไรแบบนี้สำหรับ Python
นิลส์

คำตอบ:


389

Python ประเมินค่านิพจน์จากซ้ายไปขวา ขอให้สังเกตว่าในขณะที่การประเมินการมอบหมายด้านขวามือจะถูกประเมินก่อนทางด้านซ้าย

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

นั่นหมายถึงการแสดงออกดังต่อไปนี้a,b = b,a:

  • ทางด้านขวามือb,aจะถูกประเมินนั่นก็คือการสร้าง tuple ของสององค์ประกอบในหน่วยความจำ องค์ประกอบทั้งสองเป็นวัตถุที่กำหนดโดยตัวระบุbและaที่มีอยู่ก่อนที่คำสั่งจะถูกเข้ารหัสในระหว่างการดำเนินการของโปรแกรม
  • หลังจากการสร้างสิ่งอันดับนี้ยังไม่มีการมอบหมายวัตถุแบบ tuple นี้ แต่ก็ไม่เป็นไร Python รู้ว่ามันอยู่ที่ไหนภายใน
  • จากนั้นทางด้านซ้ายจะได้รับการประเมินนั่นคือบอกว่า tuple นั้นถูกกำหนดให้ทางด้านซ้ายมือ
  • เมื่อด้านซ้ายประกอบด้วยสองตัวระบุ tuple จะถูกแยกออกเพื่อให้ตัวระบุแรกaถูกกำหนดให้กับองค์ประกอบแรกของ tuple (ซึ่งเป็นวัตถุที่เป็นb อย่างเป็นทางการก่อนการแลกเปลี่ยนเนื่องจากมีชื่อb)
    และ ตัวระบุที่สองbถูกกำหนดให้กับองค์ประกอบที่สองของ tuple (ซึ่งเป็นวัตถุที่เคยเป็นมาก่อนหน้าการแลกเปลี่ยนเพราะตัวระบุเป็นa)

กลไกนี้ได้เปลี่ยนวัตถุที่กำหนดให้กับตัวระบุaและb

ดังนั้นเพื่อตอบคำถามของคุณ: ใช่เป็นวิธีมาตรฐานในการสลับสองตัวระบุบนวัตถุสองรายการ
โดยวิธีการที่วัตถุไม่ได้เป็นตัวแปรพวกเขาเป็นวัตถุ


1
เท่าที่ฉันเข้าใจการแลกเปลี่ยนสองตัวแปรด้วยวิธีนี้ไม่ได้ใช้หน่วยความจำเพิ่มเติมเพียงแค่หน่วยความจำสำหรับ 2 ตัวแปรเองใช่ไหม?
Catbuilts

1
@Catbuilts การสร้าง tuple จะใช้หน่วยความจำพิเศษเพิ่มขึ้น (น่าจะมากกว่า swap ที่เป็นตัวแปร 3 ตัวแปร) แต่เนื่องจากสิ่งเดียวที่ถูกสลับคือที่อยู่หน่วยความจำจึงไม่มีหน่วยความจำเพิ่มเติมในสัมบูรณ์ ความรู้สึก (อาจเพิ่ม 24 ไบต์)
Brilliand

@ Brilliand: Thks คุณมีเอกสารใด ๆ สำหรับเรื่องนี้หรือไม่ มันค่อนข้างน่าสนใจและฉันต้องการอ่านเพิ่มเติม ขอบคุณ
Catbuilts

1
@Catbuilts ฉันไม่แน่ใจ แต่มันอาจช่วยให้อ่านเกี่ยวกับวิธีการทำงานของพอยน์เตอร์ C ++ ในขณะที่ Python พยายามทำสิ่งที่ดีที่สุดโดยอัตโนมัติ C ++ ให้ตัวเลือกทั้งหมดแก่คุณในการทำสิ่งต่าง ๆ ในทางที่ถูกและผิดดังนั้นจึงเป็นจุดเริ่มต้นที่ดีสำหรับการเรียนรู้สิ่งที่เป็นข้อเสียของ Python . โปรดจำไว้ว่าการมีระบบปฏิบัติการ "64- บิต" หมายความว่าการจัดเก็บที่อยู่หน่วยความจำต้องใช้หน่วยความจำ 64 บิตซึ่งเป็นส่วนหนึ่งของที่ฉันได้รับหมายเลข "24 ไบต์"
Brilliand

คำอธิบายที่ดี เพียงแค่เพิ่มว่านี่คือเหตุผลที่คุณยังสามารถใช้วิธีนี้ในการจัดเรียงจำนวนของ "ตัวแปร" ใด ๆ a, b, c = c, a, bเช่น
alexlomba87


38

ฉันรู้จักสามวิธีในการสลับตัวแปร แต่a, b = b, aวิธีที่ง่ายที่สุดคือ นั่นคือ

XOR (สำหรับจำนวนเต็ม)

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

หรือรัดกุม

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

ตัวแปรชั่วคราว

w = x
x = y
y = w
del w

Tuple swap

x, y = y, x

1
เป็นสิ่งที่ง่ายและมีเพียงสิ่งเดียวที่ไม่ทำให้สับสน
Jorge Leitao

17
XOR ไม่สลับ "ตัวแปร" มันสลับตัวแปรจำนวนเต็ม (หรือประเภทอื่น ๆ ที่ใช้งานตัวดำเนินการ XOR อย่างเหมาะสม) นอกจากนี้เนื่องจากคำตอบของ Rogalski ทำให้ Tuple Swap ได้รับการปรับให้เหมาะสมที่สุดในล่าม สั้นชัดเจนและรวดเร็ว
Rawler

ปัญหา XOR สามารถหลีกเลี่ยงได้โดยใช้ตัวดำเนินการ + - แต่ฉันก็ยังรู้สึกว่าดีที่สุดคือ a, b = b, a codex = x + yy = xy x = xycode
ashish

22

ฉันจะไม่บอกว่ามันเป็นวิธีมาตรฐานในการแลกเปลี่ยนเพราะจะทำให้เกิดข้อผิดพลาดที่ไม่คาดคิด

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

nums[i]nums[nums[i] - 1]จะได้รับการแก้ไขก่อนแล้วจึงส่งผลกระทบต่อตัวแปรที่สอง


2
คุณมีปัญหาในเกือบภาษาการเขียนโปรแกรมใด ๆ ที่ไม่ปลอดภัยในการใช้ swap (a, b) หากขึ้นอยู่กับ b หรือในทางกลับกัน ยกตัวอย่างเช่นการแลกเปลี่ยน (A, B) อาจจะมีการขยายไปยัง: var c=a, ,a=b b=cจากนั้นการมอบหมายครั้งสุดท้ายจะใช้ค่าใหม่ของaเพื่อประเมินที่อยู่ของ b
Kai Petzke

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. จะแก้ปัญหา
Bill Cheng

2
@JacksonKelley การประเมินทางด้านขวามือนั้นมีความปลอดภัย ในnums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: ปัญหาคือเมื่องูหลามทำการมอบหมายด้านซ้ายnums[i]มีการเปลี่ยนแปลงซึ่งทำให้การnums[nums[i] - 1]เปลี่ยนแปลงที่ไม่คาดคิด U สามารถคิดในตอนแรกต้องการ u nums[1],nums[2] = nums[2],nums[1]แต่หลังจากnums[1] = nums[2]วิ่งคุณไม่ต้องnums[2] = nums[1]แทน u nums[888] = nums[1]มี
guo

5

ไม่ทำงานสำหรับอาร์เรย์หลายมิติเนื่องจากมีการอ้างอิงที่นี่

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)

ดูเพิ่มเติมสลับส่วนของ Numpy Array


นี่เป็นคุณสมบัติพิเศษ (หรือข้อผิดพลาด) ของไลบรารี numpy
Kai Petzke

-1

เพื่อแก้ไขปัญหาที่อธิบายโดยeyquemคุณสามารถใช้copyโมดูลเพื่อคืนค่า tuple ที่มี (ย้อนกลับ) สำเนาของค่าผ่านฟังก์ชั่น:

from copy import copy

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

ฟังก์ชั่นเดียวกับlambda:

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

จากนั้นกำหนดชื่อเหล่านั้นให้เป็นชื่อที่ต้องการเช่นนี้

x, y = swapper(y, x)

หมายเหตุ:หากคุณต้องการคุณสามารถนำเข้า / ใช้แทนdeepcopycopy


ปัญหาที่คุณพยายามแก้ไขด้วยการทำสำเนาคืออะไร?
Hanan Shteingart

คนที่กล่าวถึงในการโพสต์ของ eyquem
LogicalBranch

1
แต่ก็ไม่ได้ระบุปัญหาใด ๆ ในความเป็นจริงเขาบอกว่า "ใช่มันเป็นวิธีมาตรฐานในการแลกเปลี่ยนสองตัวระบุ"
Hanan Shteingart

-2

คุณสามารถรวมการแลกเปลี่ยนtupleและXOR : 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))

หรือ

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))

ใช้แลมบ์ดา :

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))

เอาท์พุท:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.