Python: thay đổi giá trị trong một bộ giá trị


124

Tôi mới sử dụng python nên câu hỏi này có thể hơi cơ bản. Tôi có một tuple được gọi là valuescó chứa thông tin sau:

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

Tôi muốn thay đổi giá trị đầu tiên (tức là 275) trong bộ giá trị này nhưng tôi hiểu rằng bộ giá trị là bất biến nên values[0] = 200sẽ không hoạt động. Làm thế nào tôi có thể đạt được điều này?


24
các bộ giá trị là bất biến , bạn cần tạo một bộ giá trị mới để đạt được điều này.
Hunter McMillen

Câu trả lời:


178

Đầu tiên bạn cần hỏi, tại sao bạn muốn làm điều này?

Nhưng có thể thông qua:

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

Nhưng nếu bạn cần thay đổi mọi thứ, có lẽ bạn nên giữ nó như một list


4
Một trường hợp sử dụng là nếu bạn đang lưu trữ một số lượng lớn các chuỗi nhỏ trong đó các giá trị hiếm khi thay đổi nhưng trong một số trường hợp họ có thể muốn. Đối với một chuỗi nhỏ nhưng có độ dài khác 0, mức tiêu thụ bộ nhớ của tuple (60 byte cho một phần tử) so với danh sách (104 byte) và tạo ra sự khác biệt. Một trường hợp sử dụng khác dành cho các nhóm có tên vì danh sách có tên không tồn tại tự nhiên.
Michael Scott Cuthbert

81
Câu trả lời "tại sao bạn muốn làm điều đó" khiến tôi phát điên trên StackOverflow. Đừng cho rằng người đăng ban đầu "muốn" làm điều đó. Trên thực tế, tốt hơn là không nên cho rằng người đăng ban đầu đang tạo ra tất cả những điều này từ đầu không biết gì tốt hơn. Thông thường, chúng tôi xử lý đầu ra từ một mô-đun hoặc nguồn dữ liệu khác ở định dạng hoặc kiểu mà chúng tôi không thể kiểm soát.
rtphokie

9
@rtphokie muốn thay đổi một vùng chứa bất biến (do đó, "tại sao" là một câu hỏi rất hợp lệ) khác với việc diễn giải các định dạng khác nhau, một số trong đó có thể là một bộ. Cảm ơn vì đã trút giận :)
Jon Clements

7
Có vẻ như không cần thiết và bộ nhớ không hiệu quả để tạo lại thành một danh sách và sử dụng một biến tạm thời. Bạn chỉ có thể giải nén vào một bộ dữ liệu có cùng tên và trong khi giải nén bất kỳ bản cập nhật nào cần cập nhật.
Brian Spiering

5
@JonClements Bạn đưa ra một điểm rất có giá trị rằng làm điều này là hành động xấu. Điều đó nên có trong câu trả lời của bạn. Tuy nhiên, các câu hỏi tu từ thường bị hiểu là xúc phạm không cần thiết. Những thông tin này được cấu trúc tốt hơn trong các hình thức: "Đây là thói quen xấu vì ..." hoặc thậm chí "Hãy xem xét một cách cẩn thận nếu bạn thực sự cần điều này, nó thường ngụ ý một lỗi trong thiết kế bởi vì ..."
Philip Couling

74

Tùy thuộc vào vấn đề của bạn, cắt có thể là một giải pháp thực sự gọn gàng:

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

Điều này cho phép bạn thêm nhiều phần tử hoặc cũng có thể thay thế một vài phần tử (đặc biệt nếu chúng là "hàng xóm". Trong trường hợp trên, truyền vào danh sách có lẽ phù hợp và dễ đọc hơn (mặc dù ký hiệu cắt ngắn hơn nhiều)).


24

Như Trufa đã chỉ ra, về cơ bản có hai cách để thay thế phần tử của tuple tại một chỉ mục nhất định. Chuyển đổi tuple thành danh sách, thay thế phần tử và chuyển đổi trở lại hoặc tạo một tuple mới bằng cách nối.

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:]
   ...:

Vì vậy, phương pháp nào tốt hơn, tức là, nhanh hơn?

Hóa ra là đối với các bộ giá trị ngắn (trên Python 3.3), việc nối thực sự nhanh hơn!

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

Tuy nhiên, nếu chúng ta xem xét các bộ giá trị dài hơn, chuyển đổi danh sách là cách để thực hiện:

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

Đối với các bộ giá trị rất dài, chuyển đổi danh sách về cơ bản tốt hơn đáng kể!

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

Ngoài ra, hiệu suất của phương pháp nối phụ thuộc vào chỉ mục mà chúng ta thay thế phần tử. Đối với phương pháp danh sách, chỉ mục là không liên quan.

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

Vì vậy: Nếu tuple của bạn ngắn, hãy cắt và nối. Nếu nó dài, hãy thực hiện chuyển đổi danh sách!


