Python: การเปลี่ยนค่าในทูเปิล


124

ฉันยังใหม่กับ python ดังนั้นคำถามนี้อาจจะเป็นพื้นฐานเล็กน้อย ฉันเรียกทูเพิลvaluesซึ่งมีสิ่งต่อไปนี้:

('275', '54000', '0.0', '5000.0', '0.0')

ฉันต้องการเปลี่ยนค่าแรก (กล่าวคือ275) ในทูเปิลนี้ แต่ฉันเข้าใจว่าสิ่งที่ไม่เปลี่ยนรูปจึงvalues[0] = 200ไม่ได้ผล ฉันจะบรรลุเป้าหมายนี้ได้อย่างไร?


24
tuples ไม่เปลี่ยนรูปคุณต้องสร้างทูเพิลใหม่เพื่อให้บรรลุสิ่งนี้
Hunter McMillen

คำตอบ:


178

ก่อนอื่นคุณต้องถามว่าทำไมคุณถึงต้องการทำสิ่งนี้?

แต่เป็นไปได้ผ่าน:

t = ('275', '54000', '0.0', '5000.0', '0.0')
lst = list(t)
lst[0] = '300'
t = tuple(lst)

แต่ถ้าคุณจำเป็นต้องเปลี่ยนสิ่งต่างๆคุณอาจจะดีกว่าที่จะเก็บไว้เป็นไฟล์ list


4
กรณีการใช้งานอย่างหนึ่งคือถ้าคุณจัดเก็บลำดับเล็ก ๆ จำนวนมากโดยที่ค่าต่างๆแทบจะไม่เปลี่ยนแปลง แต่ในบางครั้งอาจต้องการ สำหรับลำดับความยาวขนาดเล็ก แต่ไม่เป็นศูนย์การใช้หน่วยความจำของทูเพิล (60 ไบต์สำหรับองค์ประกอบเดียว) เทียบกับรายการ (104 ไบต์) และสร้างความแตกต่าง การใช้งานอีกกรณีหนึ่งคือ namestuples เนื่องจากไม่มีชื่อรายการโดยกำเนิด
Michael Scott Cuthbert

82
คำตอบ "ทำไมคุณถึงต้องการทำเช่นนั้น" ทำให้ฉันรู้สึกแย่ใน StackOverflow อย่าถือว่าผู้โพสต์ต้นฉบับ "ต้องการ" ทำเช่นนั้น อันที่จริงมันเป็นการดีกว่าที่จะไม่คิดว่าผู้โพสต์ต้นฉบับกำลังสร้างทั้งหมดนี้ตั้งแต่เริ่มต้นไม่ทราบดีกว่า บ่อยครั้งที่เรากำลังจัดการกับเอาต์พุตจากโมดูลหรือแหล่งข้อมูลอื่นในรูปแบบหรือประเภทที่เราไม่สามารถควบคุมได้
rtphokie

9
@rtphokie ต้องการที่จะกลายพันธุ์คอนเทนเนอร์ที่ไม่เปลี่ยนรูป (ดังนั้น "ทำไม" จึงเป็นคำถามที่ถูกต้องมาก) แตกต่างจากการตีความรูปแบบต่างๆซึ่งบางส่วนอาจเป็นทูเปิล ขอบคุณที่ระบาย :)
Jon Clements

7
ดูเหมือนว่าไม่จำเป็นและหน่วยความจำไม่มีประสิทธิภาพในการสร้างใหม่ในรายการและใช้ตัวแปรชั่วคราว คุณสามารถแกะเป็น tuple ที่มีชื่อเดียวกันและในขณะที่แกะการอัปเดตสิ่งที่จำเป็นต้องอัปเดต
Brian Spiering

5
@JonClements คุณให้ประเด็นที่มีค่ามากว่าการทำแบบนี้เป็นการปฏิบัติที่ไม่ดี นั่นควรอยู่ในคำตอบของคุณ อย่างไรก็ตามคำถามเชิงโวหารมักถูกตีความว่าเป็นการเสื่อมเสียโดยไม่จำเป็น ข้อมูลดังกล่าวมีโครงสร้างที่ดีกว่าในรูปแบบ: "นี่เป็นการปฏิบัติที่ไม่ดีเพราะ ... "หรือแม้กระทั่ง"พิจารณาอย่างรอบคอบว่าคุณต้องการสิ่งนี้จริง ๆ หรือไม่โดยปกติจะมีความหมายถึงความผิดพลาดในการออกแบบเนื่องจาก ... "
Philip Couling

