Python'da artış ve azalma operatörlerinin davranışı


797

Bir değişkene (gibi ++count) bir ön artış / azalma operatörünün uygulanabileceğini fark ettim . Derler, ama aslında değişkenin değerini değiştirmez!

Python'daki arttırma / azaltma operatörlerinin (++ / -) davranışı nedir?

Python neden C / C ++ 'da görülen bu operatörlerin davranışlarından sapıyor?


19
Python C veya C ++ değildir. Farklı tasarım kararları dili oluşturmaya başladı. Özellikle Python, rastgele bir ifadede kullanılabilecek atama işleçlerini kasten tanımlamaz; daha ziyade, atama ifadeleri ve artırılmış atama ifadeleri vardır. Aşağıdaki referansa bakın.
Ned Deily

8
Python'un ++ve --operatörlerin sizi ne düşündürdü ?
u0b34a0f6ae

29
Kaizer: C / C ++ 'dan geliyor, ++ sayımı yazıyorum ve Python'da derliyor. Bu yüzden dilin operatörleri olduğunu düşündüm.
Ashwin Nanjappa

3
@Fox Kanıtta olmayan bir düzeyde planlama ve organizasyon olduğunu varsayıyorsunuz
Temel

4
@mehaase ++ ve - "işaretçi aritmetiği için sözdizimsel şeker olarak" mevcut değildir, çünkü birçok işlemcinin kendi yerel talimatlarının bir parçası olarak otomatik artış ve azalan bellek erişim mekanizmaları (genel olarak işaretçi indeksleme, yığın indeksleme) vardır Ayarlamak. Örneğin, 6809 montajcısında: sta x++... sonuç veren atomik talimat, aakümülatörü xişaret ettiği yerde saklar , ardından xakümülatörün büyüklüğüne göre artar . Bu, işaretçi aritmetiğinden daha hızlı olduğu için yapılır, çünkü çok yaygındır ve anlaşılması kolaydır. Hem öncesi hem de sonrası.
fyngyrz

Yanıtlar:


1059

++bir operatör değil. İki +operatör. +Operatörüdür kimlik şey yapmaz operatörü. (Açıklama: +ve -tekli işleçler yalnızca sayılar üzerinde çalışır, ancak varsayımsal bir işleçin ++dizeler üzerinde çalışmasını beklemeyeceğinizi varsayıyorum.)

++count

Olarak ayrıştırır

+(+count)

Hangi anlama gelir

count

Yapmak +=istediğiniz şeyi yapmak için biraz daha uzun bir operatör kullanmanız gerekir :

count += 1

Şüpheliyim ++ve --operatörler tutarlılık ve sadelik için dışarıda bırakıldılar. Guido van Rossum'un karar için verdiği kesin argümanı bilmiyorum, ancak birkaç argüman hayal edebiliyorum:

  • Daha basit ayrıştırma. Teknik olarak, ayrıştırma ++countolması gerektiği gibi belirsiz +, +, count(iki tekli +aynı kolaylıkla olması gerektiği şekilde operatörler) ++, count(bir tekli ++operatör). Önemli bir sözdizimsel belirsizlik değil, ama var.
  • Daha basit bir dil. ++eşanlamından başka bir şey değildir += 1. C derleyicileri aptal olduğu ve çoğu bilgisayarın sahip a += 1olduğu inctalimatı nasıl optimize edeceğini bilmediği için bir steno icat edildi . Derleyicileri ve bayt kodu yorumlanmış dilleri optimize etmenin bu gününde, programcıların kodlarını optimize etmelerine izin vermek için bir dile operatör eklemek genellikle, özellikle tutarlı ve okunabilir şekilde tasarlanmış Python gibi bir dilde kaşlarını çatar.
  • Kafa karıştırıcı yan etkiler. ++Operatörlerle birlikte dillerdeki yaygın bir acemi hatası, arttırma öncesi ve arttırma / azaltma operatörleri arasındaki farkları (hem öncelik hem de dönüş değerinde) karıştırmaktır ve Python dil "gotcha" -s'yi ortadan kaldırmayı sever. Öncelik sorunları arasında C öncesi / sonrası-artım pisliği inanılmaz kolay oldukça kıllı ve.

13
"+ İşleci, hiçbir şey yapmayan" kimlik "işlecidir." Sadece sayısal tipler için; diğer türler için varsayılan olarak bir hatadır.
newacct