1
@ErikAronesty không phải lúc nào cũng vậy. Một khi trường hợp hữu ích đang mở rộng một lớp mà bạn không thể thay đổi và các phương thức của nó trả về một bộ giá trị mà từ đó bạn chỉ muốn thay đổi phần tử đầu tiên. return (val,) + res [1:] rõ ràng hơn res2 = list (res); res2 [0] = val; trở tuple (res2)
yucer

9

Có thể với một lớp lót:

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

1
Làm thế nào bạn sẽ thay đổi chỉ phần tử thứ ba valuesvới điều này?
sdbbs

2
Đây là cách bạn có thể thay đổi bất kỳ yếu tố trong một tuple -i = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering

8

Không phải điều này là cao cấp, nhưng nếu ai tò mò, nó có thể được thực hiện trên một dòng với:

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

Đó là nhanh hơn tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Tại sao máy phát điện không hiểu?)
Anonymous

8

Tôi tin rằng kỹ thuật này trả lời câu hỏi, nhưng không làm điều này ở nhà. Hiện tại, tất cả các câu trả lời đều liên quan đến việc tạo một bộ mới, nhưng bạn có thể sử dụng ctypesđể sửa đổi một bộ trong bộ nhớ. Dựa trên các chi tiết triển khai khác nhau của CPython trên hệ thống 64 bit, một cách để thực hiện việc này như sau:

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
Luôn luôn tốt khi biết điều gì đó khác với những câu trả lời thông thường. Chúc mừng!
Kartheek Palepu

Những người muốn viết mã bằng C có lẽ chỉ nên làm chính xác điều đó. Lấy cắp thông dịch viên như vậy chỉ bỏ sót chủ đề ở đây. Nó cũng không đáng tin cậy, vì chi tiết triển khai cPython có thể thay đổi bất kỳ lúc nào mà không có cảnh báo và có khả năng phá vỡ bất kỳ mã nào dựa trên bộ giá trị không thể thay đổi. Hơn nữa, bộ giá trị là đối tượng thu thập nhẹ nhất trong python, vì vậy không có vấn đề gì với việc tạo một cái mới. Nếu bạn thực sự cần sửa đổi bộ sưu tập thường xuyên, hãy sử dụng danh sách để thay thế.
Bachsau

1
Bạn đã quên giảm số lượng tham chiếu xuống giá trị mà bạn đang loại bỏ. Điều đó sẽ gây ra rò rỉ.
wizzwizz 4

6

Như Hunter McMillen đã viết trong các nhận xét, các bộ giá trị là bất biến, bạn cần tạo một bộ giá trị mới để đạt được điều này. Ví dụ:

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

CHỈNH SỬA: Điều này không hoạt động trên các bộ giá trị với các mục trùng lặp !!

Dựa trên ý tưởng của Pooya :

Nếu bạn đang lên kế hoạch làm điều này thường xuyên (điều bạn không nên làm vì các bộ giá trị không thể thay đổi được vì một lý do), bạn nên làm điều gì đó như sau:

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

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

Hoặc dựa trên ý tưởng của Jon :

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

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

3

Frist, tự hỏi bản thân tại sao bạn muốn thay đổi của bạn tuple. Có một lý do tại sao string và tuple là bất biến trong Ptyhon , nếu bạn muốn thay đổi tuplenó thì có lẽ nó phải là một list.

Thứ hai, nếu bạn vẫn muốn thay đổi tuple của mình thì bạn có thể chuyển đổi tuple của mình tuplethành a listrồi chuyển đổi lại và gán lại tuple mới cho cùng một biến. Điều này thật tuyệt nếu bạn chỉ định thay đổi bộ tuple của mình một lần . Nếu không, cá nhân tôi nghĩ rằng điều đó là phản trực giác. Bởi vì về cơ bản, nó là tạo một tuple mới và bất cứ khi nào bạn muốn thay đổi tuple, bạn sẽ phải thực hiện chuyển đổi. Ngoài ra, Nếu bạn đọc mã, sẽ rất khó hiểu khi nghĩ tại sao không chỉ tạolist ? Nhưng nó rất hay vì nó không yêu cầu bất kỳ thư viện nào.

Tôi đề nghị sử dụng mutabletuple(typename, field_names, default=MtNoDefault)từ phiên bản thay đổi 0,2 . Cá nhân tôi nghĩ rằng cách này là trực quandễ đọc hơn. Cá nhân đọc mã sẽ biết rằng người viết có ý định thay đổi bộ tuple này trong tương lai. Nhược điểm so với listphương pháp chuyển đổi ở trên là điều này yêu cầu bạn nhập tệp py bổ sung.

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 : Đừng cố thay đổi tuple. nếu bạn thực hiện và đó là hoạt động một lần, hãy chuyển đổi tuplethành danh sách, thay đổi nó, chuyển listthành mới tuplevà gán lại cho biến đang giữ cũ tuple. Nếu ham muốn tuplevà bằng cách nào đó muốn tránh listvà muốn đột biến nhiều hơn một lần thì hãy tạo mutabletuple.


