Perilaku operator kenaikan dan penurunan dalam Python

798

Saya perhatikan bahwa operator pra-kenaikan / penurunan dapat diterapkan pada variabel (seperti ++count). Mengkompilasi, tetapi sebenarnya tidak mengubah nilai variabel!

Apa perilaku operator pra-kenaikan / penurunan (++ / -) di Python?

Mengapa Python menyimpang dari perilaku operator ini terlihat di C / C ++?

Ashwin Nanjappa
sumber
19
Python bukan C atau C ++. Keputusan desain yang berbeda digunakan untuk membuat bahasa. Secara khusus, Python sengaja tidak mendefinisikan operator penugasan yang dapat digunakan dalam ekspresi sewenang-wenang; alih-alih, ada pernyataan tugas dan pernyataan tugas tambahan. Lihat referensi di bawah ini.
Ned Deily
8
Apa yang membuat Anda berpikir tentang python ++dan --operator?
u0b34a0f6ae
29
Kaizer: Berasal dari C / C ++, saya menulis ++ count dan mengkompilasinya dengan Python. Jadi, saya pikir bahasanya memiliki operator.
Ashwin Nanjappa
3
@Fox Anda mengasumsikan tingkat perencanaan dan organisasi tidak dalam bukti
Dasar
4
@mease + + dan - tidak ada dalam c "sebagai gula sintaksis untuk aritmatika pointer", mereka ada karena banyak prosesor memiliki peningkatan otomatis dan penurunan mekanisme akses memori (dalam pengindeksan pointer secara umum, pengindeksan tumpukan) sebagai bagian dari instruksi asli mereka set. Misalnya, dalam assembler 6809: sta x++... instruksi atom yang dihasilkan menyimpan aakumulator di mana xmenunjuk, kemudian bertambah xmenurut ukuran akumulator. Ini dilakukan karena lebih cepat daripada pointer aritmatika, karena sangat umum, dan karena itu mudah dimengerti. Baik pra- dan-pos.
fyngyrz

Jawaban:

1059

++bukan operator. Itu adalah dua +operator. The +Operator adalah identitas operator, yang tidak apa-apa. (Klarifikasi: operator +dan -unary hanya bekerja pada angka, tetapi saya berasumsi bahwa Anda tidak akan mengharapkan ++operator hipotetis untuk bekerja pada string.)

++count

Pars sebagai

+(+count)

Yang diterjemahkan menjadi

count

Anda harus menggunakan +=operator yang sedikit lebih lama untuk melakukan apa yang ingin Anda lakukan:

count += 1

Saya curiga ++dan --operator ditinggalkan untuk konsistensi dan kesederhanaan. Saya tidak tahu argumen pasti yang diberikan Guido van Rossum untuk keputusan itu, tetapi saya bisa membayangkan beberapa argumen:

  • Penguraian yang lebih sederhana. Secara teknis, parsing ++countadalah ambigu, karena bisa +, +, count(dua unary +operator) semudah itu bisa ++, count(satu unary ++operator). Ini bukan ambiguitas sintaksis yang signifikan, tetapi memang ada.
  • Bahasa yang lebih sederhana. ++tidak lebih dari sinonim untuk += 1. Itu adalah singkatan yang ditemukan karena kompiler C bodoh dan tidak tahu bagaimana mengoptimalkan instruksi yang a += 1dimiliki inckebanyakan komputer. Pada hari ini mengoptimalkan kompiler dan bytecode bahasa yang ditafsirkan, menambahkan operator ke bahasa untuk memungkinkan programmer untuk mengoptimalkan kode mereka biasanya disukai, terutama dalam bahasa seperti Python yang dirancang agar konsisten dan dapat dibaca.
  • Efek samping yang membingungkan. Salah satu kesalahan newbie umum dalam bahasa dengan ++operator adalah mencampur perbedaan (baik dalam nilai sebelum dan di kembali) antara operator sebelum / sesudah kenaikan / penurunan, dan Python suka menghilangkan bahasa "gotcha" -s. The isu diutamakan dari pra / pasca-kenaikan di C cukup berbulu, dan sangat mudah untuk mengacaukan.