74

ขึ้นอยู่กับปัญหาของคุณการแบ่งส่วนอาจเป็นวิธีแก้ปัญหาที่เรียบร้อยจริงๆ:

>>> b = (1, 2, 3, 4, 5)
>>> b[:2] + (8,9) + b[3:]
(1, 2, 8, 9, 4, 5)
>>> b[:2] + (8,) + b[3:]
(1, 2, 8, 4, 5)

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


24

ดังที่ Trufa ได้แสดงไปแล้วโดยทั่วไปมีสองวิธีในการแทนที่องค์ประกอบของทูเปิลที่ดัชนีที่กำหนด แปลงทูเพิลเป็นรายการแทนที่องค์ประกอบและแปลงกลับหรือสร้างทูเพิลใหม่โดยการเรียงต่อกัน

In [1]: def replace_at_index1(tup, ix, val):
   ...:     lst = list(tup)
   ...:     lst[ix] = val
   ...:     return tuple(lst)
   ...:

In [2]: def replace_at_index2(tup, ix, val):
   ...:     return tup[:ix] + (val,) + tup[ix+1:]
   ...:

แล้ววิธีไหนดีกว่าเร็วกว่ากัน?

ปรากฎว่าสำหรับ tuples สั้น ๆ (บน Python 3.3) การเรียงต่อกันนั้นเร็วขึ้นจริง!

In [3]: d = tuple(range(10))

In [4]: %timeit replace_at_index1(d, 5, 99)
1000000 loops, best of 3: 872 ns per loop

In [5]: %timeit replace_at_index2(d, 5, 99)
1000000 loops, best of 3: 642 ns per loop

แต่ถ้าเราดูสิ่งที่ยาวขึ้นการแปลงรายการเป็นวิธีที่จะไป:

In [6]: k = tuple(range(1000))

In [7]: %timeit replace_at_index1(k, 500, 99)
100000 loops, best of 3: 9.08 µs per loop

In [8]: %timeit replace_at_index2(k, 500, 99)
100000 loops, best of 3: 10.1 µs per loop

สำหรับสิ่งที่มีความยาวมากการแปลงรายการจะดีกว่ามาก!

In [9]: m = tuple(range(1000000))

In [10]: %timeit replace_at_index1(m, 500000, 99)
10 loops, best of 3: 26.6 ms per loop

In [11]: %timeit replace_at_index2(m, 500000, 99)
10 loops, best of 3: 35.9 ms per loop

นอกจากนี้ประสิทธิภาพของวิธีการเรียงต่อกันยังขึ้นอยู่กับดัชนีที่เราแทนที่องค์ประกอบ สำหรับวิธีการแสดงรายการดัชนีไม่เกี่ยวข้อง

In [12]: %timeit replace_at_index1(m, 900000, 99)
10 loops, best of 3: 26.6 ms per loop

In [13]: %timeit replace_at_index2(m, 900000, 99)
10 loops, best of 3: 49.2 ms per loop

ดังนั้น: หากทูเปิลของคุณสั้นให้หั่นเป็นชิ้นและเชื่อมต่อกัน ถ้ามันยาวให้ทำการแปลงรายการ!


1
@ErikAronesty ไม่เสมอไป. เมื่อกรณีที่เป็นประโยชน์กำลังขยายคลาสที่คุณไม่สามารถเปลี่ยนแปลงได้และวิธีการของใครจะส่งคืนทูเปิลที่คุณต้องการเปลี่ยนเฉพาะองค์ประกอบแรก return (val,) + res [1:] ชัดเจนกว่า res2 = list (res); res2 [0] = วาล; return tuple (res2)
yucer

9

เป็นไปได้ด้วยซับเดียว:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])

1
คุณจะเปลี่ยนเฉพาะองค์ประกอบที่สามในvaluesสิ่งนี้ได้อย่างไร?
sdbbs

2
นี่คือวิธีที่คุณสามารถเปลี่ยนองค์ประกอบใด ๆ ในทูเพิล -i = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering

8

ไม่ใช่ว่าจะดีกว่า แต่ถ้าใครอยากรู้ก็สามารถทำได้ในบรรทัดเดียวด้วย:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])

เร็วกว่านั้นtuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))ไหม (ทำไมไม่สร้างความเข้าใจ)
ไม่ระบุชื่อ

