Python: zmiana wartości w krotce


124

Jestem nowy w Pythonie, więc to pytanie może być trochę podstawowe. Mam zwaną krotkę, valuesktóra zawiera następujące elementy:

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

Chcę zmienić pierwszą wartość (tj. 275) W tej krotce, ale rozumiem, że krotki są niezmienne, więc values[0] = 200nie będą działać. Jak mogę to osiągnąć?


24
krotki są niezmienne , aby to osiągnąć, musisz utworzyć nową krotkę.
Hunter McMillen,

Odpowiedzi:


178

Najpierw musisz zapytać, dlaczego chcesz to zrobić?

Ale jest to możliwe poprzez:

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

Ale jeśli będziesz musiał coś zmienić, prawdopodobnie lepiej zachowaj to jako plik list


4
Jednym z przypadków użycia jest sytuacja, gdy przechowujesz dużą liczbę małych sekwencji, w których wartości rzadko się zmieniają, ale czasami mogą tego chcieć. W przypadku sekwencji o małej, ale niezerowej długości, zużycie pamięci krotki (60 bajtów dla jednego elementu) w porównaniu z listą (104 bajty) ma znaczenie. Innym przykładem użycia jest namedtuples, ponieważ namedlist nie istnieje natywnie.
Michael Scott Cuthbert

81
Odpowiedzi „dlaczego chcesz to zrobić” doprowadzają mnie do szału na StackOverflow. Nie zakładaj, że oryginalny plakat „chce” to zrobić. Właściwie lepiej nie zakładać, że oryginalny plakat tworzy to wszystko od zera, nie wie nic lepszego. Częściej mamy do czynienia z danymi wyjściowymi z innego modułu lub źródła danych w formacie lub typie, którego nie możemy kontrolować.
rtphokie

9
@rtphokie chcąc zmutować niezmienny kontener (dlatego „dlaczego” jest bardzo ważnym pytaniem) różni się od interpretowania różnych formatów, z których niektóre mogą być krotką. Ale dzięki za upust :)
Jon Clements

7
Przekształcenie w listę i użycie zmiennej tymczasowej wydaje się niepotrzebne i nieefektywne pod względem pamięci. Możesz po prostu rozpakować do krotki o tej samej nazwie i podczas rozpakowywania aktualizacji wszystko, co wymaga aktualizacji.
Brian Spiering

5
@JonClements Podkreślasz bardzo cenną uwagę, że robienie tego jest złą praktyką. To powinno być w twojej odpowiedzi. Jednak pytania retoryczne są często interpretowane jako niepotrzebnie uwłaczające. Taka informacja jest lepiej uporządkowana w formie: „To zła praktyka, ponieważ ...” lub nawet „Zastanów się dokładnie, czy naprawdę tego potrzebujesz; zwykle oznacza to błąd w projekcie, ponieważ ...”
Philip Couling

74

W zależności od problemu krojenie może być naprawdę zgrabnym rozwiązaniem:

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

Pozwala to na dodanie wielu elementów lub też zastąpienie kilku elementów (szczególnie jeśli są one „sąsiadami”. W powyższym przypadku rzutowanie na listę jest prawdopodobnie bardziej odpowiednie i czytelne (mimo że notacja wycinania jest znacznie krótsza).


24

Cóż, jak już pokazała Trufa, istnieją zasadniczo dwa sposoby zastąpienia elementu krotki w danym indeksie. Przekonwertuj krotkę na listę, zamień element i przekonwertuj z powrotem lub utwórz nową krotkę przez konkatenację.

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

Więc która metoda jest lepsza, to znaczy szybsza?

Okazuje się, że w przypadku krótkich krotek (w Pythonie 3.3) konkatenacja jest faktycznie szybsza!

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

Jeśli jednak przyjrzymy się dłuższym krotkom, najlepszą drogą jest konwersja listy:

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

W przypadku bardzo długich krotek konwersja listy jest znacznie lepsza!

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

Również wydajność metody konkatenacji zależy od indeksu, w którym zastępujemy element. W przypadku metody listowej indeks nie ma znaczenia.

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

Więc: jeśli twoja krotka jest krótka, pokrój i połącz. Jeśli jest długi, wykonaj konwersję listy!


1
@ErikAronesty nie zawsze. Kiedyś przydatnym przypadkiem jest rozszerzenie klasy, której nie możesz zmienić i której metody zwracają krotkę, z której chcesz zmienić tylko pierwszy element. return (val,) + res [1:] jest jaśniejsze niż res2 = list (res); res2 [0] = val; return tuple (res2)
yucer

9

Jest to możliwe z jednym wkładem:

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

1
Jak zmieniłbyś w tym tylko trzeci element values?
sdbbs

2
Oto, jak możesz zmienić dowolny element w krotce -i = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering

8

Nie żeby to było lepsze, ale jeśli ktoś jest ciekawy, można to zrobić w jednej linii za pomocą:

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

Czy to szybciej niż tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Dlaczego nie rozumienie przez generator?)
Anonimowy