45
Ayrıca, Python'da + = ve arkadaşlarının ifadelerde kullanılabilecek işleçler olmadığını unutmayın. Bunun yerine, Python'da "artırılmış atama ifadesinin" bir parçası olarak tanımlanırlar. Bu, Python'daki, C'de yapılabileceklerin aksine, rasgele ifadeler içinde bir operatör olarak atamaya ("=") izin vermeme dil tasarımı kararıyla tutarlıdır. Bkz. Docs.python.org/reference/…
Ned Deily

15
Tekli +operatörün bir kullanımı vardır. Ondalık nesneler için, geçerli hassasiyete yuvarlar.
u0b34a0f6ae

21
Ayrıştırıcı basitleştirmesi üzerine bahse giriyorum. PEP 3099'da "Python 3000'de Değişmeyecek Şeyler" adlı bir öğeye dikkat edin: "Ayrıştırıcı LL'den (1) daha karmaşık olmayacak. Basit karmaşık olmaktan daha iyi. Bu fikir ayrıştırıcıya uzanıyor. Python'un dilbilgisini kısıtlamak LL (1) ayrıştırıcısı bir lanet değil, bir lütuftur. Bizi denize düşmemizi ve isimlendirilmeyecek Perl gibi diğer dinamik diller gibi korkak gramer kuralları ile bitirmemizi engelleyen kelepçeler koyuyor. " Ben belirsizliği nasıl görmüyorum + +ve ++kırılma LL (1) olmadan.
Mike DeSimone

7
Bunun ++bir eşanlamlıdan başka bir şey olmadığını söylemek doğru değil += 1. ++ 'ın artım öncesi ve artım sonrası varyantları vardır, bu yüzden aynı şey değildir. Yine de diğer noktalarınıza katılıyorum.
PhilHibbs

384

Artırmak veya azaltmak istediğinizde, bunu genellikle bir tamsayı üzerinde yapmak istersiniz. Şöyle ki:

b++

Ancak Python'da tamsayı değişmezdir . Onları değiştiremezsiniz. Bunun nedeni, tamsayı nesnelerinin çeşitli adlar altında kullanılabilmesidir. Bunu dene:

>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True

Yukarıdaki a ve b aslında aynı nesnedir. A değerini arttıracak olursanız, b değerini de artırırsınız. İstediğin bu değil. Bu yüzden yeniden atamanız gerekiyor. Bunun gibi:

b = b + 1

Veya daha basit:

b += 1

Hangi değer atar biçin b+1. Bu bir arttırma operatörü değildir, çünkü artmaz b, yeniden atar.

Kısacası: Python burada farklı davranıyor, çünkü C değil ve makine kodu etrafında düşük seviyeli bir sarıcı değil, artışların mantıklı olmadığı ve aynı zamanda C'deki kadar gerekli olmadığı yüksek seviye dinamik bir dil Örneğin, her döngüde kullandığınızda bunları kullanabilirsiniz.


75
Bu örnek yanlıştır (ve muhtemelen kimlikle değişmezliği karıştırıyorsunuz) - 255'e kadar sayılar için aynı nesneleri kullanan bir vm optimizasyonu nedeniyle aynı kimliğe sahipler (veya bunun gibi bir şey). Örn (daha büyük sayılar): >>> a = 1231231231231 >>> b = 1231231231231 >>> id (a), id (b) (32171144, 32171168)
ionelmc

56
Değişmezlik iddiası sahte. Kavramsal olarak, değişkenei++ atamak anlamına gelir . aracı atamak için , değişiklik nesne tarafından işaret . Yani, değerini artırmak anlamına gelmez ! i + 1 ii = 5; i++6iinti5
Mekanik salyangoz

3
@Mekanik salyangoz: Bu durumda operatörleri arttırmazdı. Ve sonra + = operatörü daha net, daha açık, daha esnek ve aynı şeyi yine de yapıyor.
Lennart Regebro

7
@LennartRegebro: C ++ ve Java'da i++yalnızca değerlerde çalışır. İşaret ettiği nesneyi arttırmak iisteseydi, bu kısıtlama gerekli olmayacaktır.
Mekanik salyangoz

4
Bu cevabı oldukça şaşırtıcı buluyorum. Neden ++ ile + = 1 için steno dışında bir şey ifade edeceğini düşünüyorsunuz? C'de tam olarak bu anlama gelir (dönüş değerinin kullanılmadığı varsayılarak). Havadan başka bir anlam çıkardınız.
Don Hatch

52