8

ฉันเชื่อว่าสิ่งนี้ตอบคำถามได้ในทางเทคนิค แต่อย่าทำที่บ้าน ในขณะนี้คำตอบทั้งหมดเกี่ยวข้องกับการสร้างทูเพิลใหม่ แต่คุณสามารถใช้ctypesเพื่อแก้ไขทูเพิลในหน่วยความจำได้ อาศัยรายละเอียดการใช้งานต่างๆของ CPython บนระบบ 64 บิตวิธีหนึ่งในการดำเนินการดังต่อไปนี้:

def modify_tuple(t, idx, new_value):
    # `id` happens to give the memory address in CPython; you may
    # want to use `ctypes.addressof` instead.
    element_ptr = (ctypes.c_longlong).from_address(id(t) + (3 + idx)*8)
    element_ptr.value = id(new_value)
    # Manually increment the reference count to `new_value` to pretend that
    # this is not a terrible idea.
    ref_count = (ctypes.c_longlong).from_address(id(new_value))
    ref_count.value += 1

t = (10, 20, 30)
modify_tuple(t, 1, 50)   # t is now (10, 50, 30)
modify_tuple(t, -1, 50)  # Will probably crash your Python runtime

1
ควรรู้บางสิ่งที่แตกต่างจากคำตอบปกติเสมอ ไชโย!
Kartheek Palepu

ผู้ที่ต้องการเขียนโค้ดใน C ควรจะทำอย่างนั้น แฮ็คล่ามแบบนั้นก็คิดถึงหัวข้อที่นี่ นอกจากนี้ยังไม่น่าเชื่อถือเนื่องจากรายละเอียดการใช้งาน cPython สามารถเปลี่ยนแปลงได้ตลอดเวลาโดยไม่มีการเตือนและมีแนวโน้มที่จะทำลายรหัสใด ๆ ที่อาศัยสิ่งทอที่ไม่สามารถเลียนแบบได้ นอกจากนี้ทูเปิลยังเป็นวัตถุคอลเลกชันที่มีน้ำหนักเบาที่สุดในไพ ธ อนดังนั้นจึงไม่มีปัญหาในการสร้างใหม่ หากคุณจำเป็นต้องแก้ไขคอลเลกชันบ่อยๆให้ใช้รายการแทน
Bachsau

1
คุณลืมลดจำนวนการอ้างอิงลงในมูลค่าที่คุณทิ้งไป นั่นจะทำให้เกิดการรั่วไหล
wizzwizz4

6

ตามที่ Hunter McMillen เขียนไว้ในความคิดเห็นสิ่งที่ไม่เปลี่ยนแปลงคุณต้องสร้างทูเพิลใหม่เพื่อให้บรรลุเป้าหมายนี้ ตัวอย่างเช่น:

>>> tpl = ('275', '54000', '0.0', '5000.0', '0.0')
>>> change_value = 200
>>> tpl = (change_value,) + tpl[1:]
>>> tpl
(200, '54000', '0.0', '5000.0', '0.0')

3

แก้ไข: สิ่งนี้ใช้ไม่ได้กับสิ่งที่มีรายการที่ซ้ำกัน !!

ตามแนวคิดของ Pooya :

หากคุณวางแผนที่จะทำสิ่งนี้บ่อยๆ (ซึ่งคุณไม่ควรเพราะสิ่งที่ไม่สามารถเปลี่ยนแปลงได้ด้วยเหตุผล) คุณควรทำสิ่งนี้:

def modTupByIndex(tup, index, ins):
    return tuple(tup[0:index]) + (ins,) + tuple(tup[index+1:])

print modTupByIndex((1,2,3),2,"a")

หรือตามความคิดของจอน :

def modTupByIndex(tup, index, ins):
    lst = list(tup)
    lst[index] = ins
    return tuple(lst)

print modTupByIndex((1,2,3),1,"a")

3

tupleเริ่มถามตัวเองว่าทำไมคุณต้องการที่จะกลายพันธุ์ของคุณ มีเหตุผลว่าทำไมสตริงและทูเปิลไม่เปลี่ยนรูปใน Ptyhonถ้าคุณต้องการกลายพันธุ์ของคุณtupleมันน่าจะเป็นlistแทน

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

