Python: bir demetteki değeri değiştirme


124

Python'da yeniyim, bu yüzden bu soru biraz basit olabilir. valuesAşağıdakileri içeren bir demetim var :

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

275Bu demetteki ilk değeri (yani, ) değiştirmek istiyorum ama tupleların değişmez olduğunu anlıyorum, bu yüzden values[0] = 200çalışmayacak. Bunu nasıl başarabilirim?


24
demetler değişmezdir , bunu başarmak için yeni bir demet oluşturmanız gerekir.
Hunter McMillen

Yanıtlar:


178

Önce sormalısın, bunu neden yapmak istiyorsun?

Ancak şu yollarla mümkündür:

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

Ancak bir şeyleri değiştirmeniz gerekecekse, muhtemelen bunu bir list


4
Kullanım durumlarından biri, değerlerin nadiren değiştiği ancak birkaç durumda isteyebilecekleri çok sayıda küçük dizi depoluyor olmanızdır. Küçük ama sıfır olmayan uzunlukta bir dizi için, tuple (tek eleman için 60 bayt) ile list (104 bayt) arasındaki bellek tüketimi ve bir fark yaratır. Adlı liste yerel olarak mevcut olmadığından başka bir kullanım durumu, adlandırılmış çiftler içindir.
Michael Scott Cuthbert

81
"Bunu neden yapmak istiyorsun" yanıtları beni StackOverflow'da deli ediyor. Orijinal göndericinin bunu yapmak istediğini varsaymayın. Aslında, orijinal posterin tüm bunları sıfırdan yarattığını varsaymamak daha iyidir. Daha sık olarak, kontrol edemediğimiz bir format veya türdeki başka bir modülden veya veri kaynağından çıktıyla ilgileniriz.
rtphokie

9
@rtphokie'nin değişmez bir kapsayıcıyı mutasyona uğratmak istemesi (bu nedenle "neden" çok geçerli bir sorudur), bazıları bir demet olabilen farklı biçimleri yorumlamaktan farklıdır. Havalandırdığınız için teşekkürler :)
Jon Clements

7
Bir listeyi yeniden oluşturmak ve geçici bir değişken kullanmak gereksiz ve hafıza yetersiz görünüyor. Aynı adı taşıyan bir gruba açabilirsiniz ve güncellenmesi gereken her şeyi güncellemeyi paketten çıkarırken yapabilirsiniz.
Brian Spiering

5
@JonClements Bunu yapmanın kötü bir uygulama olduğu konusunda çok değerli bir noktaya değindiniz. Cevabınızda bu olmalı . Ancak retorik sorular genellikle gereksiz yere aşağılayıcı olarak yorumlanır. Bu tür bilgiler şu şekilde daha iyi yapılandırılmıştır: "Bu kötü bir uygulamadır çünkü ..." veya hatta "Buna gerçekten ihtiyacınız varsa dikkatlice düşünün; genellikle tasarımda bir hata anlamına gelir çünkü ..."
Philip Couling

74

Sorununuza bağlı olarak dilimleme gerçekten düzgün bir çözüm olabilir:

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

Bu, birden fazla öğe eklemenize veya birkaç öğeyi değiştirmenize olanak tanır (özellikle "komşularsa". Yukarıdaki durumda, bir listeye atama muhtemelen daha uygun ve okunabilirdir (dilimleme notasyonu çok daha kısa olsa bile).


24

Trufa'nın daha önce gösterdiği gibi, temelde belirli bir dizindeki bir demet elemanını değiştirmenin iki yolu vardır. Tuple'ı bir listeye dönüştürün, öğeyi değiştirin ve geri dönüştürün ya da birleştirme ile yeni bir demet oluşturun.

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

Peki, hangi yöntem daha iyi, yani daha hızlı?

Kısa tuple'lar için (Python 3.3'te), birleştirme aslında daha hızlıdır!

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

Yine de daha uzun demetlere bakarsak, liste dönüştürme gidilecek yoldur:

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

Çok uzun tuplelar için liste dönüştürme önemli ölçüde daha iyidir!

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

Ayrıca, birleştirme yönteminin performansı, öğeyi değiştirdiğimiz dizine bağlıdır. Liste yöntemi için dizin konu dışıdır.

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

Yani: Demonuz kısaysa, dilimleyin ve birleştirin. Uzunsa, liste dönüştürme işlemini yapın!


1
@ErikAronesty her zaman değil. Bir kez yararlı durum, değiştiremeyeceğiniz ve yöntemlerinin yalnızca ilk öğeyi değiştirmek istediğiniz bir demet döndürdüğü bir sınıfı genişletmektir. return (val,) + res [1:] res2 = list (res) 'den daha nettir; res2 [0] = val; dönüş tuple (res2)
yucer

