Python กำหนดตัวแปรหลายตัวให้เป็นค่าเดียวกัน? รายการพฤติกรรม


132

ฉันพยายามใช้การกำหนดหลายอย่างตามที่แสดงด้านล่างเพื่อเริ่มต้นตัวแปร แต่ฉันสับสนกับพฤติกรรมฉันคาดว่าจะกำหนดรายการค่าใหม่แยกกันฉันหมายถึง b [0] และ c [0] เท่ากับ 0 เหมือนเดิม

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

ผลลัพธ์คือ: [1, 3, 5] [1, 3, 5] [1, 3, 5]

ถูกต้องหรือไม่ ฉันควรใช้อะไรในการมอบหมายงานหลาย ๆ อะไรที่แตกต่างจากนี้?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

ผลลัพธ์: ('f:', 3) ('e:', 4)


2
คุณต้องการa, bและc,ไปยังจุดทั้งหมดเป็นค่าเดียวกัน (ในกรณีนี้รายการ) หรือไม่หรือคุณต้องการa=0, และb=3 c=5ในกรณีที่คุณต้องการหรือเพียงแค่a,b,c = [0,3,5] a,b,c = 0,3,5
chepner

คำตอบ:


271

หากคุณมาที่ Python จากภาษาใน C / Java / etc ครอบครัวมันอาจช่วยให้คุณเลิกคิดaว่าเป็น "ตัวแปร" และเริ่มคิดว่ามันเป็น "ชื่อ"

a, bและcไม่ได้เป็นตัวแปรที่แตกต่างกันมีค่าเท่ากัน เป็นชื่อที่แตกต่างกันสำหรับค่าที่เหมือนกัน ตัวแปรมีประเภทอัตลักษณ์ที่อยู่และทุกประเภทเช่นนั้น

ชื่อไม่มีเลย ค่าทำของหลักสูตรและคุณสามารถมีจำนวนมากของชื่อสำหรับค่าเดียวกัน

หากคุณให้Notorious B.I.G.สุนัขร้อน * Biggie SmallsและChris Wallaceมีสุนัขร้อน หากคุณเปลี่ยนองค์ประกอบแรกของaเป็น 1 องค์ประกอบแรกของbและcคือ 1

หากคุณต้องการทราบว่าสองชื่อตั้งชื่อวัตถุเดียวกันหรือไม่ให้ใช้ตัวisดำเนินการ:

>>> a=b=c=[0,3,5]
>>> a is b
True

จากนั้นคุณถาม:

อะไรที่แตกต่างจากนี้?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

ที่นี่คุณกำลังเชื่อมโยงชื่อeกับค่า4อีกครั้ง นั่นไม่ส่งผลกระทบต่อชื่อdและfแต่อย่างใด

ในรุ่นก่อนหน้าของคุณคุณได้รับมอบหมายให้ไม่a[0] aดังนั้นจากมุมมองของa[0]คุณคุณกำลังตอบa[0]กลับ แต่จากมุมมองของaคุณกำลังเปลี่ยนมันในสถานที่

คุณสามารถใช้idฟังก์ชั่นซึ่งจะให้หมายเลขเฉพาะที่แสดงถึงเอกลักษณ์ของวัตถุเพื่อดูว่าวัตถุใดเป็นสิ่งที่แม้ว่าisจะช่วยไม่ได้ก็ตาม:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

สังเกตว่าa[0]เปลี่ยนจาก 4297261120 เป็น 4297261216 ตอนนี้เป็นชื่อสำหรับค่าอื่น และb[0]ตอนนี้ยังเป็นชื่อสำหรับค่าใหม่เดียวกัน นั่นเป็นเพราะaและbยังคงตั้งชื่อวัตถุเดียวกัน