8

Wydaje mi się, że to technicznie odpowiada na pytanie, ale nie rób tego w domu. W tej chwili wszystkie odpowiedzi obejmują utworzenie nowej krotki, ale możesz użyć jej ctypesdo zmodyfikowania krotki w pamięci. Opierając się na różnych szczegółach implementacji języka CPython w systemie 64-bitowym, można to zrobić w następujący sposób:

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
Zawsze dobrze jest wiedzieć coś innego niż zwykłe odpowiedzi. Twoje zdrowie !
Kartheek Palepu

Osoby, które chcą kodować w C, powinny prawdopodobnie po prostu to zrobić. Hakowanie tłumacza w ten sposób pomija ten temat. Jest również zawodny, ponieważ szczegóły implementacji cPythona mogą ulec zmianie w dowolnym momencie bez ostrzeżenia i prawdopodobnie zepsują każdy kod, który polega na niezmienności krotek. Ponadto krotki są najlżejszymi obiektami kolekcji w Pythonie, więc nie ma problemu z utworzeniem nowego. Jeśli bezwzględnie potrzebujesz często modyfikować kolekcję, użyj zamiast tego listy.
Bachsau

1
Zapomniałeś zmniejszyć liczbę odwołań do wartości, którą odrzucasz. To spowoduje wyciek.
wizzwizz4

6

Jak napisał Hunter McMillen w komentarzach, krotki są niezmienne, aby to osiągnąć, musisz utworzyć nową krotkę. Na przykład:

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

EDYCJA: To jeszcze nie działa na krotkach ze zduplikowanymi wpisami !!

Na podstawie pomysłu Pooyi :

Jeśli planujesz robić to często (czego nie powinieneś, ponieważ krotki są niezmienne z jakiegoś powodu), powinieneś zrobić coś takiego:

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

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

Lub w oparciu o pomysł Jona :

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

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

3

Po pierwsze, zadaj sobie pytanie, dlaczego chcesz zmutować swój plik tuple. Istnieje powód, dla którego ciągi znaków i krotka są niezmienne w Ptyhonie , jeśli chcesz zmutować swój tuple, prawdopodobnie powinien to być lista.

Po drugie, jeśli nadal chcesz zmutować swoją krotkę, możesz przekonwertować tupleją na lista, a następnie przekonwertować ją z powrotem i ponownie przypisać nową krotkę do tej samej zmiennej. Jest to świetne rozwiązanie, jeśli zamierzasz zmutować swoją krotkę tylko raz . W przeciwnym razie osobiście uważam, że jest to sprzeczne z intuicją. Ponieważ zasadniczo tworzy nową krotkę i za każdym razem, jeśli chcesz zmutować krotkę, musisz wykonać konwersję. Jeśli przeczytasz kod, zastanowienie się, dlaczego nie po prostu utworzyć pliku list? Ale jest fajny, ponieważ nie wymaga żadnej biblioteki.

Sugeruję użycie mutabletuple(typename, field_names, default=MtNoDefault)z mutabletuple 0.2 . Osobiście uważam, że ten sposób jest bardziej intuicyjny i czytelny. Osobiste odczytanie kodu wiedziałoby, że autor zamierza w przyszłości zmodyfikować tę krotkę. Wadą w porównaniu z listpowyższą metodą konwersji jest to, że wymaga to zaimportowania dodatkowego pliku 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 : Nie próbuj mutować tuple. jeśli to zrobisz i jest to jednorazowa operacja, przekonwertuj ją tuplena listę, zmień ją, zamień listna nową tuplei ponownie przypisz z powrotem do zmiennej przechowującej stare tuple. Jeśli tuplepragniesz i chcesz w jakiś sposób uniknąć listi chcesz mutować więcej niż raz, stwórz mutabletuple.