2

dựa trên Ý tưởng của JonTrufa thân yêu

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

nó thay đổi tất cả các lần xuất hiện giá trị cũ của bạn


Đây sẽ là khá khó chịu (cho việc thiếu một từ tốt hơn) nếu bạn đã thay đổi nhiều giá trị nhưng sau đó một lần nữa, tại sao bạn sẽ muốn được sửa đổi một tuple ở nơi đầu tiên ...
Trufa

@Trufa vâng, tôi đang cố gắng để viết nó: D
Pooya

Tên phương thức edit_tuple_by_index không chính xác và có thể gây nhầm lẫn.
msw

1

Bạn không thể. Nếu bạn muốn thay đổi nó, bạn cần phải sử dụng một danh sách thay vì một tuple.

Lưu ý rằng thay vào đó bạn có thể tạo một bộ tuple mới có giá trị mới làm phần tử đầu tiên của nó.


0

Tôi đã tìm thấy cách tốt nhất để chỉnh sửa bộ giá trị là tạo lại bộ giá trị bằng cách sử dụng phiên bản trước đó làm cơ sở.

Đây là một ví dụ tôi đã sử dụng để tạo một phiên bản màu sáng hơn (tôi đã mở nó vào thời điểm đó):

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

Nó làm gì, là nó đi qua tuple 'color' và đọc từng mục, làm gì đó với nó, và cuối cùng là thêm nó vào tuple mới.

Vì vậy, những gì bạn muốn sẽ giống như:

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

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

Cái cụ thể đó không hoạt động, nhưng khái niệm là thứ bạn cần.

tuple = (0, 1, 2)

tuple = lặp qua tuple, thay đổi từng mục nếu cần

đó là khái niệm.


0

Tôi đến muộn với trò chơi nhưng tôi nghĩ cách đơn giản nhất , thân thiện với tài nguyên nhất và nhanh nhất (tùy thuộc vào tình huống) , là ghi đè lên chính tuple. Vì điều này sẽ loại bỏ nhu cầu tạo danh sách & biến và được lưu trữ trong một dòng.

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

>>> (1, 2, 24)

Nhưng: Điều này chỉ hữu ích cho các bộ giá trị khá nhỏ và cũng giới hạn bạn ở một giá trị bộ giá cố định, tuy nhiên, đây là trường hợp của các bộ giá trị thường xuyên.

Vì vậy, trong trường hợp cụ thể này, nó sẽ như thế này:

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

điều này chỉ hoạt động nếu đây là một tuple tĩnh với độ dài đã biết. không mã rất có thể sẽ thất bại sớm hay muộn vì quá cụ thể của nó ...
patroqueeet

vâng - @patroqueeet, Vì vậy, tôi đã nêu rõ những nhược điểm của phương pháp này, sau Nhưng: ...-. Vui lòng xem xét lại phiếu phản đối của bạn, cảm ơn;)
GordanTrevis

1
này, xem xét lại và nhấp vào nút. nhưng phiếu bầu hiện đã bị SO khóa: /
patroqueeet

0

tldr; "giải pháp thay thế" là tạo một đối tượng tuple mới, không thực sự sửa đổi

Trong khi đây là một câu hỏi rất cũ, ai đó đã nói với tôi về sự điên rồ của bộ tuples đột biến Python này. Điều mà tôi vô cùng ngạc nhiên / tò mò và đang thực hiện một số thao tác trên googling, tôi đã hạ cánh ở đây (và các mẫu tương tự khác trên mạng)

Tôi đã chạy một số bài kiểm tra để chứng minh lý thuyết của mình

Lưu ý ==không bình đẳng giá trị trong khi bình đẳng istham chiếu (obj a có phải là trường hợp giống như obj b không)

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

Sản lượng:

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

Bạn không thể sửa đổi các mục trong bộ tuple, nhưng bạn có thể sửa đổi thuộc tính của các đối tượng có thể thay đổi trong bộ dữ liệu (ví dụ: nếu các đối tượng đó là danh sách hoặc đối tượng lớp thực tế)

Ví dụ

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

tôi đã làm điều này:

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

và để thay đổi, chỉ cần làm

list[0]=6

và bạn có thể thay đổi một tuple: D

đây là nó được sao chép chính xác từ 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
tuple là một danh sách, không phải là một tuple. x = (y)không làm gì khác ngoài việc gán y cho x.
m4tx

2
cũng bằng cách sử dụng tên "tuple" và "list" làm biến, bạn sẽ khó so sánh các loại tuple và danh sách sau này.
Michael Scott Cuthbert

-4

Bạn có thể thay đổi giá trị của tuple bằng cách sử dụng bản sao bằng cách tham chiếu

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

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

1
Chỉ có điều đây là danh sách, không phải bộ giá trị mà bạn có thể thay đổi bằng mọi cách, có tham chiếu thứ hai hay không.
Bachsau
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.