ภายใต้การครอบคลุมa[0]=1กำลังเรียกใช้เมธอดบนวัตถุรายการ (มันเทียบเท่ากับa.__setitem__(0, 1).) ดังนั้นก็ไม่ได้จริงๆ rebinding อะไรเลย มันชอบโทรmy_object.set_something(1). แน่นอนว่าเป็นไปได้ว่าวัตถุกำลังเชื่อมโยงแอตทริบิวต์อินสแตนซ์เพื่อใช้วิธีนี้ แต่นั่นไม่ใช่สิ่งสำคัญ สิ่งที่สำคัญคือคุณไม่ได้กำหนดอะไรเลยคุณแค่กลายพันธุ์วัตถุ a[0]=1และมันก็เป็นเช่นเดียวกันกับ


user570826 ถามว่า:

จะเป็นอย่างไรถ้าเรามี a = b = c = 10

นั่นเป็นสถานการณ์เดียวกันกับa = b = c = [1, 2, 3]: คุณมีสามชื่อสำหรับค่าเดียวกัน

แต่ในกรณีนี้ค่าคือ an intและints ไม่เปลี่ยนรูป ในทั้งสองกรณีคุณสามารถ rebind aเป็นค่าที่แตกต่างกัน (เช่นa = "Now I'm a string!") แต่จะไม่ส่งผลกระทบต่อค่าเดิมซึ่งbและcจะยังคงเป็นชื่อ ความแตกต่างคือมีรายการคุณสามารถเปลี่ยนค่า[1, 2, 3]ลงไป[1, 2, 3, 4]ด้วยการทำเช่นa.append(4); ตั้งแต่ที่จริงการเปลี่ยนแปลงค่าที่bและcเป็นชื่อสำหรับตอนนี้จะขb [1, 2, 3, 4]ไม่มีทางเปลี่ยนมูลค่า10เป็นอย่างอื่นได้ 10คือ 10 ตลอดไปเช่นเดียวกับ Claudia ที่แวมไพร์คือ 5 ตลอดไป (อย่างน้อยก็จนกว่าเธอจะถูกแทนที่ด้วย Kirsten Dunst)


* คำเตือน: อย่าให้ Notorious BIG เป็นฮอทดอก ไม่ควรให้อาหารซอมบี้ Gangsta แร็พหลังเที่ยงคืน


จะเกิดอะไรขึ้นถ้าเราhave, a = b = c = 10;และเมื่อเราพยายามอัปเดตค่าของ b มันจะส่งผลกระทบอื่นใด? แม้ว่าฉันจะตรวจสอบรหัสของพวกเขาว่าเหมือนกัน
AJ

@ user570826: 10ไม่เปลี่ยนรูปนั่นหมายความว่าไม่มีวิธีอัปเดตค่าดังนั้นคำถามของคุณจึงไม่สมเหตุสมผล คุณสามารถชี้ไปbที่ค่าอื่นได้ แต่การทำเช่นนั้นจะไม่มีผลต่อaและcซึ่งยังคงชี้ไปที่ค่าเดิม ความแตกต่างที่ทำให้รายการเกิดขึ้นคือไม่สามารถเปลี่ยนแปลงได้เช่นคุณสามารถappendไปยังรายการหรือlst[0] = 3และจะอัปเดตค่าซึ่งจะมองเห็นได้จากชื่อทั้งหมดสำหรับค่านั้น
abarnert

72

อาการไอ

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

10
IMHO สิ่งนี้ตอบคำถามสำคัญข้อแรกของ OP ว่าฉันควรใช้อะไรสำหรับการมอบหมายงานหลาย ๆ งานในขณะที่คำตอบที่ได้รับการจัดอันดับสูงกว่าและสมองมากกว่าข้างต้นไม่ได้
Will Croxford

2
หรือa,b,c = 1,2,3ไม่มีวงเล็บทำงานใน Python 2 หรือ 3 ถ้าคุณต้องการความสามารถในการอ่านเพิ่มเติม
Will Croxford

14

ใช่นั่นคือพฤติกรรมที่คาดหวัง a, b และ c ถูกกำหนดเป็นป้ายกำกับสำหรับรายการเดียวกัน หากคุณต้องการสามรายการที่แตกต่างกันคุณต้องกำหนดรายการทีละรายการ คุณสามารถทำซ้ำรายการที่โจ่งแจ้งหรือใช้หนึ่งในหลายวิธีในการคัดลอกรายการ:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