2

oparty na Pomyśle Jona i drogiej Trufie

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

zmienia wszystkie stare wystąpienia wartości


Byłoby to dość niewygodne (z powodu braku lepszego słowa), gdybyś zmienił wiele wartości, ale z drugiej strony, dlaczego miałbyś chcieć modyfikować krotkę w pierwszej kolejności ...
Trufa,

@Trufa tak, próbuję to napisać: D
Pooya

Nazwa metody modified_tuple_by_index jest niedokładna i może powodować zamieszanie.
msw

1

Nie możesz. Jeśli chcesz to zmienić, musisz użyć listy zamiast krotki.

Zauważ, że możesz zamiast tego utworzyć nową krotkę, która będzie miała nową wartość jako pierwszy element.


0

Odkryłem, że najlepszym sposobem edycji krotek jest odtworzenie krotki przy użyciu poprzedniej wersji jako podstawy.

Oto przykład, którego użyłem do stworzenia jaśniejszej wersji koloru (miałem go już wtedy otwartą):

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

To, co robi, polega na tym, że przechodzi przez krotkę „kolor” i czyta każdy element, robi z nim coś, a na koniec dodaje go do nowej krotki.

Więc chciałbyś coś takiego:

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

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

Ten konkretny nie działa, ale koncepcja jest tym, czego potrzebujesz.

tuple = (0, 1, 2)

tuple = iteracja przez krotkę, zmień każdy element w razie potrzeby

taka jest koncepcja.


0

Spóźniłem się na grę, ale myślę, że najprostszym , najbardziej przyjaznym dla zasobów i najszybszym sposobem (w zależności od sytuacji) jest nadpisanie samej krotki. Ponieważ wyeliminowałoby to potrzebę tworzenia listy i zmiennych i jest archiwizowane w jednej linii.

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

>>> (1, 2, 24)

Ale: Jest to przydatne tylko w przypadku raczej małych krotek, a także ogranicza cię do ustalonej wartości krotki, niemniej jednak tak jest w przypadku krotek przez większość czasu.

Czyli w tym konkretnym przypadku wyglądałoby to tak:

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

działa to tylko wtedy, gdy jest to statyczna krotka o znanej długości. kod nie najprawdopodobniej prędzej czy później zawiedzie, ponieważ jest zbyt specyficzny ...
patroqueeet

tak - @patroqueeet, Dlatego jasno określiłem wady tego podejścia, po Ale: ...-. Proszę, rozważ ponownie swój głos przeciw, dzięki;)
GordanTrevis

1
hej, ponownie rozważałem i kliknąłem przycisk. ale głosowanie jest teraz zablokowane przez SO: /
patroqueeet

0

tldr; „obejściem” jest utworzenie nowego obiektu krotki, a nie faktyczna modyfikacja oryginału

Chociaż jest to bardzo stare pytanie, ktoś powiedział mi o tym szaleństwie mutacji krotek w Pythonie. Co byłem bardzo zaskoczony / zaintrygowany, i robiąc trochę googlowania, wylądowałem tutaj (i inne podobne próbki online)

Przeprowadziłem test, aby udowodnić swoją teorię

Uwaga ==czy równość wartości, podczas gdy równość isreferencyjna (czy jest to ta sama instancja co 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))

Plony:

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

Nie możesz modyfikować elementów w krotce, ale możesz modyfikować właściwości zmiennych obiektów w krotkach (na przykład jeśli te obiekty są listami lub rzeczywistymi obiektami klas)

Na przykład

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

ja to zrobiłem:

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

a żeby zmienić, po prostu zrób

list[0]=6

i możesz zmienić krotkę: D

tutaj jest to dokładnie skopiowane z 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
krotka to lista, a nie krotka. x = (y)nie robi nic poza przypisaniem y do x.
m4tx

2
również używając nazw „krotka” i „lista” jako zmiennych, później będzie trudno porównać typy krotek i list.
Michael Scott Cuthbert

-4

Możesz zmienić wartość krotki za pomocą kopiowania przez odniesienie

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

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

1
Tyle, że są to listy, a nie krotki, które i tak możesz zmienić, podając drugą referencję lub nie.
Bachsau
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.