Diğerlerinin cevapları, bir kişinin +genellikle ne yaptığını gösterdiği kadar doğru olsa da (yani, numarayı olduğu gibi bırakın), ne olduğunu açıklamadıkları sürece eksiktirler.

Tam olarak, +xolarak değerlendirilen x.__pos__()ve ++xiçin x.__pos__().__pos__().

ÇOK garip bir sınıf yapısı hayal edebiliyordum (Çocuklar, bunu evde yapmayın!):

class ValueKeeper(object):
    def __init__(self, value): self.value = value
    def __str__(self): return str(self.value)

class A(ValueKeeper):
    def __pos__(self):
        print 'called A.__pos__'
        return B(self.value - 3)

class B(ValueKeeper):
    def __pos__(self):
        print 'called B.__pos__'
        return A(self.value + 19)

x = A(430)
print x, type(x)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)

13

Python bu operatörlere sahip değildir, ancak gerçekten ihtiyacınız varsa aynı işlevselliğe sahip bir işlev yazabilirsiniz.

def PreIncrement(name, local={}):
    #Equivalent to ++name
    if name in local:
        local[name]+=1
        return local[name]
    globals()[name]+=1
    return globals()[name]

def PostIncrement(name, local={}):
    #Equivalent to name++
    if name in local:
        local[name]+=1
        return local[name]-1
    globals()[name]+=1
    return globals()[name]-1

Kullanımı:

x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2

Bir işlevin içinde, yerel değişkeni değiştirmek istiyorsanız locals () öğesini ikinci bir argüman olarak eklemeniz gerekir, aksi takdirde global değişmeye çalışır.

x = 1
def test():
    x = 10
    y = PreIncrement('x') #y will be 2, local x will be still 10 and global x will be changed to 2
    z = PreIncrement('x', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()

Ayrıca bu işlevlerle şunları yapabilirsiniz:

x = 1
print(PreIncrement('x'))   #print(x+=1) is illegal!

Ama bence aşağıdaki yaklaşım çok daha açık:

x = 1
x+=1
print(x)

Azaltma operatörleri:

def PreDecrement(name, local={}):
    #Equivalent to --name
    if name in local:
        local[name]-=1
        return local[name]
    globals()[name]-=1
    return globals()[name]

def PostDecrement(name, local={}):
    #Equivalent to name--
    if name in local:
        local[name]-=1
        return local[name]+1
    globals()[name]-=1
    return globals()[name]+1

Javascript'i python'a çeviren modülümde bu işlevleri kullandım.


Not: harika olsa da, yerel ayarlarınız sınıf işlevi yığın çerçevesinde varsa bu yardımcı yöntemler çalışmaz. ie - onları bir sınıf yöntemi def içinden çağırmak işe yaramaz - 'locals ()' dict bir anlık görüntüdür ve yığın çerçevesini güncellemez.
Adam

11

Python'da, Common Lisp, Scheme veya Ruby gibi dillerin aksine, ifadeler ve ifadeler arasındaki ayrım katı bir şekilde uygulanır.

Vikipedi

Bu tür operatörleri tanıtarak ifade / ifade bölünmesini kıracaksınız.

Aynı nedenden dolayı yazamazsın

if x = 0:
  y = 1

bu ayrımın korunmadığı diğer dillerde de yapabilirsiniz.


İlginç bir şekilde, bu kısıtlama yeni Python 3.8 sürümünde Ödev ifadeleri için yeni sözdizimi ile kaldırılacaktır (PEP-572 python.org/dev/peps/pep-0572 ). if (n := len(a)) > 10: y = n + 1Örneğin yazabileceğiz . Bu amaç için yeni bir operatörün getirilmesi nedeniyle ayrımın net olduğuna dikkat edin ( :=)
Zertrin

8

TL; DR

Python'un tekli arttırma / azaltma operatörleri ( --/ ++) yoktur. Bunun yerine, bir değeri artırmak için

a += 1

Daha fazla detay ve gotchas

Ama burada dikkatli ol. C'den geliyorsanız, bu bile python'da farklıdır. Python yerine kullandığı piton C yapsa anlamında "değişkenleri", yok adlar ve nesneler ve python intler değişken değildir.

öyleyse yaptığını söyleyelim

a = 1

Python'da bunun anlamı şudur: intdeğere sahip türde bir nesne oluşturun 1ve adı aona bağlayın. Nesne bir örneği olan intsahip olan bir değer 1, ve adı a bunu ifade eder. Adı ave atıfta bulunduğu nesne farklıdır.

Şimdi diyelim ki

a += 1

Yana intler iletmenin şöyle, burada ne olur ise:

  1. aatıfta bulunulan nesneyi arayın ( intkimliğine sahip 0x559239eeb380)
  2. nesnesinin değerini aramak 0x559239eeb380(o 1)
  3. bu değere 1 ekleyin (1 + 1 = 2)
  4. değeri olan yeni bir int nesne oluştur 2(nesne kimliği var 0x559239eeb3a0)
  5. adı abu yeni nesneye yeniden bağla
  6. Şimdi anesneye atıfta bulunur 0x559239eeb3a0ve orijinal nesneye ( 0x559239eeb380) artık adla başvurulmaz a. Orijinal nesneye atıfta bulunan başka adlar yoksa, daha sonra çöp toplanır.

Kendiniz deneyin:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))