คำสั่ง Assignment ใน Python จะไม่คัดลอกวัตถุ - พวกมันผูกชื่อกับวัตถุและวัตถุสามารถมีป้ายกำกับได้มากเท่าที่คุณกำหนด ในการแก้ไขครั้งแรกของคุณการเปลี่ยน [0] คุณกำลังอัปเดตองค์ประกอบหนึ่งของรายการเดียวที่ a, b และ c อ้างถึงทั้งหมด ในวินาทีที่คุณเปลี่ยน e คุณกำลังเปลี่ยน e เป็นป้ายกำกับสำหรับวัตถุอื่น (4 แทนที่จะเป็น 3)


13

ใน python ทุกอย่างเป็นวัตถุเช่นเดียวกับตัวแปรประเภท "ง่าย" (int, float ฯลฯ .. )

เมื่อคุณเปลี่ยนค่าตัวแปรคุณจะเปลี่ยนตัวชี้และถ้าคุณเปรียบเทียบระหว่างสองตัวแปรก็จะเปรียบเทียบพอยน์เตอร์ (เพื่อความชัดเจนตัวชี้คือที่อยู่ในหน่วยความจำคอมพิวเตอร์จริงที่เก็บตัวแปรไว้)

ด้วยเหตุนี้เมื่อคุณเปลี่ยนค่าตัวแปรภายในคุณจะเปลี่ยนค่าในหน่วยความจำและจะส่งผลต่อตัวแปรทั้งหมดที่ชี้ไปที่ที่อยู่นี้

ตัวอย่างเช่นเมื่อคุณทำ:

a = b =  5 

ซึ่งหมายความว่า a และ b ชี้ไปยังที่อยู่เดียวกันในหน่วยความจำที่มีค่า 5 แต่เมื่อคุณทำ:

a = 6

ไม่มีผลกับ b เพราะตอนนี้ a ชี้ไปยังตำแหน่งหน่วยความจำอื่นที่มี 6 และ b ยังคงชี้ไปยังที่อยู่หน่วยความจำที่มี 5

แต่เมื่อคุณทำ:

a = b = [1,2,3]

a และ b ชี้ไปที่ตำแหน่งเดียวกันอีกครั้ง แต่ความแตกต่างคือถ้าคุณเปลี่ยนค่ารายการค่าใดรายการหนึ่ง:

a[0] = 2

มันเปลี่ยนค่าของหน่วยความจำที่ a is point อยู่ แต่ a ยังคงชี้ไปยังแอดเดรสเดียวกับ b และด้วยเหตุนี้ b ก็เปลี่ยนไปเช่นกัน


6
สิ่งนี้ทำให้เข้าใจผิดอย่างมาก ตัวชี้ไม่สามารถมองเห็นได้อย่างแน่นอนในระดับ Python และอย่างน้อยสองในสี่การใช้งานหลัก (PyPy และ Jython) จะไม่ใช้มันแม้แต่ในการนำไปใช้งาน
ยกเลิก

1
คุณสามารถอ่านและสำรวจภายใน python ได้และคุณจะพบว่าทุกตัวแปรใน python นั้นเป็นตัวชี้
Ori Seri

4
ไม่ได้ในการนำ Python (CPython) มาใช้งานตัวแปรทุกตัวจะเป็นตัวชี้ไปยังไฟล์PyObject. ไม่เป็นความจริงในการใช้งานอื่น ๆ เช่น PyPy หรือ Jython (ในความเป็นจริงยังไม่ชัดเจนว่ามันจะเป็นจริงได้อย่างไรเพราะภาษาที่นำไปใช้งานเหล่านั้นเขียนด้วยตัวชี้ไม่ได้)
ยกเลิก