Chris Lutz
sumber
13
"Operator + adalah operator" identitas ", yang tidak melakukan apa pun." Hanya untuk tipe numerik; untuk tipe lain, ini adalah kesalahan secara default.
newacct
45
Perlu diketahui juga bahwa, dalam Python, + = dan teman bukanlah operator yang dapat digunakan dalam ekspresi. Sebaliknya, dalam Python mereka didefinisikan sebagai bagian dari "pernyataan tugas augmented". Ini konsisten dengan keputusan desain bahasa di Python untuk tidak mengizinkan penugasan ("=") sebagai operator dalam ekspresi sewenang-wenang, tidak seperti apa yang dapat dilakukan seseorang di C. Lihat docs.python.org/reference/…
Ned Deily
15
+Operator unary memiliki penggunaan. Untuk desimal. Objek desimal, membulatkan ke presisi saat ini.
u0b34a0f6ae
21
Saya bertaruh pada penyederhanaan parser. Perhatikan item dalam PEP 3099 , "Hal-hal yang Tidak Akan Berubah dengan Python 3000": "Parser tidak akan lebih kompleks dari LL (1). Sederhana lebih baik dari kompleks. Ide ini meluas ke parser. Membatasi tata bahasa Python menjadi parser LL (1) adalah berkah, bukan kutukan. Ini menempatkan kita dalam borgol yang mencegah kita menjadi berlebihan dan berakhir dengan aturan tata bahasa yang funky seperti beberapa bahasa dinamis lain yang tidak akan disebutkan namanya, seperti Perl. " Saya tidak melihat bagaimana cara ambigu + +dan ++tanpa melanggar LL (1).
Mike DeSimone
7
Tidak benar mengatakan bahwa ++itu tidak lebih dari sinonim untuk += 1. Ada varian pre-increment dan post-increment dari ++ sehingga jelas bukan hal yang sama. Saya setuju dengan sisa poin Anda.
PhilHibbs
384

Saat Anda ingin menambah atau mengurangi, Anda biasanya ingin melakukannya pada bilangan bulat. Seperti itu:

b++

Namun dalam Python, bilangan bulat tidak berubah . Itu adalah Anda tidak dapat mengubahnya. Ini karena objek integer dapat digunakan dengan beberapa nama. Coba ini:

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

a dan b di atas sebenarnya adalah objek yang sama. Jika Anda menambahkan a, Anda juga akan bertambah b. Bukan itu yang Anda inginkan. Jadi, Anda harus menugaskan kembali. Seperti ini:

b = b + 1

Atau lebih sederhana:

b += 1

Yang akan dipindahkan bke b+1. Itu bukan operator kenaikan, karena tidak naik b, itu ditugaskan kembali.

Singkatnya: Python berperilaku berbeda di sini, karena itu bukan C, dan bukan pembungkus tingkat rendah di sekitar kode mesin, tetapi bahasa dinamis tingkat tinggi, di mana kenaikan tidak masuk akal, dan juga tidak sepenting di C , di mana Anda menggunakannya setiap kali Anda memiliki loop, misalnya.