6

Evet, ++ ve - işlevselliğini de kaçırdım. Birkaç milyon c kod satırı, eski kafamda bu tür bir düşünceyi şekillendirdi ve onunla savaşmak yerine ... İşte uyguladığım bir sınıf:

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

İşte bu:

class counter(object):
    def __init__(self,v=0):
        self.set(v)

    def preinc(self):
        self.v += 1
        return self.v
    def predec(self):
        self.v -= 1
        return self.v

    def postinc(self):
        self.v += 1
        return self.v - 1
    def postdec(self):
        self.v -= 1
        return self.v + 1

    def __add__(self,addend):
        return self.v + addend
    def __sub__(self,subtrahend):
        return self.v - subtrahend
    def __mul__(self,multiplier):
        return self.v * multiplier
    def __div__(self,divisor):
        return self.v / divisor

    def __getitem__(self):
        return self.v

    def __str__(self):
        return str(self.v)

    def set(self,v):
        if type(v) != int:
            v = 0
        self.v = v

Şöyle kullanabilirsiniz:

c = counter()                          # defaults to zero
for listItem in myList:                # imaginary task
     doSomething(c.postinc(),listItem) # passes c, but becomes c+1

... zaten c var, bunu yapabilirsin ...

c.set(11)
while c.predec() > 0:
    print c

....ya da sadece...

d = counter(11)
while d.predec() > 0:
    print d

... ve tamsayıya (yeniden) atamak için ...

c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323

... bu c'yi sayaç sayacı olarak korurken:

c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323

DÜZENLE:

Ve sonra bu biraz beklenmedik (ve tamamen istenmeyen) davranış var ,

c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s

... çünkü bu tuple içinde getitem () kullanılan şey değildir, bunun yerine nesneye bir başvuru biçimlendirme işlevine geçirilir. İç çekmek. Yani:

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s

... ya da daha ayrıntılı ve açık bir şekilde gerçekte ne olmak istediğimizi, bununla birlikte ayrıntı biçimiyle gerçek formda ters yönde belirtilmesine rağmen ( c.vbunun yerine kullanın) ...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s

2

Python'da C gibi dillerde post / ön artış / azalma operatörleri yoktur.

Biz görebilirsiniz ++ya --birden işaretler çarpılır alma gibi biz matematikte yaptığımız gibi (-1) * (-1) = (1).

Örneğin

---count

Olarak ayrıştırır

-(-(-count)))

Hangi anlama gelir

-(+count)

Çarpımı Çünkü -ile işareti -işareti olduğunu+

Ve sonunda,

-count

1
Bu diğer cevapların yapmadığını söylüyor?
Daniel B.

@DanielB. Diğer cevaplar dahili olarak neler olduğunu söylemedi. Ve ikisi de yazacağınız zaman ne olacağını söylemediler -----count.
Anuj

İlk, kabul edilen cevap. ...
Daniel

1
Çarpma işleminin yapıldığına dair herhangi bir söz yok, bu yüzden bir fikir ve nokta cevabının diğer kullanıcılar için yararlı olacağını düşündüm. Bundan anladıysan suç olmaz. Öğrenme, öğrendiğiniz kaynaktan daha önemlidir.
Anuj

0

Python 3.8+ ile şunları yapabilirsiniz:

(a:=a+1) #same as a++

Bununla çok fazla düşünebilirsiniz.

>>> a = 0
>>> while (a:=a+1) < 5:
    print(a)


1
2
3
4

Veya daha karmaşık sözdizimiyle bir şeyler yazmak istiyorsanız (hedef optimizasyon değildir):

>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
    print(a)


1
2
3
4

Bir doz hatasız yoksa 0 döndürür ve sonra 1 olarak ayarlanır.

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.