ผมขอแนะนำให้ใช้mutabletuple(typename, field_names, default=MtNoDefault)จากmutabletuple 0.2 โดยส่วนตัวแล้วฉันคิดว่าวิธีนี้เป็นวิธีที่ง่ายและอ่านง่ายกว่า ผู้อ่านโค้ดส่วนบุคคลจะรู้ว่าผู้เขียนตั้งใจที่จะกลายพันธุ์ทูเปิลนี้ในอนาคต ข้อเสียเมื่อเปรียบเทียบกับlistวิธีการแปลงด้านบนคือคุณต้องนำเข้าไฟล์ py เพิ่มเติม

from mutabletuple import mutabletuple

myTuple = mutabletuple('myTuple', 'v w x y z')
p = myTuple('275', '54000', '0.0', '5000.0', '0.0')
print(p.v) #print 275
p.v = '200' #mutate myTuple
print(p.v) #print 200

TL; DR : tupleอย่าพยายามที่จะกลายพันธุ์ ถ้าคุณทำและมันคือการดำเนินการครั้งเดียวแปลงtupleไปยังรายการกลายพันธุ์มันเลี้ยวlistเข้าใหม่และมอบหมายกลับไปตัวแปรถือเก่าtuple tupleหากปรารถนาtupleและอย่างใดต้องการที่จะหลีกเลี่ยงและต้องการที่จะกลายพันธุ์มากกว่าหนึ่งครั้งแล้วสร้างlistmutabletuple


2

ตามไอเดียของจอนและTrufa ที่รัก

def modifyTuple(tup, oldval, newval):
    lst=list(tup)
    for i in range(tup.count(oldval)):
        index = lst.index(oldval)
        lst[index]=newval

    return tuple(lst)

print modTupByIndex((1, 1, 3), 1, "a")

มันเปลี่ยนค่าเก่าทั้งหมดของคุณที่เกิดขึ้น


สิ่งนี้จะค่อนข้างอึดอัด (สำหรับการไม่มีคำที่ดีกว่า) ถ้าคุณต้องเปลี่ยนค่าหลายค่า แต่อีกครั้งทำไมคุณถึงต้องการแก้ไขทูเปิลตั้งแต่แรก ...
Trufa

@ Trufa ใช่ฉันกำลังพยายามเขียนอยู่: D
Pooya

ชื่อเมธอด modified_tuple_by_index ไม่ถูกต้องและทำให้เกิดความสับสน
msw

1

คุณทำไม่ได้ หากคุณต้องการเปลี่ยนแปลงคุณต้องใช้รายการแทนทูเพิล

โปรดทราบว่าคุณสามารถสร้างทูเพิลใหม่ที่มีค่าใหม่เป็นองค์ประกอบแรกแทนได้


0

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

นี่คือตัวอย่างที่ฉันใช้ในการสร้างสีที่อ่อนกว่า (ตอนนั้นฉันเปิดไว้แล้ว):

colour = tuple([c+50 for c in colour])

มันทำอะไรมันต้องผ่าน 'สี' ของทูเพิลและอ่านแต่ละรายการทำอะไรกับมันและสุดท้ายก็เพิ่มลงในทูเพิลใหม่

สิ่งที่คุณต้องการจะเป็นดังนี้:

values = ('275', '54000', '0.0', '5000.0', '0.0')

values  = (tuple(for i in values: if i = 0: i = 200 else i = values[i])

สิ่งที่เฉพาะเจาะจงนั้นไม่ได้ผล แต่แนวคิดคือสิ่งที่คุณต้องการ

tuple = (0, 1, 2)

tuple = วนซ้ำผ่านทูเพิลแก้ไขแต่ละรายการตามต้องการ

นั่นคือแนวคิด


0

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

new = 24
t = (1, 2, 3)
t = (t[0],t[1],new)

>>> (1, 2, 24)

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

ดังนั้นในกรณีนี้จะมีลักษณะดังนี้:

new = '200'
t = ('275', '54000', '0.0', '5000.0', '0.0')
t = (new, t[1], t[2], t[3], t[4])

>>> ('200', '54000', '0.0', '5000.0', '0.0')

ใช้งานได้ก็ต่อเมื่อนี่คือ tuple แบบคงที่ที่มีความยาวที่ทราบ ไม่ใช่โค้ดมักจะล้มเหลวไม่ช้าก็เร็วเพราะมันเจาะจงเกินไป ...
patroqueeet

ใช่ - @patroqueeet ดังนั้นฉันจึงระบุข้อเสียของแนวทางนี้ไว้อย่างชัดเจนหลังจาก But: ...-. โปรดพิจารณาการลงคะแนนของคุณอีกครั้งขอบคุณ;)
GordanTrevis