Lennart Regebro
sumber
75
Contoh itu salah (dan Anda mungkin membingungkan ketidakmampuan dengan identitas) - mereka memiliki id yang sama karena beberapa optimasi vm yang menggunakan objek yang sama untuk angka hingga 255 (atau sesuatu seperti itu). Misalnya (angka lebih besar): >>> a = 1231231231231 >>> b = 1231231231231 >>> id (a), id (b) (32171144, 32171168)
ionelmc
56
Klaim kekekalan itu palsu. Secara konseptual, i++berarti menugaskan i + 1ke variabel i . i = 5; i++sarana untuk menetapkan 6ke i, tidak memodifikasi intobjek ditunjuk oleh i. Artinya, bukan berarti menambah nilai5 !
Mekanik keong
3
@ Siput mekanik: Dalam hal ini tidak akan menjadi operator kenaikan sama sekali. Dan kemudian operator + = lebih jelas, lebih eksplisit, lebih fleksibel dan tetap melakukan hal yang sama.
Lennart Regebro
7
@LennartRegebro: Di C ++ dan Java, i++hanya beroperasi pada lvalues. Jika itu dimaksudkan untuk menambah objek yang ditunjuk oleh i, pembatasan ini tidak perlu.
Mekanik keong
4
Saya menemukan jawaban ini cukup membingungkan. Mengapa Anda mengasumsikan ++ akan berarti apa pun selain singkatan untuk + = 1? Itulah tepatnya artinya dalam C (dengan asumsi nilai kembali tidak digunakan). Anda sepertinya telah menarik makna lain dari udara.
Don Hatch
52

Sementara jawaban yang lain adalah benar sejauh mereka menunjukkan apa yang +biasa dilakukan (yaitu, biarkan angkanya apa adanya, jika itu adalah satu), mereka tidak lengkap sejauh mereka tidak menjelaskan apa yang terjadi.

Tepatnya, +xmengevaluasi ke x.__pos__()dan ++xke x.__pos__().__pos__().

Saya dapat membayangkan struktur kelas yang SANGAT aneh (Anak-anak, jangan lakukan ini di rumah!) Seperti ini:

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)
glglgl
sumber
13

Python tidak memiliki operator ini, tetapi jika Anda benar-benar membutuhkannya, Anda dapat menulis fungsi yang memiliki fungsi yang sama.

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

Pemakaian:

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

Di dalam fungsi Anda harus menambahkan lokal () sebagai argumen kedua jika Anda ingin mengubah variabel lokal, jika tidak maka ia akan mencoba mengubah global.

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

Juga dengan fungsi-fungsi ini yang dapat Anda lakukan:

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

Namun menurut saya pendekatan berikut ini jauh lebih jelas:

x = 1
x+=1
print(x)

Operator penurunan:

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

Saya menggunakan fungsi-fungsi ini dalam modul saya menerjemahkan javascript ke python.

Piotr Dabkowski
sumber
Catatan: meski hebat, metode pembantu ini tidak akan berfungsi jika penduduk setempat ada di bingkai tumpukan fungsi kelas. yaitu - memanggil mereka dari dalam metode metode def tidak akan berfungsi - dikt 'lokal ()' adalah snapshot, dan tidak memperbarui bingkai stack.
Adam
11

Dalam Python, perbedaan antara ekspresi dan pernyataan ditegakkan secara kaku, berbeda dengan bahasa seperti Common Lisp, Scheme, atau Ruby.

Wikipedia

Jadi dengan memperkenalkan operator seperti itu, Anda akan merusak ekspresi / pernyataan.

Untuk alasan yang sama Anda tidak dapat menulis

if x = 0:
  y = 1

yang Anda bisa dalam beberapa bahasa lain di mana perbedaan seperti itu tidak dipertahankan.

Vitalii Fedorenko
sumber
Menariknya, pembatasan ini akan dicabut dalam rilis Python 3.8 yang akan datang dengan sintaks baru untuk ekspresi Penugasan (PEP-572 python.org/dev/peps/pep-0572 ). Kami akan dapat menulis if (n := len(a)) > 10: y = n + 1misalnya. Perhatikan bahwa perbedaannya jelas karena pengenalan operator baru untuk tujuan itu ( :=)
Zertrin
8

TL; DR

Python tidak memiliki operator kenaikan / penurunan unary ( --/ ++). Sebagai gantinya, untuk menambah nilai, gunakan

a += 1

Lebih detail dan mengerti

Tapi hati-hati di sini. Jika Anda berasal dari C, bahkan ini berbeda dalam python. Python tidak memiliki "variabel" dalam pengertian C, sebaliknya python menggunakan nama dan objek , dan dalam python ints tidak dapat diubah.