9

Tek astar ile mümkündür:

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

1
Bununla sadece üçüncü unsuru nasıl değiştirirdiniz values?
sdbbs

2
Bir gruptaki herhangi bir öğeyi nasıl değiştirebileceğiniz aşağıda açıklanmıştır -i = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering

8

Bu üstün olduğu için değil, ancak merak eden biri varsa, tek bir satırda şu şekilde yapılabilir:

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

Bu daha hızlı mı tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Neden oluşturucu anlayışı değil?)
Anonim

8

Bunun teknik olarak soruyu yanıtladığına inanıyorum, ancak bunu evde yapma. Şu anda, tüm yanıtlar yeni bir demet oluşturmayı içerir, ancak ctypesbellek içindeki bir demeti değiştirmek için kullanabilirsiniz . 64 bitlik bir sistemde CPython'un çeşitli uygulama ayrıntılarına güvenerek, bunu yapmanın bir yolu aşağıdaki gibidir:

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
Her zamanki cevaplardan farklı bir şey bilmek her zaman iyidir. Şerefe!
Kartheek Palepu

C'de kodlamak isteyenler muhtemelen tam olarak bunu yapmalıdır. Tercümanı bu şekilde hacklemek buradaki konuyu kaçırır. Aynı zamanda güvenilmezdir, çünkü cPython uygulama detayları herhangi bir uyarı olmadan herhangi bir zamanda değişebilir ve büyük olasılıkla tupleların değişmez olmasına dayanan herhangi bir kodu kırabilir. Ayrıca, tuplelar python'daki en hafif koleksiyon nesneleridir, bu nedenle yeni bir tane oluşturmada sorun yoktur. Bir koleksiyonu sık sık değiştirmeniz gerekiyorsa, bunun yerine bir liste kullanın.
Bachsau

1
Referans sayısını, atmakta olduğunuz değere indirmeyi unuttunuz. Bu sızıntıya neden olur.
wizzwizz4

6

Hunter McMillen'ın yorumlarda yazdığı gibi, demetler değişmez, bunu başarmak için yeni bir demet oluşturmanız gerekir. Örneğin:

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

DÜZENLEME: Bu henüz yinelenen girişlere sahip tuplelar üzerinde çalışmıyor !!

Pooya'nın fikrine dayanarak :

Bunu sık sık yapmayı planlıyorsanız (tuples bir nedenden ötürü değişmez olduğu için yapmamalısınız) şöyle bir şey yapmalısınız:

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

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

Veya Jon'un fikrine dayanarak :

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

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

3

Frist, neden kendini mutasyona uğratmak istediğini kendine sor tuple. Ptyhon'da dizelerin ve tuple'ın değişmez olmasının bir nedeni vardır , eğer kendi dizinizi mutasyona uğratmak istiyorsanız tuple, muhtemelen listonun yerine bir olmalıdır .

Eğer hala tuple mutasyona istiyorsanız İkincisi, o zaman dönüştürebilirsiniz tuplea listgeri dönüştürmek, sonra aynı değişkene yeni başlığın yeniden atama. Bu, dizinizi yalnızca bir kez mutasyona uğratacaksanız harika . Aksi takdirde, şahsen bunun mantıksız olduğunu düşünüyorum. Çünkü aslında yeni bir demet oluşturuyor ve her seferinde tuple'ı mutasyona uğratmak isterseniz, dönüşümü gerçekleştirmeniz gerekir. Ayrıca kodu okursanız, neden sadece bir tane oluşturmadığınızı düşünmek kafa karıştırıcı olacaktır.list ? Ama güzel çünkü herhangi bir kitaplık gerektirmiyor.

Ben kullanmanızı öneririz mutabletuple(typename, field_names, default=MtNoDefault)gelen mutabletuple 0.2 . Ben şahsen bu yolun daha sezgisel ve okunaklı olduğunu düşünüyorum . Kodu okuyan kişi, yazarın gelecekte bu demeti değiştirmeyi planladığını bilirdi. Olumsuz tarafı, listyukarıdaki dönüştürme yöntemiyle karşılaştırıldığında , bunun ek py dosyasını içe aktarmanızı gerektirmesidir.

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 : Mutasyona uğramaya çalışmayın tuple. Yapmanız ve bir kerelik operasyon dönüştürmek ise tuplelisteye, onu mutasyona çevirmek listyeni içine tupleeski tutan değişkene ve yeniden atamakta arka tuple. Arzu ediyorsanız tupleve bir şekilde kaçınmak listve birden fazla mutasyona uğramak istiyorsanız, o zaman yaratın mutabletuple.


