Python'da iki değişkeni değiştirmek için standart bir yöntem var mı?


345

Python'da, bu sözdizimini kullanarak değiştirilen iki değişken değer gördüm:

left, right = right, left

Bu, iki değişken değerini değiştirmenin standart yolu olarak mı kabul edilir, yoksa iki değişkenin konvansiyon tarafından en sık değiştirildiği başka yollar da var mı?


1
@eyquem: değerlendirme sırasının bir grup / liste ataması dili tarafından tanımlanıp tanımlanmadığı anlaşılır . Python yapar, çoğu eski dil bilmez.
smci

Hrmm C ++, Python için neden böyle bir şeye sahip olamıyoruz?
Nils

Yanıtlar:


389

Python ifadeleri soldan sağa doğru değerlendirir. Bir ödevi değerlendirirken, sağ tarafın sol taraftan önce değerlendirildiğine dikkat edin.

http://docs.python.org/3/reference/expressions.html#evaluation-order

Bu, ifade için aşağıdakiler anlamına gelir a,b = b,a :

  • sağ taraf b,a değerlendirilir, yani bellekte iki elemandan oluşan bir demet oluşturulur. İki öğe, tanımlayıcılar tarafından belirlenen bve aprogramın yürütülmesi sırasında yönergelerin kodlanmasından önce var olan nesnelerdir .
  • bu grubun yaratılmasından hemen sonra, bu grup nesnesinin hiçbir ataması yapılmadı, ancak önemli değil, Python nerede olduğunu biliyor
  • daha sonra, sol taraf değerlendirilir, yani tuple sol tarafa atanır
  • sol taraf iki tanımlayıcıdan oluştuğu için, ilk tanımlayıcının agrubun ilk öğesine atanması için (bu, adı olduğu için takastan önce b olan nesnedir b)
    ve ikinci tanımlayıcı b(eskiden hedefi olan başlığın ikinci eleman atanan bir , kimliğini çünkü takas önce a)

Bu mekanizma, tanımlayıcılara atanan nesneleri etkili bir şekilde değiştirmiştir aveb

Bu nedenle, sorunuzu cevaplamak için: EVET, iki tanımlayıcıyı iki nesne üzerinde değiştirmenin standart yoludur.
Bu arada, nesneler değişken değil, nesnelerdir.


1
Anladığım kadarıyla, iki değişkeni bu şekilde değiştirmek fazladan bellek kullanmaz, sadece 2 değişken için bellek kullanır, doğru mu?
Catbuilts

1
@Catbuilts Tuplein oluşturulması biraz fazladan bellek kaplar (muhtemelen takasın 3-değişkenli versiyonundan daha fazla zaman alır), ancak değiştirilen tek şey bellek adresleri olduğundan, mutlak olarak fazladan bellek olmayacaktır. duygusu (belki 24 ekstra bayt).
Brilliand

@Brilliand: Thks. Bu konuyla ilgili herhangi bir belgeniz var mı? Oldukça ilginç ve daha fazla okumak istiyorum. Teşekkürler.
Catbuilts

1
@Catbuilts Emin değilim, ancak C ++ işaretçilerin nasıl çalıştığını okumak yardımcı olabilir. Python işleri sizin için en iyi şekilde otomatik olarak yapmaya çalışırken, C ++ aslında tüm olası doğru ve yanlış yollarla işleri yapmanız için tüm seçenekleri sunar, bu yüzden Python'un aldığı yaklaşımın dezavantajlarını öğrenmek için iyi bir başlangıç ​​noktasıdır. . Ayrıca, "64 bit" işletim sistemine sahip olmanın bir bellek adresi depolamanın 64 bit bellek kapladığı anlamına geldiğini unutmayın - bu benim "24 bayt" numaramı nereden aldığımın bir parçasıdır.
Brilliand

Harika bir açıklama. Bu nedenle, bu yöntemi istediğiniz sayıda "değişkeni" yeniden düzenlemek için de kullanabilirsiniz a, b, c = c, a, b.
alexlomba87


38

Değişkenleri değiştirmenin üç yolunu biliyorum ama a, b = b, aen basiti bu. Var

XOR (tamsayılar için)

x = x ^ y
y = y ^ x
x = x ^ y

Veya kısaca,

x ^= y
y ^= x
x ^= y

Geçici değişken

w = x
x = y
y = w
del w

Grup değişimi

x, y = y, x

1
En basit ve gizlenmeyen tek kişidir.
Jorge Leitao

17
XOR "değişkenleri" değiştirmez. Tamsayı değişkenlerini değiştirir. (Ya da XOR operatörünü düzgün bir şekilde uygulayan diğer birkaç tür) Ayrıca, Rogalski'nin cevabına göre, Tuple Swap, yorumlayıcıda optimize edildiğinden, ona karşı gerçekten hiçbir şey yoktur. Kısa, net ve hızlı.
Rawler

XOR sorunu + - operatör kullanımı ile önlenebilir, ancak yine de en iyi olduğunu hissediyorum a, b = b, a codex = x + yy = xy x = xycode
ashish

22

Bazı beklenmedik hatalara neden olacağı için takas için standart bir yol olduğunu söyleyemem.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]önce değiştirilir ve daha sonra ikinci değişkeni etkiler nums[nums[i] - 1].


2
Hemen hemen tüm programlama dillerinde sorun var, yani b'ye bağlıysa (a, b) takas kullanmak güvenli değil, tersi de doğrudur. Örneğin, değiştirilebilir (a, b) genişletilebilir olabilir: var c=a, a=b, b=c. Ve sonra, son atama ab'nin adresini değerlendirmek için yeni değerini kullanacaktır .
Kai Petzke

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Sorunu çözerdim.
Bill Cheng

2
@JacksonKelley Sağ tarafın değerlendirmesi güvenlidir. In nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Sorun, python sol taraf atamasını yaptığı, nums[i]değiştiği, nums[nums[i] - 1]beklenmedik değişime neden olmasıdır . U ilk başta u hayal edebilirsiniz nums[1],nums[2] = nums[2],nums[1], ama nums[1] = nums[2]çalıştırdıktan sonra , artık var nums[2] = nums[1], bunun yerine u var nums[888] = nums[1].
guo

5

Çok boyutlu diziler için çalışmaz, çünkü referanslar burada kullanılır.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Ayrıca bkz . Numpy dizilerinin dilimlerini değiştirme


Bu gerçekten de numpy kütüphanesinin özel bir özelliğidir.
Kai Petzke

-1

Tarafından açıklanan sorunları çözmek için Eyquem üstesinden gelmek için , copybir işlev aracılığıyla değerlerin (ters) kopyalarını içeren bir demet döndürmek için modülü kullanabilirsiniz :

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

A ile aynı işlev lambda:

swapper = lambda x, y: (copy(y), copy(x))

Ardından, bunları aşağıdaki gibi istenen adlara atayın:

x, y = swapper(y, x)

NOT: isterseniz, almak deepcopyyerine kullanabilirsiniz copy.


kopya ile çözmeye çalıştığınız sorun nedir?
Hanan Shteingart

Eyem'in yazısında tartışılanlar .
LogicalBranch

1
ancak herhangi bir sorun belirtmez, aslında "EVET, iki tanımlayıcıyı değiştirmenin standart yolu" diyor
Hanan Shteingart

-2

Birleştirebilirsiniz tanımlama grubu ve XOR takaslarını: x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

veya

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Lambda kullanma :

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Çıktı:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
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.