1
เดี๋ยวก่อนพิจารณาใหม่แล้วคลิกปุ่ม แต่ตอนนี้การโหวตถูกล็อคโดย SO: /
patroqueeet

0

TLDR; "วิธีแก้ปัญหา" คือการสร้างวัตถุทูเพิลใหม่ไม่ใช่การแก้ไขต้นฉบับ

แม้ว่านี่จะเป็นคำถามที่เก่ามาก แต่ก็มีคนบอกฉันเกี่ยวกับความบ้าคลั่งของ Python ที่กลายพันธุ์ ซึ่งฉันรู้สึกประหลาดใจ / ทึ่งมากและการทำ googling ฉันมาที่นี่ (และตัวอย่างอื่น ๆ ที่คล้ายกันทางออนไลน์)

ฉันทำการทดสอบเพื่อพิสูจน์ทฤษฎีของฉัน

หมายเหตุให้==ค่าความเท่าเทียมกันในขณะที่ความเท่าเทียมกันของการisอ้างอิง (เป็น obj เช่นเดียวกับ obj b)

a = ("apple", "canana", "cherry")
b = tuple(["apple", "canana", "cherry"])
c = a

print("a: " + str(a))
print("b: " + str(b))
print("c: " + str(c))
print("a == b :: %s" % (a==b))
print("b == c :: %s" % (b==c))
print("a == c :: %s" % (a==c))
print("a is b :: %s" % (a is b))
print("b is c :: %s" % (b is c))
print("a is c :: %s" % (a is c))

d = list(a)
d[1] = "kiwi"
a = tuple(d)

print("a: " + str(a))
print("b: " + str(b))
print("c: " + str(c))
print("a == b :: %s" % (a==b))
print("b == c :: %s" % (b==c))
print("a == c :: %s" % (a==c))
print("a is b :: %s" % (a is b))
print("b is c :: %s" % (b is c))
print("a is c :: %s" % (a is c))

อัตราผลตอบแทน:

a: ('apple', 'canana', 'cherry')
b: ('apple', 'canana', 'cherry')
c: ('apple', 'canana', 'cherry')
a == b :: True
b == c :: True
a == c :: True
a is b :: False
b is c :: False
a is c :: True
a: ('apple', 'kiwi', 'cherry')
b: ('apple', 'canana', 'cherry')
c: ('apple', 'canana', 'cherry')
a == b :: False
b == c :: True
a == c :: False
a is b :: False
b is c :: False
a is c :: False

0

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

ตัวอย่างเช่น

my_list = [1,2]
tuple_of_lists = (my_list,'hello')
print(tuple_of_lists) # ([1, 2], 'hello')
my_list[0] = 0
print(tuple_of_lists) # ([0, 2], 'hello')

-2

ฉันทำอย่างนี้:

list = [1,2,3,4,5]
tuple = (list)

และหากต้องการเปลี่ยนแปลงเพียงแค่ทำ

list[0]=6

และคุณสามารถเปลี่ยนทูเพิลได้: D

นี่คือมันคัดลอกมาจาก IDLE ทั้งหมด

>>> list=[1,2,3,4,5,6,7,8,9]

>>> tuple=(list)

>>> print(tuple)

[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list[0]=6

>>> print(tuple)

[6, 2, 3, 4, 5, 6, 7, 8, 9]

2
ทูเพิลเป็นรายการไม่ใช่ทูเปิล x = (y)ไม่ทำอะไรเลยนอกจากกำหนด y ให้กับ x
m4tx

2
นอกจากนี้การใช้ชื่อ "tuple" และ "list" เป็นตัวแปรจะทำให้ยากที่จะเปรียบเทียบทูเปิลและประเภทรายการในภายหลัง
Michael Scott Cuthbert

-4

คุณสามารถเปลี่ยนค่าของทูเปิลโดยใช้สำเนาโดยการอ้างอิง

>>> tuple1=[20,30,40]

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

>>> print(tuple2)
    [20, 10, 40]

>>> print(tuple1)
    [20, 10, 40]

1
เฉพาะสิ่งเหล่านี้คือรายการไม่ใช่สิ่งที่คุณสามารถเปลี่ยนแปลงได้โดยมีการอ้างอิงครั้งที่สองหรือไม่
Bachsau
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.