jadi katakanlah Anda lakukan

a = 1

Apa artinya ini di python adalah: membuat objek bertipe intmemiliki nilai 1dan mengikat nama auntuk itu. The objek adalah instance dari intmemiliki nilai 1, dan nama a mengacu pada itu. Nama adan objek yang dirujuk berbeda.

Sekarang katakanlah Anda lakukan

a += 1

Karena ints tidak dapat diubah, apa yang terjadi di sini adalah sebagai berikut:

  1. mencari objek yang amerujuk (itu adalah intdengan id 0x559239eeb380)
  2. mencari nilai objek 0x559239eeb380(itu 1)
  3. tambahkan 1 ke nilai itu (1 + 1 = 2)
  4. buat objek baru int dengan nilai 2(memiliki id objek 0x559239eeb3a0)
  5. rebind nama ake objek baru ini
  6. Sekarang amerujuk ke objek 0x559239eeb3a0dan objek asli ( 0x559239eeb380) tidak lagi dirujuk oleh namanya a. Jika tidak ada nama lain yang merujuk ke objek asli itu akan menjadi sampah yang dikumpulkan nanti.

Cobalah sendiri:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
RBF06
sumber
6

Ya, saya melewatkan fungsi ++ dan - juga. Beberapa juta baris kode c menanamkan pemikiran seperti itu di kepala saya yang lama, dan bukannya melawannya ... Ini adalah kelas yang saya gunakan untuk mengimplementasikan:

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

Ini dia:

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

Anda mungkin menggunakannya seperti ini:

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

... sudah memiliki c, Anda bisa melakukan ini ...

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

....atau hanya...

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

... dan untuk (ulang) penugasan ke dalam bilangan bulat ...

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

... sementara ini akan mempertahankan c sebagai penghitung tipe:

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

EDIT:

Dan kemudian ada sedikit perilaku tak terduga (dan benar-benar tidak diinginkan) ini ,

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

... karena di dalam tuple itu, getitem () bukan yang digunakan, alih-alih referensi ke objek diteruskan ke fungsi pemformatan. Mendesah. Begitu:

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

... atau, lebih jelasnya, dan secara eksplisit apa yang sebenarnya kita inginkan terjadi, meskipun diindikasikan dalam bentuk sebenarnya oleh verbosity (digunakan c.vsebagai gantinya) ...

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

Tidak ada operator pasca / kenaikan / penurunan dalam python seperti dalam bahasa seperti C.

Kita dapat melihat ++atau --sebagai beberapa tanda yang dikalikan, seperti yang kita lakukan dalam matematika (-1) * (-1) = (+1).

Misalnya

---count

Pars sebagai

-(-(-count)))

Yang diterjemahkan menjadi

-(+count)

Karena, penggandaan -tanda dengan -tanda adalah+

Dan akhirnya,

-count
Anuj
sumber
1
Apa yang dikatakan ini bahwa jawaban lain tidak?
Daniel B.
@DanielB. Jawaban lain belum memberi tahu apa yang terjadi secara internal. Dan, mereka juga tidak mengatakan apa yang akan terjadi ketika Anda akan menulis -----count.
Anuj
Jawaban pertama yang diterima berlaku. ...
Daniel B.
2
Tidak ada menyebutkan bahwa perkalian sedang dilakukan, jadi saya pikir konsili dan to the point jawabannya akan berguna untuk sesama pengguna. Jangan tersinggung jika Anda mengerti dari itu. Belajar lebih penting daripada sumber dari mana Anda belajar.
Anuj
0

Dengan python 3.8+ Anda dapat melakukan:

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

Anda dapat melakukan banyak pemikiran dengan ini.

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


1
2
3
4

Atau jika Anda ingin menulis sesuatu dengan sintaksis yang lebih canggih (tujuannya bukan optimasi):

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


1
2
3
4

Ini mengembalikan 0 jika ada yang tidak ada tanpa kesalahan, dan kemudian akan menetapkan ke 1

Henry
sumber