ฉันคิดว่าการใช้ "ตัวชี้" ในแง่แนวคิดนั้นใช้ได้ (อาจมีข้อจำกัดความรับผิดชอบว่าการนำไปใช้อาจแตกต่างกันไป) โดยเฉพาะหากเป้าหมายคือการถ่ายทอดพฤติกรรม
Levon

@abarnert เมื่อมีคนพูดว่า Python พวกเขาหมายถึง CPython ไม่ใช่การนำไปใช้งานอื่น ๆ เช่นเดียวกับเมื่อมีคนพูดว่าคลีเน็กซ์หมายถึงกระดาษเช็ดหน้า การเล่นเกมอรรถศาสตร์ในความคิดเห็นเหล่านี้ไม่จำเป็นจริงๆ สำหรับสิ่งที่เขาเขียนเป็นพฤติกรรมของสิ่งที่เขาอธิบายผิดหรือไม่?
swade

10

คุณสามารถใช้id(name)เพื่อตรวจสอบว่าชื่อสองชื่อแสดงถึงวัตถุเดียวกันหรือไม่:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

รายการไม่แน่นอน; หมายความว่าคุณสามารถเปลี่ยนค่าได้โดยไม่ต้องสร้างวัตถุใหม่ อย่างไรก็ตามขึ้นอยู่กับว่าคุณเปลี่ยนค่าอย่างไร:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

หากคุณกำหนดรายการใหม่ให้กับaid ของมันจะเปลี่ยนไปดังนั้นจึงไม่มีผลbกับcค่าของ:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

จำนวนเต็มไม่เปลี่ยนรูปดังนั้นคุณไม่สามารถเปลี่ยนค่าได้โดยไม่ต้องสร้างวัตถุใหม่:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1

1
idไม่จำเป็นต้องเป็นตำแหน่งหน่วยความจำ ตามที่เอกสารกล่าวไว้สิ่งนี้จะคืนค่า "เอกลักษณ์ ... จำนวนเต็ม ... ซึ่งรับประกันว่าจะไม่ซ้ำกันและคงที่สำหรับวัตถุนี้ตลอดอายุการใช้งาน" CPython ใช้ที่อยู่หน่วยความจำเป็นidแต่การใช้งาน Python อื่น ๆ อาจไม่ได้ ตัวอย่างเช่น PyPy ไม่ และการพูดว่า "ตัวแปรสองตัวชี้ไปที่ตำแหน่งหน่วยความจำเดียวกัน" ทำให้ทุกคนเข้าใจผิดในรูปแบบ C "สองชื่อสำหรับวัตถุเดียวกัน" มีทั้งความถูกต้องมากกว่าและทำให้เข้าใจผิดน้อยลง
ยกเลิก

@abarnert ขอบคุณสำหรับคำชี้แจงฉันอัปเดตคำตอบ
jurgenreza

7

ในตัวอย่างแรกของa = b = c = [1, 2, 3]คุณคุณกำลังพูดว่า:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

หากคุณต้องการตั้งค่า 'a' เท่ากับ 1, 'b' เท่ากับ '2' และ 'c' เท่ากับ 3 ให้ลองทำดังนี้:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

หวังว่านี่จะช่วยได้!


4

ในกรณีแรกคุณกำลังกำหนดชื่อหลายชื่อให้กับไฟล์ list . มีเพียงรายการเดียวเท่านั้นที่สร้างขึ้นในหน่วยความจำและชื่อทั้งหมดอ้างถึงตำแหน่งนั้น ดังนั้นการเปลี่ยนรายการโดยใช้ชื่อใด ๆ จะเป็นการแก้ไขรายการในหน่วยความจำ

ในกรณีที่สองสำเนาหลายชุดที่มีค่าเดียวกันจะถูกสร้างขึ้นในหน่วยความจำ ดังนั้นแต่ละสำเนาจึงไม่ขึ้นต่อกัน


3

สิ่งที่คุณต้องการคือ:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)

3

รหัสที่ทำในสิ่งที่ฉันต้องการอาจเป็น:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

ผลลัพธ์:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.