2

Jon 's Idea ve sevgili Trufa'ya dayanarak

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

tüm eski değer oluşumlarınızı değiştirir


Birden fazla değeri değiştirirseniz bu oldukça rahatsız edici olur (daha iyi bir kelimenin olmaması için) ama sonra tekrar, neden ilk başta bir demeti değiştirmek isteyesiniz ...
Trufa

@Trufa evet, yazmaya çalışıyorum: D
Pooya

Modifiye_tuple_by_index yöntem adı yanlıştır ve karışıklığa neden olmaya bağlıdır.
msw

1

Yapamazsın. Değiştirmek isterseniz, tuple yerine bir liste kullanmanız gerekir.

Bunun yerine, ilk öğesi olarak yeni değeri olan yeni bir demet oluşturabileceğinizi unutmayın.


0

Tuple'ları düzenlemenin en iyi yolunu, önceki sürümü temel olarak kullanarak tuple'ı yeniden oluşturmak olduğunu buldum.

İşte bir rengin daha açık versiyonunu yapmak için kullandığım bir örnek (o sırada onu zaten açmıştım):

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

Yaptığı şey, demet 'renginden' geçip her bir öğeyi okur, ona bir şeyler yapar ve sonunda onu yeni demete ekler.

Öyleyse istediğiniz şey şuna benzer:

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

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

Bu belirli olan işe yaramıyor, ancak konsept ihtiyacınız olan şey.

tuple = (0, 1, 2)

tuple = tuple boyunca yineleyin, her öğeyi gerektiği gibi değiştirin

kavram bu.


0

Oyuna geç kaldım ama bence en basit , kaynak dostu ve en hızlı yol (duruma bağlı olarak) , başlığın üzerine yazmak. Bu, liste ve değişken oluşturma ihtiyacını ortadan kaldıracağından ve tek satırda arşivlendiğinden.

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

>>> (1, 2, 24)

Ancak: Bu yalnızca oldukça küçük tuple'lar için kullanışlıdır ve ayrıca sizi sabit bir tuple değeriyle sınırlar, yine de çoğu zaman bu durum tupleler için geçerlidir.

Yani bu özel durumda şöyle görünecektir:

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

bu yalnızca uzunluğu bilinen statik bir demet ise işe yarar. kod değil büyük olasılıkla er ya da geç başarısız olacak çünkü çok özel ...
patroqueeet

evet - @patroqueeet, Bu nedenle But: ...- sonrasında bu yaklaşımın olumsuz yanlarını açıkça belirttim. Lütfen olumsuz oyunuzu tekrar değerlendirin, teşekkürler;)
GordanTrevis

1
hey, yeniden düşündüm ve düğmeye tıkladım. ancak oylama artık SO tarafından kilitlendi: /
patroqueeet

0

TLDR; "geçici çözüm" aslında orijinali değiştirmeden yeni bir demet nesnesi oluşturmaktır

Bu çok eski bir soru olsa da, birisi bana bu Python mutasyonu tuples deliliğinden bahsetti. Çok şaşırdığım / ilgimi çektiğim ve biraz googling yaptığım için buraya geldim (ve çevrimiçi diğer benzer örnekler)

Teorimi kanıtlamak için bazı testler yaptım

Not ==eşitliğe değer verirken isreferans eşitliği yapar (obj, obj b ile aynı örnektir)

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

Verim:

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

Tuple'daki öğeleri değiştiremezsiniz, ancak tuplelardaki değişken nesnelerin özelliklerini değiştirebilirsiniz (örneğin, bu nesneler listeler veya gerçek sınıf nesneleri ise)

Örneğin

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

bunu ben yaptım:

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

ve değiştirmek için sadece yap

list[0]=6

ve bir demeti değiştirebilirsiniz: D

işte tam olarak IDLE'den kopyalandı

>>> 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 bir listedir, bir demet değil. x = (y)hiçbir şey yapmaz ancak y'yi x'e atar.
m4tx

2
ayrıca değişken olarak "tuple" ve "list" adını kullanarak, daha sonra tuple ve liste türlerini karşılaştırmayı zorlaştıracaksınız.
Michael Scott Cuthbert

-4

Referans ile kopyalamayı kullanarak tuple değerini değiştirebilirsiniz.

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

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

1
Sadece bunlar listelerdir, tuple değil, yine de değiştirebileceğiniz, ikinci bir referans verilir veya verilmez.
Bachsau
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.