Ubah variabel dalam loop python

Beberapa debugger untuk Python dijelaskan di bawah ini, dan fungsi bawaannya memungkinkan Anda untuk masuk ke salah satunya

Modul pdb adalah debugger mode konsol yang sederhana namun memadai untuk Python. Ini adalah bagian dari pustaka Python standar, dan memang demikian. Anda juga dapat menulis debugger Anda sendiri dengan menggunakan kode untuk pdb sebagai contoh

Lingkungan pengembangan interaktif IDLE, yang merupakan bagian dari distribusi Python standar (biasanya tersedia sebagai Alat/skrip/idle3), menyertakan debugger grafis

PythonWin adalah IDE Python yang menyertakan debugger GUI berdasarkan pdb. Warna debugger PythonWin breakpoints dan memiliki beberapa fitur keren seperti debugging program non-PythonWin. PythonWin tersedia sebagai bagian dari proyek pywin32 dan sebagai bagian dari distribusi ActivePython

Eric adalah IDE yang dibangun di atas PyQt dan komponen pengeditan Scintilla

trepan3k adalah debugger mirip gdb

Visual Studio Code adalah IDE dengan alat debugging yang terintegrasi dengan perangkat lunak kontrol versi

Ada sejumlah IDE Python komersial yang menyertakan debugger grafis. Mereka termasuk

  • Sayap IDE

  • IDE Komodo

  • PyCharm

Ya

Pylint dan Pyflakes melakukan pemeriksaan dasar yang akan membantu Anda menemukan bug lebih cepat

Pemeriksa tipe statis seperti Mypy, Pyre, dan Pytype dapat memeriksa petunjuk tipe dalam kode sumber Python

Anda tidak memerlukan kemampuan untuk mengkompilasi kode Python ke C jika yang Anda inginkan adalah program yang berdiri sendiri yang dapat diunduh dan dijalankan oleh pengguna tanpa harus menginstal distribusi Python terlebih dahulu. Ada sejumlah alat yang menentukan kumpulan modul yang diperlukan oleh suatu program dan mengikat modul-modul ini bersama-sama dengan biner Python untuk menghasilkan satu modul yang dapat dieksekusi.

Salah satunya adalah dengan menggunakan alat freeze, yang termasuk dalam pohon sumber Python sebagai Tools/freeze. Itu mengubah kode byte Python ke array C;

Ini bekerja dengan memindai sumber Anda secara rekursif untuk pernyataan impor (dalam kedua bentuk) dan mencari modul di jalur Python standar serta di direktori sumber (untuk modul bawaan). Ini kemudian mengubah bytecode untuk modul yang ditulis dengan Python menjadi kode C (penginisialisasi array yang dapat diubah menjadi objek kode menggunakan modul marshal) dan membuat file konfigurasi yang dibuat khusus yang hanya berisi modul bawaan yang benar-benar digunakan dalam . Itu kemudian mengkompilasi kode C yang dihasilkan dan menautkannya dengan juru bahasa Python lainnya untuk membentuk biner mandiri yang bertindak persis seperti skrip Anda

Paket-paket berikut dapat membantu pembuatan konsol dan GUI yang dapat dieksekusi

  • Nuitka (Lintas platform)

  • PyInstaller (Lintas platform)

  • PyOxidizer (Lintas platform)

  • cx_Freeze (Lintas platform)

  • py2app (khusus macOS)

  • py2exe (khusus Windows)

Ya. Gaya pengkodean yang diperlukan untuk modul perpustakaan standar didokumentasikan sebagai PEP 8

Ini bisa menjadi kejutan untuk mendapatkan kode yang sebelumnya berfungsi ketika dimodifikasi dengan menambahkan pernyataan penugasan di suatu tempat di badan fungsi

Kode ini

>>> x = 10
>>> def bar():
..     print(x)
...
>>> bar()
10

bekerja, tapi kode ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_

menghasilkan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_22

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment

Ini karena ketika Anda membuat tugas ke variabel dalam lingkup, variabel tersebut menjadi lokal untuk lingkup tersebut dan membayangi variabel bernama serupa di lingkup luar. Karena pernyataan terakhir di foo menetapkan nilai baru ke

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24, kompiler mengenalinya sebagai variabel lokal. Akibatnya ketika
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
25 sebelumnya mencoba untuk mencetak variabel lokal yang tidak diinisialisasi dan hasil kesalahan

Dalam contoh di atas, Anda dapat mengakses variabel lingkup luar dengan mendeklarasikannya secara global

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_

Deklarasi eksplisit ini diperlukan untuk mengingatkan Anda bahwa (tidak seperti situasi analog yang dangkal dengan kelas dan variabel instan) Anda sebenarnya mengubah nilai variabel di lingkup luar

>>> print(x)
11

Anda dapat melakukan hal serupa dalam lingkup bersarang menggunakan kata kunci

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_

Dalam Python, variabel yang hanya direferensikan di dalam suatu fungsi bersifat global secara implisit. Jika sebuah variabel diberi nilai di mana saja di dalam badan fungsi, itu dianggap sebagai lokal kecuali secara eksplisit dinyatakan sebagai global

Meskipun agak mengejutkan pada awalnya, pertimbangan sesaat menjelaskan hal ini. Di satu sisi, mensyaratkan variabel yang ditetapkan memberikan batasan terhadap efek samping yang tidak diinginkan. Di sisi lain, jika

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
27 diperlukan untuk semua referensi global, Anda akan selalu menggunakan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
27. Anda harus mendeklarasikan sebagai global setiap referensi ke fungsi bawaan atau ke komponen modul yang diimpor. Kekacauan ini akan mengalahkan kegunaan deklarasi
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
27 untuk mengidentifikasi efek samping

Asumsikan Anda menggunakan for loop untuk mendefinisikan beberapa lambda yang berbeda (atau bahkan fungsi biasa), mis. g

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)

Ini memberi Anda daftar yang berisi 5 lambda yang menghitung

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
31. Anda mungkin berharap bahwa, ketika dipanggil, mereka akan kembali, masing-masing,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
32,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
33,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
34,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
35, dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
36. Namun, ketika Anda benar-benar mencoba, Anda akan melihat bahwa semuanya kembali
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
36

>>> squares[2]()
16
>>> squares[4]()
16

Hal ini terjadi karena

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 tidak bersifat lokal untuk lambda, tetapi didefinisikan di lingkup luar, dan diakses saat lambda dipanggil — bukan saat didefinisikan. Di akhir perulangan, nilai
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 adalah
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
34, jadi semua fungsi sekarang mengembalikan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
41, i. e.
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_36. Anda juga dapat memverifikasi ini dengan mengubah nilai
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 dan melihat bagaimana hasil lambdas berubah

>>> x = 8
>>> squares[2]()
64

Untuk menghindari hal ini, Anda perlu menyimpan nilai dalam variabel lokal ke lambda, sehingga mereka tidak bergantung pada nilai global

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)

Di sini,

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_45 membuat variabel baru
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
46 lokal ke lambda dan dihitung ketika lambda didefinisikan sehingga memiliki nilai yang sama dengan yang dimiliki
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 pada saat itu di loop. Artinya, nilai
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_46 akan menjadi
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
32 di lambda pertama,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
33 di lambda kedua,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
51 di lambda ketiga, dan seterusnya. Oleh karena itu setiap lambda sekarang akan mengembalikan hasil yang benar

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_0

Perhatikan bahwa perilaku ini tidak khusus untuk lambda, tetapi juga berlaku untuk fungsi reguler

Cara kanonik untuk berbagi informasi lintas modul dalam satu program adalah dengan membuat modul khusus (sering disebut config atau cfg). Cukup impor modul konfigurasi di semua modul aplikasi Anda; . Karena hanya ada satu instance dari setiap modul, setiap perubahan yang dibuat pada objek modul akan tercermin di mana-mana. Sebagai contoh

config. py

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_1

mod. py

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_2

utama. py

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_3

Perhatikan bahwa menggunakan modul juga merupakan dasar untuk mengimplementasikan pola desain singleton, untuk alasan yang sama

Secara umum, jangan gunakan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_52. Melakukan hal itu mengacaukan ruang nama importir, dan mempersulit linter untuk mendeteksi nama yang tidak ditentukan

Impor modul di bagian atas file. Melakukan hal itu memperjelas modul apa yang dibutuhkan kode Anda dan menghindari pertanyaan apakah nama modul ada dalam cakupan. Menggunakan satu impor per baris memudahkan untuk menambah dan menghapus impor modul, tetapi menggunakan banyak impor per baris menggunakan lebih sedikit ruang layar

Ini praktik yang baik jika Anda mengimpor modul dalam urutan berikut

  1. modul pustaka standar – e. g. , , ,

  2. modul perpustakaan pihak ketiga (apa pun yang dipasang di direktori paket situs Python) – e. g.

    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    _57,
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    58,
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    59

  3. modul yang dikembangkan secara lokal

Terkadang perlu memindahkan impor ke fungsi atau kelas untuk menghindari masalah dengan impor melingkar. kata Gordon McMillan

Circular imports are fine where both modules use the “import ” form of import. They fail when the 2nd module wants to grab a name out of the first (“from module import name”) and the import is at the top level. That’s because names in the 1st are not yet available, because the first module is busy importing the 2nd.

Dalam hal ini, jika modul kedua hanya digunakan dalam satu fungsi, maka impor dapat dengan mudah dipindahkan ke fungsi tersebut. Pada saat impor dipanggil, modul pertama sudah selesai diinisialisasi, dan modul kedua dapat melakukan impornya

Mungkin juga diperlukan untuk memindahkan impor dari kode tingkat atas jika beberapa modul khusus untuk platform. Dalam hal ini, bahkan tidak mungkin untuk mengimpor semua modul di bagian atas file. Dalam hal ini, mengimpor modul yang benar dalam kode khusus platform yang sesuai adalah pilihan yang baik

Hanya pindahkan impor ke lingkup lokal, seperti di dalam definisi fungsi, jika diperlukan untuk menyelesaikan masalah seperti menghindari impor melingkar atau mencoba mengurangi waktu inisialisasi modul. Teknik ini sangat membantu jika banyak impor yang tidak diperlukan bergantung pada bagaimana program dijalankan. Anda mungkin juga ingin memindahkan impor ke suatu fungsi jika modul hanya digunakan dalam fungsi itu. Perhatikan bahwa memuat modul pertama kali mungkin mahal karena inisialisasi modul satu kali, tetapi memuat modul berkali-kali sebenarnya gratis, hanya menghabiskan beberapa pencarian kamus. Bahkan jika nama modul telah keluar dari ruang lingkup, modul tersebut mungkin tersedia di

Jenis bug ini biasanya menggigit programmer pemula. Pertimbangkan fungsi ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_4

Pertama kali Anda memanggil fungsi ini,

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_61 berisi satu item. Kedua kalinya,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_61 berisi dua item karena ketika
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
63 mulai dijalankan,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
61 dimulai dengan item yang sudah ada di dalamnya

Seringkali pemanggilan fungsi diharapkan membuat objek baru untuk nilai default. Bukan ini yang terjadi. Nilai default dibuat tepat satu kali, saat fungsi ditentukan. Jika objek itu diubah, seperti kamus dalam contoh ini, panggilan selanjutnya ke fungsi tersebut akan merujuk ke objek yang diubah ini

Menurut definisi, objek yang tidak dapat diubah seperti angka, string, tupel, dan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65, aman dari perubahan. Perubahan pada objek yang dapat diubah seperti kamus, daftar, dan instance kelas dapat menyebabkan kebingungan

Karena fitur ini, merupakan praktik pemrograman yang baik untuk tidak menggunakan objek yang dapat diubah sebagai nilai default. Alih-alih, gunakan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_65 sebagai nilai default dan di dalam fungsi, periksa apakah parameternya adalah
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65 dan buat daftar/kamus baru/apa pun jika itu. Misalnya, jangan menulis

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_5

tetapi

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_6

Fitur ini dapat bermanfaat. Saat Anda memiliki fungsi yang memakan waktu komputasi, teknik yang umum adalah meng-cache parameter dan nilai yang dihasilkan dari setiap panggilan ke fungsi, dan mengembalikan nilai yang di-cache jika nilai yang sama diminta lagi. Ini disebut "memoisasi", dan dapat diimplementasikan seperti ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_7

Anda bisa menggunakan variabel global yang berisi kamus alih-alih nilai default;

Kumpulkan argumen menggunakan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_68 dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
69 penentu dalam daftar parameter fungsi; . Anda kemudian dapat meneruskan argumen ini saat memanggil fungsi lain dengan menggunakan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
68 dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
69

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_8

didefinisikan oleh nama yang muncul dalam definisi fungsi, sedangkan nilai sebenarnya diteruskan ke fungsi saat memanggilnya. Parameter menentukan apa yang dapat diterima oleh suatu fungsi. Misalnya, diberikan definisi fungsi

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_9

foo, bar dan kwargs adalah parameter dari

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
72. Namun, saat menelepon
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_72, misalnya

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
0

nilai

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_74,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
75, dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
76 adalah argumen

Jika Anda menulis kode seperti

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
1

Anda mungkin bertanya-tanya mengapa menambahkan elemen ke

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
77 mengubah
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 juga

Ada dua faktor yang menghasilkan hasil ini

  1. Variabel hanyalah nama yang merujuk ke objek. Melakukan

    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    _79 tidak membuat salinan daftar – itu membuat variabel baru
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    77 yang merujuk ke objek yang sama
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    24 mengacu pada. Ini berarti bahwa hanya ada satu objek (daftar), dan keduanya
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    24 dan
    >>> x = 10
    >>> def foo():
    ..     print(x)
    ..     x += 1
    
    77 mengacu padanya

  2. Daftar adalah , artinya Anda dapat mengubah kontennya

Setelah panggilan ke

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_84, konten objek yang dapat diubah telah berubah dari
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
85 menjadi
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
86. Karena kedua variabel merujuk ke objek yang sama, menggunakan salah satu nama mengakses nilai yang dimodifikasi
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
86

Jika kami malah menetapkan objek yang tidak dapat diubah ke

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
2

kita dapat melihat bahwa dalam hal ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_24 dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
77 tidak sama lagi. Ini karena bilangan bulat adalah , dan ketika kita melakukannya
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
91 kita tidak mengubah int
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
92 dengan menaikkan nilainya; . Setelah penugasan ini, kami memiliki dua objek (int
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
93 dan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
92) dan dua variabel yang merujuk ke mereka (
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 sekarang merujuk ke
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
93 tetapi
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
77 masih merujuk ke
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
92)

Beberapa operasi (misalnya

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
02 dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
03) mengubah objek, sedangkan operasi yang mirip secara dangkal (misalnya
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
04 dan ) membuat objek baru. Secara umum dalam Python (dan dalam semua kasus di perpustakaan standar) metode yang memutasi objek akan mengembalikan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65 untuk membantu menghindari dua jenis operasi yang membingungkan. Jadi, jika Anda keliru menulis
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_03 berpikir itu akan memberi Anda salinan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
77 yang telah diurutkan, Anda malah akan berakhir dengan
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65, yang kemungkinan akan menyebabkan program Anda menghasilkan kesalahan yang mudah didiagnosis

Namun, ada satu kelas operasi dimana operasi yang sama terkadang memiliki perilaku yang berbeda dengan tipe yang berbeda. operator penugasan ditambah. Misalnya,

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10 memutasikan daftar tetapi bukan tupel atau int (
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
11 setara dengan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
12 dan memutasikan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
13, sedangkan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
14 dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
15 membuat objek baru)

Dengan kata lain

  • Jika kita memiliki objek yang bisa berubah (, , , dll. ), kita dapat menggunakan beberapa operasi khusus untuk memutasinya dan semua variabel yang merujuknya akan melihat perubahannya

  • Jika kita memiliki objek yang tidak dapat diubah (, , , dll. ), semua variabel yang merujuk padanya akan selalu melihat nilai yang sama, tetapi operasi yang mengubah nilai tersebut menjadi nilai baru selalu mengembalikan objek baru

Jika Anda ingin mengetahui apakah dua variabel merujuk ke objek yang sama atau tidak, Anda dapat menggunakan operator, atau fungsi bawaan

Ingatlah bahwa argumen diteruskan dengan penugasan di Python. Karena penugasan hanya membuat referensi ke objek, tidak ada alias antara nama argumen di pemanggil dan yang dipanggil, jadi tidak ada referensi panggilan per se. Anda dapat mencapai efek yang diinginkan dengan beberapa cara

  1. Dengan mengembalikan tuple hasil

    >>> foo()
    Traceback (most recent call last):
      ...
    UnboundLocalError: local variable 'x' referenced before assignment
    
    _3

    Ini hampir selalu merupakan solusi paling jelas

  2. Dengan menggunakan variabel global. Ini tidak aman untuk thread, dan tidak disarankan

  3. Dengan melewati objek yang bisa berubah (dapat diubah di tempat).

    >>> foo()
    Traceback (most recent call last):
      ...
    UnboundLocalError: local variable 'x' referenced before assignment
    
    _4

  4. Dengan mengirimkan kamus yang bermutasi

    >>> foo()
    Traceback (most recent call last):
      ...
    UnboundLocalError: local variable 'x' referenced before assignment
    
    5

  5. Atau bundel nilai dalam instance kelas

    >>> foo()
    Traceback (most recent call last):
      ...
    UnboundLocalError: local variable 'x' referenced before assignment
    
    6

    Hampir tidak pernah ada alasan bagus untuk menjadi serumit ini

Pilihan terbaik Anda adalah mengembalikan tuple yang berisi banyak hasil

Anda memiliki dua pilihan. Anda dapat menggunakan cakupan bersarang atau Anda dapat menggunakan objek yang dapat dipanggil. Misalnya, Anda ingin mendefinisikan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_24 yang mengembalikan fungsi
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
25 yang menghitung nilai
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
26. Menggunakan lingkup bersarang

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_7

Atau menggunakan objek yang bisa dipanggil

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
8

Dalam kedua kasus tersebut,

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_9

memberikan objek yang dapat dipanggil di mana

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_27

Pendekatan objek yang dapat dipanggil memiliki kelemahan yaitu sedikit lebih lambat dan menghasilkan kode yang sedikit lebih panjang. Namun, perhatikan bahwa kumpulan callable dapat membagikan tanda tangan mereka melalui pewarisan

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_0

Objek dapat mengenkapsulasi status untuk beberapa metode

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_1

Di sini

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_28,
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
29 dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
30 bertindak seperti fungsi yang berbagi variabel penghitungan yang sama

Secara umum, coba atau untuk kasus umum. Tidak semua objek bisa disalin, tapi sebagian besar bisa

Beberapa objek dapat disalin dengan lebih mudah. Kamus memiliki metode

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_2

Urutan dapat disalin dengan mengiris

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_3

Untuk instance

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 dari kelas yang ditentukan pengguna, mengembalikan daftar alfabet dari nama yang berisi atribut dan metode instan dan atribut yang ditentukan oleh kelasnya

Secara umum, tidak bisa, karena objek sebenarnya tidak memiliki nama. Pada dasarnya, penugasan selalu mengikat sebuah nama ke sebuah nilai; . Pertimbangkan kode berikut

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_4

Bisa dibilang kelas tersebut memiliki nama. meskipun terikat pada dua nama dan dipanggil melalui nama

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
38 instance yang dibuat masih dilaporkan sebagai instance dari kelas
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
39. Namun, tidak mungkin untuk mengatakan apakah nama instance adalah
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
40 atau
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
41, karena kedua nama terikat dengan nilai yang sama

Secara umum, kode Anda tidak perlu "mengetahui nama" dari nilai tertentu. Kecuali jika Anda sengaja menulis program introspektif, ini biasanya merupakan indikasi bahwa perubahan pendekatan mungkin bermanfaat

Dalam komp. lang. python, Fredrik Lundh pernah memberikan analogi yang sangat bagus untuk menjawab pertanyaan ini

Cara yang sama seperti Anda mendapatkan nama kucing yang Anda temukan di teras Anda. kucing (objek) itu sendiri tidak dapat memberi tahu Anda namanya, dan ia tidak terlalu peduli - jadi satu-satunya cara untuk mengetahui apa namanya adalah dengan bertanya kepada semua tetangga (ruang nama) Anda apakah itu kucing (objek) mereka…

…. dan jangan kaget jika Anda menemukan bahwa itu dikenal dengan banyak nama, atau tanpa nama sama sekali

Koma bukan operator di Python. Pertimbangkan sesi ini

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_5

Karena koma bukan operator, tetapi pemisah antara ekspresi di atas dievaluasi seolah-olah Anda telah memasukkannya

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_6

bukan

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_7

Hal yang sama berlaku untuk berbagai operator penugasan (

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
42,
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10 dll). Mereka sebenarnya bukan operator tetapi pembatas sintaksis dalam pernyataan penugasan

Ya ada. Sintaksnya adalah sebagai berikut

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_8

Sebelum sintaks ini diperkenalkan di Python 2. 5, idiom umum adalah menggunakan operator logika

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_9

Namun, idiom ini tidak aman, karena dapat memberikan hasil yang salah ketika on_true memiliki nilai boolean yang salah. Oleh karena itu, selalu lebih baik menggunakan formulir ________3______44

Ya. Biasanya ini dilakukan dengan bersarang di dalam

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_45. Lihat tiga contoh berikut, sedikit diadaptasi dari Ulf Bartelt

>>> print(x)
11
0

Jangan coba ini di rumah, anak-anak

Garis miring dalam daftar argumen suatu fungsi menunjukkan bahwa parameter sebelumnya hanya untuk posisi. Parameter khusus posisi adalah parameter tanpa nama yang dapat digunakan secara eksternal. Saat memanggil fungsi yang menerima parameter hanya posisional, argumen dipetakan ke parameter hanya berdasarkan posisinya. Misalnya, adalah fungsi yang menerima parameter hanya posisi. Dokumentasinya terlihat seperti ini

>>> print(x)
11
1

Garis miring di akhir daftar parameter berarti kedua parameter hanya untuk posisi. Jadi, memanggil dengan argumen kata kunci akan menyebabkan kesalahan

>>> print(x)
11
2

Untuk menentukan digit oktal, awali nilai oktal dengan nol, lalu huruf kecil atau huruf besar “o”. Misalnya, untuk mengatur variabel "a" ke nilai oktal "10" (8 dalam desimal), ketik

>>> print(x)
11
3

Heksadesimal sama mudahnya. Cukup awali angka heksadesimal dengan nol, lalu huruf kecil atau besar “x”. Digit heksadesimal dapat ditentukan dalam huruf kecil atau huruf besar. Misalnya, dalam juru bahasa Python

>>> print(x)
11
4

Ini terutama didorong oleh keinginan bahwa

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
49 memiliki tanda yang sama dengan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
50. Jika Anda menginginkannya, dan juga menginginkannya

>>> print(x)
11
5

maka pembagian bilangan bulat harus mengembalikan lantai. C juga mengharuskan identitas tersebut dipertahankan, dan kemudian kompiler yang memotong

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
51 perlu membuat
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
49 memiliki tanda yang sama dengan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
53

Ada beberapa kasus penggunaan nyata untuk

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_49 saat
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
50 negatif. Ketika
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_50 positif, ada banyak, dan hampir semuanya lebih berguna untuk
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
49 menjadi
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
58. Jika jam mengatakan 10 sekarang, apa yang dikatakan 200 jam yang lalu?

Mencoba mencari

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_20 atribut literal dengan cara normal memberikan a karena periode dipandang sebagai titik desimal

>>> print(x)
11
6

Solusinya adalah memisahkan literal dari titik dengan spasi atau tanda kurung

>>> print(x)
11
7

Untuk bilangan bulat, gunakan konstruktor tipe bawaan, mis. g.

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_64. Demikian pula, mengkonversi ke floating-point, mis. g.
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_66

Secara default, ini mengartikan angka sebagai desimal, sehingga

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
67 benar, dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
68 memunculkan.
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
70 mengambil basis untuk dikonversi dari sebagai argumen opsional kedua, jadi
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
71. Jika basis ditentukan sebagai 0, angka tersebut ditafsirkan menggunakan aturan Python. awalan '0o' menunjukkan oktal, dan '0x' menunjukkan angka hex

Jangan gunakan fungsi bawaan jika yang Anda butuhkan hanyalah mengonversi string menjadi angka. akan secara signifikan lebih lambat dan menimbulkan risiko keamanan. seseorang dapat memberi Anda ekspresi Python yang mungkin memiliki efek samping yang tidak diinginkan. Misalnya, seseorang dapat mengirimkan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
74 yang akan menghapus direktori home Anda

juga memiliki efek menafsirkan angka sebagai ekspresi Python, sehingga e. g.

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
76 memberikan kesalahan sintaksis karena Python tidak mengizinkan awalan '0' dalam angka desimal (kecuali '0')

Untuk mengkonversi, e. g. , nomor

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
77 ke string
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
78, gunakan konstruktor tipe bawaan. Jika Anda menginginkan representasi heksadesimal atau oktal, gunakan fungsi bawaan atau. Untuk pemformatan mewah, lihat bagian dan, mis. g.
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
82 menghasilkan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
83 dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
84 menghasilkan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
85

Anda tidak bisa, karena string tidak dapat diubah. Dalam kebanyakan situasi, Anda cukup membuat string baru dari berbagai bagian yang ingin Anda rakit. Namun, jika Anda memerlukan objek dengan kemampuan untuk mengubah data unicode di tempat, coba gunakan objek atau modul

>>> print(x)
11
_8

Ada berbagai teknik

  • Yang terbaik adalah menggunakan kamus yang memetakan string ke fungsi. Keuntungan utama dari teknik ini adalah string tidak perlu cocok dengan nama fungsi. Ini juga merupakan teknik utama yang digunakan untuk meniru konstruksi kasus

    >>> print(x)
    11
    
    _9

  • Gunakan fungsi bawaan

    >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    _0

    Perhatikan bahwa berfungsi pada objek apa pun, termasuk kelas, instance kelas, modul, dan sebagainya

    Ini digunakan di beberapa tempat di perpustakaan standar, seperti ini

    >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    _1

  • Gunakan untuk menyelesaikan nama fungsi

    >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    _2

Anda dapat menggunakan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_91 untuk menghapus semua kemunculan terminator baris apa pun dari akhir string
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
92 tanpa menghapus spasi kosong lainnya. Jika string
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
92 mewakili lebih dari satu baris, dengan beberapa baris kosong di akhir, baris terminator untuk semua baris kosong akan dihapus

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_3

Karena ini biasanya hanya diinginkan saat membaca teks satu baris pada satu waktu, menggunakan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
94 cara ini bekerja dengan baik

Tidak seperti itu

Untuk penguraian input sederhana, pendekatan termudah biasanya adalah membagi baris menjadi kata-kata yang dipisahkan spasi menggunakan metode objek string dan kemudian mengonversi string desimal menjadi nilai numerik menggunakan atau.

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
95 mendukung parameter "sep" opsional yang berguna jika garis menggunakan sesuatu selain spasi putih sebagai pemisah

Untuk penguraian input yang lebih rumit, ekspresi reguler lebih kuat daripada C

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
99 dan lebih cocok untuk tugas tersebut

Lihat

String mentah yang diakhiri dengan garis miring terbalik dalam jumlah ganjil akan lolos dari kutipan string

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_4

Ada beberapa solusi untuk ini. Salah satunya adalah menggunakan string biasa dan menggandakan garis miring terbalik

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_5

Cara lainnya adalah menggabungkan string biasa yang berisi tanda garis miring terbalik ke string mentah

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_6

Dimungkinkan juga untuk menggunakan garis miring terbalik pada Windows

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_7

Perhatikan bahwa meskipun garis miring terbalik akan "melarikan diri" dari kutipan untuk tujuan menentukan di mana string mentah berakhir, tidak ada pelolosan yang terjadi saat menginterpretasikan nilai string mentah. Artinya, garis miring terbalik tetap ada pada nilai string mentah

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_8

Lihat juga spesifikasinya di

Itu yang sulit, secara umum. Pertama, berikut adalah daftar hal yang perlu diingat sebelum menyelam lebih jauh

  • Karakteristik kinerja bervariasi di seluruh implementasi Python. FAQ ini berfokus pada

  • Perilaku dapat bervariasi di seluruh sistem operasi, terutama saat berbicara tentang I/O atau multi-threading

  • Anda harus selalu menemukan hot spot di program Anda sebelum mencoba mengoptimalkan kode apa pun (lihat modul)

  • Menulis skrip tolok ukur akan memungkinkan Anda melakukan iterasi dengan cepat saat mencari peningkatan (lihat modul)

  • Sangat disarankan untuk memiliki cakupan kode yang baik (melalui pengujian unit atau teknik lainnya) sebelum berpotensi memperkenalkan regresi yang tersembunyi dalam pengoptimalan canggih

Meski begitu, ada banyak trik untuk mempercepat kode Python. Berikut adalah beberapa prinsip umum yang sangat membantu untuk mencapai tingkat kinerja yang dapat diterima

  • Membuat algoritme Anda lebih cepat (atau mengubah ke yang lebih cepat) dapat menghasilkan manfaat yang jauh lebih besar daripada mencoba memercikkan trik pengoptimalan mikro ke seluruh kode Anda

  • Gunakan struktur data yang tepat. Pelajari dokumentasi untuk dan modul

  • Ketika perpustakaan standar menyediakan primitif untuk melakukan sesuatu, kemungkinan (walaupun tidak dijamin) lebih cepat daripada alternatif apa pun yang mungkin Anda buat. Ini berlaku ganda untuk primitif yang ditulis dalam C, seperti bawaan dan beberapa jenis ekstensi. Misalnya, pastikan untuk menggunakan metode bawaan atau fungsi terkait untuk melakukan penyortiran (dan lihat contoh penggunaan tingkat lanjut yang sedang)

  • Abstraksi cenderung menciptakan tipuan dan memaksa penafsir untuk bekerja lebih banyak. Jika tingkat tipuan melebihi jumlah pekerjaan berguna yang dilakukan, program Anda akan lebih lambat. Anda harus menghindari abstraksi yang berlebihan, terutama dalam bentuk fungsi atau metode kecil (yang juga seringkali merugikan keterbacaan)

Jika Anda telah mencapai batas yang diperbolehkan oleh Python murni, ada alat untuk membawa Anda lebih jauh. Misalnya, Cython dapat mengkompilasi versi kode Python yang sedikit dimodifikasi menjadi ekstensi C, dan dapat digunakan di banyak platform berbeda. Cython dapat memanfaatkan kompilasi (dan anotasi tipe opsional) untuk membuat kode Anda jauh lebih cepat daripada saat ditafsirkan. Jika Anda yakin dengan keterampilan pemrograman C Anda, Anda juga bisa melakukannya sendiri

Lihat juga

Halaman wiki yang dikhususkan untuk tip kinerja

dan objek tidak dapat diubah, oleh karena itu menggabungkan banyak string menjadi tidak efisien karena setiap penggabungan membuat objek baru. Dalam kasus umum, total biaya runtime adalah kuadrat dalam total panjang string

Untuk mengumpulkan banyak objek, idiom yang disarankan adalah menempatkannya ke dalam daftar dan menelepon di bagian akhir

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_9

(idiom lain yang cukup efisien adalah menggunakan )

Untuk mengumpulkan banyak objek, idiom yang disarankan adalah memperluas objek menggunakan penggabungan di tempat (operator

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10)

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
0

Konstruktor tipe

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
14 mengubah urutan apa pun (sebenarnya, iterable apa pun) menjadi tuple dengan item yang sama dalam urutan yang sama

Misalnya,

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_15 menghasilkan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
16 dan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
17 menghasilkan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
18. Jika argumennya adalah tuple, itu tidak membuat salinan tetapi mengembalikan objek yang sama, jadi murah untuk menelepon ketika Anda tidak yakin bahwa suatu objek sudah menjadi tuple

Konstruktor tipe

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
20 mengubah urutan apa pun atau iterable menjadi daftar dengan item yang sama dalam urutan yang sama. Misalnya,
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_21 menghasilkan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
22 dan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
23 menghasilkan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
24. Jika argumennya adalah daftar, itu membuat salinan seperti
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
25

Urutan python diindeks dengan angka positif dan angka negatif. Untuk bilangan positif 0 adalah indeks pertama 1 adalah indeks kedua dan seterusnya. Untuk indeks negatif -1 adalah indeks terakhir dan -2 adalah indeks kedua dari belakang (di sebelah terakhir) dan seterusnya. Pikirkan

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_26 sama dengan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
27

Menggunakan indeks negatif bisa sangat nyaman. Misalnya

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_28 adalah semua string kecuali karakter terakhirnya, yang berguna untuk menghapus baris baru dari sebuah string

Gunakan fungsi bawaan

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
1

Ini tidak akan menyentuh urutan asli Anda, tetapi buat salinan baru dengan urutan terbalik untuk mengulanginya

Lihat Buku Masak Python untuk diskusi panjang tentang banyak cara untuk melakukan ini

https. //kode. status aktif. com/resep/52560/

Jika Anda tidak keberatan menyusun ulang daftar, urutkan, lalu pindai dari akhir daftar, hapus duplikat saat Anda melakukannya

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
2

Jika semua elemen daftar dapat digunakan sebagai kunci set (mis. e. mereka semua ) ini seringkali lebih cepat

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
3

Ini mengubah daftar menjadi satu set, sehingga menghapus duplikat, dan kemudian kembali ke daftar

Seperti halnya menghapus duplikat, iterasi secara terbalik secara eksplisit dengan kondisi hapus adalah salah satu kemungkinannya. Namun, lebih mudah dan lebih cepat untuk menggunakan penggantian irisan dengan iterasi maju implisit atau eksplisit. Berikut adalah tiga variasi

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
4

Pemahaman daftar mungkin tercepat

Gunakan daftar

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
5

Daftar setara dengan array C atau Pascal dalam kompleksitas waktunya;

Modul

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_87 juga menyediakan metode untuk membuat array tipe tetap dengan representasi kompak, tetapi lebih lambat untuk mengindeks daripada daftar. Perhatikan juga bahwa NumPy dan paket pihak ketiga lainnya juga mendefinisikan struktur seperti array dengan berbagai karakteristik

Untuk mendapatkan daftar tertaut gaya Lisp, Anda dapat meniru sel kontra menggunakan tupel

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
6

Jika mutabilitas diinginkan, Anda bisa menggunakan daftar, bukan tupel. Di sini analog mobil Lisp adalah

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
31 dan analog cdr adalah
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
32. Lakukan ini hanya jika Anda yakin benar-benar perlu, karena biasanya jauh lebih lambat daripada menggunakan daftar Python

Anda mungkin mencoba membuat array multidimensi seperti ini

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
7

Ini terlihat benar jika Anda mencetaknya

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
8

Namun saat Anda menetapkan nilai, nilai itu muncul di banyak tempat

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda: x**2)
_9

Alasannya adalah mereplikasi daftar dengan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_68 tidak membuat salinan, itu hanya membuat referensi ke objek yang ada. The
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
34 membuat daftar yang berisi 3 referensi ke daftar yang sama dengan panjang dua. Perubahan pada satu baris akan ditampilkan di semua baris, yang hampir pasti bukan yang Anda inginkan

Pendekatan yang disarankan adalah membuat daftar dengan panjang yang diinginkan terlebih dahulu lalu mengisi setiap elemen dengan daftar yang baru dibuat

>>> squares[2]()
16
>>> squares[4]()
16
0

Ini menghasilkan daftar yang berisi 3 daftar berbeda dengan panjang dua. Anda juga dapat menggunakan pemahaman daftar

>>> squares[2]()
16
>>> squares[4]()
16
1

Atau, Anda dapat menggunakan ekstensi yang menyediakan tipe data matriks;

Untuk memanggil metode atau fungsi dan mengumpulkan nilai pengembalian adalah daftar, a adalah solusi yang elegan

>>> squares[2]()
16
>>> squares[4]()
16
2

Untuk hanya menjalankan metode atau fungsi tanpa menyimpan nilai yang dikembalikan, loop biasa sudah cukup

>>> squares[2]()
16
>>> squares[4]()
16
3

Ini karena kombinasi dari fakta bahwa operator penugasan yang diperbesar adalah operator penugasan, dan perbedaan antara objek yang dapat diubah dan yang tidak dapat diubah di Python

Diskusi ini berlaku secara umum ketika operator penugasan tambahan diterapkan ke elemen tupel yang mengarah ke objek yang dapat diubah, tetapi kami akan menggunakan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
16 dan
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10 sebagai contoh kami

Jika Anda menulis

>>> squares[2]()
16
>>> squares[4]()
16
4

Alasan pengecualian harus segera jelas.

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_33 ditambahkan ke objek
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
39 menunjuk ke (
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
33), menghasilkan objek hasil,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
51, tetapi ketika kami mencoba untuk menetapkan hasil perhitungan,
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
51, ke elemen
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
32 dari tuple, kami mendapatkan kesalahan karena kami tidak bisa

Di balik selimut, apa yang dilakukan pernyataan penugasan tambahan ini kira-kira seperti ini

>>> squares[2]()
16
>>> squares[4]()
16
5

Itu adalah bagian penugasan dari operasi yang menghasilkan kesalahan, karena tupel tidak dapat diubah

Ketika Anda menulis sesuatu seperti

>>> squares[2]()
16
>>> squares[4]()
16
6

Pengecualian ini sedikit lebih mengejutkan, dan yang lebih mengejutkan lagi adalah fakta bahwa meskipun ada kesalahan, appendnya berfungsi

>>> squares[2]()
16
>>> squares[4]()
16
7

Untuk mengetahui mengapa hal ini terjadi, Anda perlu mengetahui bahwa (a) jika sebuah objek mengimplementasikan metode ajaib, objek tersebut akan dipanggil saat penugasan tambahan

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10 dijalankan, dan nilai pengembaliannya digunakan dalam pernyataan penugasan; . Itu sebabnya kami mengatakan bahwa untuk daftar,
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
10 adalah "singkatan" untuk
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
49

>>> squares[2]()
16
>>> squares[4]()
16
8

Ini setara dengan

>>> squares[2]()
16
>>> squares[4]()
16
_9

Objek yang ditunjuk oleh a_list telah dimutasi, dan penunjuk ke objek yang dimutasi ditugaskan kembali ke

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
13. Hasil akhir dari penugasan adalah no-op, karena ini adalah penunjuk ke objek yang sama yang sebelumnya ditunjuk oleh
>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
13, tetapi penugasan tetap terjadi

Jadi, dalam contoh tuple kita apa yang terjadi setara dengan

>>> x = 8
>>> squares[2]()
64
_0

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
44 berhasil, dan dengan demikian daftar diperpanjang, tetapi meskipun
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
53 menunjuk ke objek yang sama yang
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
39 sudah menunjuk ke, tugas akhir itu masih menghasilkan kesalahan, karena tupel tidak dapat diubah

Teknik tersebut, dikaitkan dengan Randal Schwartz dari komunitas Perl, mengurutkan elemen daftar dengan metrik yang memetakan setiap elemen ke "nilai pengurutan" -nya. Di Python, gunakan argumen

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_55 untuk metode ini

>>> x = 8
>>> squares[2]()
64
_1

Gabungkan mereka menjadi iterator tupel, urutkan daftar yang dihasilkan, lalu pilih elemen yang Anda inginkan

>>> x = 8
>>> squares[2]()
64
_2

Kelas adalah tipe objek tertentu yang dibuat dengan mengeksekusi pernyataan kelas. Objek kelas digunakan sebagai templat untuk membuat objek instan, yang mewujudkan data (atribut) dan kode (metode) khusus untuk tipe data

Kelas dapat didasarkan pada satu atau lebih kelas lain, yang disebut kelas dasarnya (es). Itu kemudian mewarisi atribut dan metode kelas dasarnya. Ini memungkinkan model objek untuk disempurnakan secara berturut-turut dengan pewarisan. Anda mungkin memiliki kelas

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
57 generik yang menyediakan metode pengakses dasar untuk kotak surat, dan subkelas seperti
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
58,
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
59,
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
60 yang menangani berbagai format kotak surat tertentu

Metode adalah fungsi pada beberapa objek

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_24 yang biasanya Anda panggil sebagai
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
62. Metode didefinisikan sebagai fungsi di dalam definisi kelas

>>> x = 8
>>> squares[2]()
64
_3

Diri hanyalah sebuah nama konvensional untuk argumen pertama dari sebuah metode. Sebuah metode didefinisikan sebagai

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
63 harus disebut sebagai
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
64 untuk beberapa contoh
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24 dari kelas di mana definisi terjadi;

Lihat juga

Gunakan fungsi bawaan. Anda dapat memeriksa apakah suatu objek adalah turunan dari sejumlah kelas dengan menyediakan tuple alih-alih satu kelas, mis. g.

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_68, dan juga dapat memeriksa apakah suatu objek adalah salah satu tipe bawaan Python, e. g.
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_69 atau
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
70

Perhatikan bahwa juga memeriksa warisan virtual dari sebuah. Jadi, tes akan mengembalikan

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_72 untuk kelas terdaftar meskipun tidak diwarisi secara langsung atau tidak langsung darinya. Untuk menguji "warisan sebenarnya", pindai kelas

>>> x = 8
>>> squares[2]()
64
_4

>>> x = 8
>>> squares[2]()
64
_5

Perhatikan bahwa sebagian besar program tidak terlalu sering digunakan pada kelas yang ditentukan pengguna. Jika Anda mengembangkan kelas sendiri, gaya berorientasi objek yang lebih tepat adalah mendefinisikan metode pada kelas yang merangkum perilaku tertentu, alih-alih memeriksa kelas objek dan melakukan hal yang berbeda berdasarkan kelas apa itu. Misalnya, jika Anda memiliki fungsi yang melakukan sesuatu

>>> x = 8
>>> squares[2]()
64
_6

Pendekatan yang lebih baik adalah dengan mendefinisikan metode

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_74 di semua kelas dan sebut saja

>>> x = 8
>>> squares[2]()
64
_7

Delegasi adalah teknik berorientasi objek (juga disebut pola desain). Katakanlah Anda memiliki objek

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_24 dan ingin mengubah perilaku salah satu metode saja. Anda dapat membuat kelas baru yang menyediakan implementasi baru dari metode yang ingin Anda ubah dan mendelegasikan semua metode lain ke metode yang sesuai dari
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
24

Pemrogram Python dapat dengan mudah mengimplementasikan delegasi. Misalnya, kelas berikut mengimplementasikan kelas yang berperilaku seperti file tetapi mengubah semua data tertulis menjadi huruf besar

>>> x = 8
>>> squares[2]()
64
_8

Di sini kelas

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_77 mengubah metode
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
78 untuk mengonversi string argumen menjadi huruf besar sebelum memanggil metode
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
79 yang mendasarinya. Semua metode lain didelegasikan ke objek
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
80 yang mendasarinya. Pendelegasian dilakukan melalui metode;

Perhatikan bahwa untuk kasus yang lebih umum, pendelegasian bisa menjadi lebih rumit. Ketika atribut harus disetel dan juga diambil, kelas juga harus menentukan metode, dan harus melakukannya dengan hati-hati. Implementasi dasar

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_82 kira-kira setara dengan yang berikut ini

>>> x = 8
>>> squares[2]()
64
_9

Sebagian besar implementasi

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_82 harus dimodifikasi untuk menyimpan status lokal untuk dirinya sendiri tanpa menyebabkan rekursi tak terbatas

Gunakan fungsi bawaan

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
0

Dalam contoh, akan secara otomatis menentukan instance dari mana ia dipanggil (nilai

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
88), mencari (MRO) dengan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
89, dan mengembalikan baris berikutnya setelah
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
90 di MRO.
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_91

Anda bisa menetapkan kelas dasar ke alias dan berasal dari alias. Maka yang harus Anda ubah hanyalah nilai yang diberikan ke alias. Kebetulan, trik ini juga berguna jika Anda ingin memutuskan secara dinamis (mis. g. tergantung pada ketersediaan sumber daya) kelas dasar mana yang akan digunakan. Contoh

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
1

Baik data statis maupun metode statis (dalam pengertian C++ atau Java) didukung dengan Python

Untuk data statis, cukup tentukan atribut kelas. Untuk menetapkan nilai baru ke atribut, Anda harus menggunakan nama kelas secara eksplisit dalam penetapan

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
2

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_92 juga mengacu pada
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
93 untuk
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
94 seperti yang
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
95 berlaku, kecuali diganti oleh
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
94 itu sendiri atau oleh beberapa kelas di jalur pencarian kelas dasar dari
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
97 kembali ke
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
98

Hati-hati. dalam metode C, penugasan seperti

>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
_99 membuat instance baru dan tidak terkait bernama "menghitung" di dict
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
88 sendiri. Pengikatan ulang nama data statis kelas harus selalu menentukan kelas apakah di dalam metode atau tidak

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
3

Metode statis dimungkinkan

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
4

Namun, cara yang jauh lebih mudah untuk mendapatkan efek dari metode statis adalah melalui fungsi tingkat modul yang sederhana

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
5

Jika kode Anda terstruktur untuk mendefinisikan satu kelas (atau hierarki kelas yang terkait erat) per modul, ini menyediakan enkapsulasi yang diinginkan

Jawaban ini sebenarnya berlaku untuk semua metode, tetapi pertanyaan biasanya muncul pertama kali dalam konteks konstruktor

Di C++ Anda akan menulis

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
6

Di Python Anda harus menulis satu konstruktor yang menangkap semua kasus menggunakan argumen default. Sebagai contoh

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
7

Ini tidak sepenuhnya setara, tetapi cukup dekat dalam praktiknya

Anda juga bisa mencoba daftar argumen dengan panjang variabel, mis. g

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
8

Pendekatan yang sama berlaku untuk semua definisi metode

Nama variabel dengan garis bawah ganda di depan "hancur" untuk menyediakan cara sederhana namun efektif untuk mendefinisikan variabel privat kelas. Pengidentifikasi apa pun dalam bentuk

>>> print(x)
11
01 (setidaknya dua garis bawah di depan, paling banyak satu garis bawah di belakang) secara tekstual diganti dengan
>>> print(x)
11
02, di mana
>>> print(x)
11
03 adalah nama kelas saat ini dengan garis bawah di depan yang dihilangkan

Ini tidak menjamin privasi. pengguna luar masih dapat dengan sengaja mengakses atribut "_classname__spam", dan nilai pribadi terlihat di

>>> print(x)
11
04 objek. Banyak programmer Python tidak pernah repot-repot menggunakan nama variabel pribadi sama sekali

Ada beberapa kemungkinan alasan untuk ini

Pernyataan itu tidak harus memanggil - itu hanya mengurangi jumlah referensi objek, dan jika ini mencapai nol

>>> print(x)
11
06 disebut

Jika struktur data Anda berisi tautan melingkar (mis. g. pohon di mana setiap anak memiliki referensi orang tua dan setiap orang tua memiliki daftar anak-anak) jumlah referensi tidak akan pernah kembali ke nol. Sesekali Python menjalankan algoritme untuk mendeteksi siklus seperti itu, tetapi pengumpul sampah mungkin berjalan beberapa saat setelah referensi terakhir ke struktur data Anda hilang, sehingga metode

>>> print(x)
11
06 Anda dapat dipanggil pada waktu yang tidak nyaman dan acak. Ini merepotkan jika Anda mencoba mereproduksi masalah. Lebih buruk lagi, urutan eksekusi metode ________7______06 objek bersifat arbitrer. Anda dapat menjalankan untuk memaksa pengumpulan, tetapi ada kasus patologis di mana objek tidak akan pernah dikumpulkan

Terlepas dari pengumpul siklus, masih merupakan ide bagus untuk mendefinisikan metode

>>> print(x)
11
11 eksplisit pada objek yang akan dipanggil setiap kali Anda selesai menggunakannya. Metode
>>> print(x)
11
_11 kemudian dapat menghapus atribut yang merujuk ke subobjek. Jangan panggil
>>> print(x)
11
_06 secara langsung –
>>> print(x)
11
06 harus menelepon
>>> print(x)
11
11 dan
>>> print(x)
11
11 harus memastikan bahwa itu dapat dipanggil lebih dari sekali untuk objek yang sama

Cara lain untuk menghindari referensi siklis adalah dengan menggunakan modul, yang memungkinkan Anda menunjuk ke objek tanpa menambah jumlah referensinya. Struktur data pohon, misalnya, harus menggunakan referensi yang lemah untuk referensi orang tua dan saudara kandungnya (jika mereka membutuhkannya. )

Terakhir, jika metode

>>> print(x)
11
_06 Anda memunculkan pengecualian, pesan peringatan akan dicetak ke

Python tidak melacak semua contoh kelas (atau tipe bawaan). Anda dapat memprogram konstruktor kelas untuk melacak semua instance dengan menyimpan daftar referensi yang lemah untuk setiap instance

Builtin mengembalikan bilangan bulat yang dijamin unik selama masa pakai objek. Karena di CPython, ini adalah alamat memori objek, sering terjadi setelah objek dihapus dari memori, objek yang baru dibuat berikutnya dialokasikan pada posisi yang sama di memori. Ini diilustrasikan oleh contoh ini

>>> squares = []
>>> for x in range(5):
..     squares.append(lambda n=x: n**2)
_9

Kedua id milik objek bilangan bulat berbeda yang dibuat sebelumnya, dan dihapus segera setelah eksekusi panggilan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
19. Untuk memastikan objek yang id-nya ingin Anda periksa masih hidup, buat referensi lain ke objek tersebut

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_00

Operator

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment
_22 menguji identitas objek. Tes
>>> print(x)
11
_24 setara dengan
>>> print(x)
11
25

Properti paling penting dari tes identitas adalah bahwa objek selalu identik dengan dirinya sendiri,

>>> print(x)
11
26 selalu mengembalikan
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
72. Tes identitas biasanya lebih cepat daripada tes kesetaraan. Dan tidak seperti tes kesetaraan, tes identitas dijamin untuk mengembalikan boolean
>>> x = 10
>>> def foobar():
..     global x
..     print(x)
..     x += 1
...
>>> foobar()
10
72 atau
>>> print(x)
11
29

Namun, tes identitas hanya dapat diganti dengan tes kesetaraan ketika identitas objek terjamin. Secara umum, ada tiga keadaan di mana identitas dijamin

1) Tugas membuat nama baru tetapi tidak mengubah identitas objek. Setelah penugasan

>>> print(x)
11
_30, dijamin bahwa
>>> print(x)
11
31

2) Memasukkan objek ke dalam wadah yang menyimpan referensi objek tidak mengubah identitas objek. Setelah penugasan daftar

>>> print(x)
11
_32, dijamin bahwa
>>> print(x)
11
33

3) Jika suatu objek adalah singleton, itu berarti hanya ada satu instance dari objek itu. Setelah penugasan

>>> print(x)
11
34 dan
>>> print(x)
11
35, dijamin bahwa
>>> print(x)
11
24 karena
>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65 adalah singleton

Dalam sebagian besar keadaan lain, tes identitas tidak disarankan dan tes kesetaraan lebih disukai. Khususnya, uji identitas tidak boleh digunakan untuk memeriksa konstanta seperti dan yang tidak dijamin sebagai lajang

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_01

Demikian pula, instance baru dari wadah yang dapat berubah tidak pernah identik

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_02

Dalam kode pustaka standar, Anda akan melihat beberapa pola umum untuk menggunakan pengujian identitas dengan benar

1) Seperti yang direkomendasikan oleh PEP 8, tes identitas adalah cara yang lebih disukai untuk memeriksa

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65. Ini berbunyi seperti bahasa Inggris biasa dalam kode dan menghindari kebingungan dengan objek lain yang mungkin memiliki nilai boolean yang bernilai salah

2) Mendeteksi argumen opsional bisa jadi rumit ketika

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
65 adalah nilai input yang valid. Dalam situasi tersebut, Anda dapat membuat objek sentinel tunggal yang dijamin berbeda dari objek lainnya. Misalnya, berikut adalah cara mengimplementasikan metode yang berperilaku seperti

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_03

3) Implementasi wadah kadang-kadang perlu menambah tes kesetaraan dengan tes identitas. Ini mencegah kode menjadi bingung oleh objek seperti

>>> print(x)
11
43 yang tidak sama dengan dirinya sendiri

Sebagai contoh, inilah penerapan

>>> print(x)
11
_44

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_04

Saat mensubklasifikasikan tipe yang tidak dapat diubah, timpa metode alih-alih metode. Yang terakhir hanya berjalan setelah instance dibuat, yang sudah terlambat untuk mengubah data dalam instance yang tidak dapat diubah

Semua kelas yang tidak dapat diubah ini memiliki tanda tangan yang berbeda dari kelas induknya

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_05

Kelas dapat digunakan seperti ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_06

Dua alat utama untuk metode caching adalah dan. Yang pertama menyimpan hasil di tingkat instance dan yang terakhir di tingkat kelas

Pendekatan cached_property hanya berfungsi dengan metode yang tidak menggunakan argumen apa pun. Itu tidak membuat referensi ke instance. Hasil metode yang di-cache hanya akan disimpan selama instance masih hidup

Keuntungannya adalah ketika sebuah instance tidak lagi digunakan, hasil metode yang di-cache akan segera dirilis. Kerugiannya adalah jika instans terakumulasi, metode yang terakumulasi juga akan dihasilkan. Mereka bisa tumbuh tanpa terikat

Pendekatan lru_cache bekerja dengan metode yang memiliki argumen hashable. Itu membuat referensi ke instance kecuali upaya khusus dilakukan untuk meneruskan referensi yang lemah

Keuntungan dari algoritme yang paling jarang digunakan adalah bahwa cache dibatasi oleh ukuran maksimum yang ditentukan. Kerugiannya adalah instans tetap hidup sampai mereka menua dari cache atau sampai cache dibersihkan

Contoh ini menunjukkan berbagai teknik

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_07

Contoh di atas mengasumsikan bahwa station_id tidak pernah berubah. Jika atribut instance yang relevan dapat diubah, pendekatan cached_property tidak dapat berfungsi karena tidak dapat mendeteksi perubahan pada atribut

Agar pendekatan lru_cache berfungsi ketika station_id dapat diubah, kelas perlu menentukan metode and sehingga cache dapat mendeteksi pembaruan atribut yang relevan

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_08

Saat modul diimpor untuk pertama kali (atau saat file sumber telah berubah sejak file kompilasi saat ini dibuat) file

>>> print(x)
11
51 yang berisi kode yang dikompilasi harus dibuat di subdirektori
>>> print(x)
11
52 dari direktori yang berisi file
>>> print(x)
11
53. File
>>> print(x)
11
_51 akan memiliki nama file yang dimulai dengan nama yang sama dengan file
>>> print(x)
11
53, dan diakhiri dengan
>>> print(x)
11
51, dengan komponen tengah yang bergantung pada biner
>>> print(x)
11
57 tertentu yang membuatnya. (Lihat PEP 3147 untuk detailnya. )

Salah satu alasan mengapa file

>>> print(x)
11
_51 tidak dapat dibuat adalah masalah izin dengan direktori yang berisi file sumber, artinya subdirektori
>>> print(x)
11
52 tidak dapat dibuat. Ini dapat terjadi, misalnya, jika Anda mengembangkan sebagai satu pengguna tetapi menjalankan sebagai pengguna lain, seperti jika Anda menguji dengan server web

Kecuali jika variabel lingkungan disetel, pembuatan a. pyc otomatis jika Anda mengimpor modul dan Python memiliki kemampuan (izin, ruang kosong, dll...) untuk membuat subdirektori

>>> print(x)
11
52 dan menulis modul yang dikompilasi ke subdirektori tersebut

Menjalankan Python pada skrip tingkat atas tidak dianggap sebagai impor dan tidak ada

>>> print(x)
11
51 yang akan dibuat. Misalnya, jika Anda memiliki modul tingkat atas
>>> print(x)
11
63 yang mengimpor modul lain
>>> print(x)
11
64, saat Anda menjalankan
>>> print(x)
11
65 (dengan mengetikkan
>>> print(x)
11
66 sebagai perintah shell),
>>> print(x)
11
51 akan dibuat untuk
>>> print(x)
11
68 karena
>>> print(x)
11
68 diimpor, tetapi tidak ada file ______57

Jika Anda perlu membuat file

>>> print(x)
11
_51 untuk
>>> print(x)
11
65 – yaitu, membuat file
>>> print(x)
11
51 untuk modul yang tidak diimpor – Anda dapat menggunakan the and modules

Modul dapat mengkompilasi modul apa pun secara manual. Salah satu caranya adalah dengan menggunakan fungsi

>>> print(x)
11
79 dalam modul itu secara interaktif

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_09

Ini akan menulis

>>> print(x)
11
51 ke
>>> print(x)
11
52 subdirektori di lokasi yang sama dengan
>>> print(x)
11
63 (atau Anda dapat menimpanya dengan parameter opsional
>>> print(x)
11
83)

Anda juga dapat secara otomatis mengkompilasi semua file dalam direktori atau direktori menggunakan modul. Anda dapat melakukannya dari prompt shell dengan menjalankan

>>> print(x)
11
85 dan memberikan jalur direktori yang berisi file Python untuk dikompilasi

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_10

Sebuah modul dapat mengetahui nama modulnya sendiri dengan melihat variabel global yang telah ditentukan sebelumnya

>>> print(x)
11
86. Jika ini memiliki nilai
>>> print(x)
11
_87, program dijalankan sebagai skrip. Banyak modul yang biasanya digunakan dengan mengimpornya juga menyediakan antarmuka baris perintah atau swa-uji, dan hanya mengeksekusi kode ini setelah memeriksa
>>> print(x)
11
86

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_11

Misalkan Anda memiliki modul berikut

>>> print(x)
11
_63

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_12

>>> print(x)
11
_90

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_13

Masalahnya adalah juru bahasa akan melakukan langkah-langkah berikut

  • impor utama

    >>> print(x)
    11
    
    _65

  • Global kosong untuk

    >>> print(x)
    11
    
    _65 telah dibuat

  • >>> print(x)
    11
    
    65 dikompilasi dan mulai dijalankan

  • >>> print(x)
    11
    
    65 impor
    >>> print(x)
    11
    
    95

  • Global kosong untuk

    >>> print(x)
    11
    
    _95 telah dibuat

  • >>> print(x)
    11
    
    95 dikompilasi dan mulai dijalankan

  • >>> print(x)
    11
    
    95 mengimpor
    >>> print(x)
    11
    
    65 (yang merupakan no-op karena sudah ada modul bernama
    >>> print(x)
    11
    
    65)

  • Mekanisme impor mencoba membaca

    >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    _01 dari
    >>> print(x)
    11
    
    65 global, untuk mengatur
    >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    03

Langkah terakhir gagal, karena Python belum selesai menafsirkan

>>> print(x)
11
65 dan kamus simbol global untuk
>>> print(x)
11
65 masih kosong

Hal yang sama terjadi ketika Anda menggunakan

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
_06, lalu mencoba mengakses
>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
07 dalam kode global

Ada (setidaknya) tiga kemungkinan solusi untuk masalah ini

Guido van Rossum merekomendasikan untuk menghindari semua penggunaan

>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
08, dan menempatkan semua kode di dalam fungsi. Inisialisasi variabel global dan variabel kelas harus menggunakan konstanta atau fungsi bawaan saja. Ini berarti segala sesuatu dari modul yang diimpor direferensikan sebagai
>>> def foo():
..    x = 10
..    def bar():
..        nonlocal x
..        print(x)
..        x += 1
..    bar()
..    print(x)
...
>>> foo()
10
11
09

Jim Roskind menyarankan melakukan langkah-langkah dalam urutan berikut di setiap modul

  • ekspor (global, fungsi, dan kelas yang tidak memerlukan kelas dasar yang diimpor)

  • >>> def foo():
    ..    x = 10
    ..    def bar():
    ..        nonlocal x
    ..        print(x)
    ..        x += 1
    ..    bar()
    ..    print(x)
    ...
    >>> foo()
    10
    11
    
    _10 pernyataan

  • kode aktif (termasuk global yang diinisialisasi dari nilai yang diimpor)

Van Rossum tidak terlalu menyukai pendekatan ini karena impor muncul di tempat yang aneh, tetapi berhasil

Matthias Urlichs merekomendasikan restrukturisasi kode Anda sehingga impor rekursif tidak diperlukan sejak awal

Solusi ini tidak saling eksklusif

Pertimbangkan untuk menggunakan fungsi kenyamanan dari sebagai gantinya

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_14

Untuk alasan efisiensi dan juga konsistensi, Python hanya membaca file modul saat pertama kali modul diimpor. Jika tidak, dalam program yang terdiri dari banyak modul di mana masing-masing mengimpor modul dasar yang sama, modul dasar akan diurai dan diurai berkali-kali. Untuk memaksa membaca ulang modul yang diubah, lakukan ini

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_15

Peringatan. teknik ini tidak 100% anti-bodoh. Secara khusus, modul yang berisi pernyataan seperti

>>> x = 10
>>> def foo():
..     print(x)
..     x += 1
_16

akan terus bekerja dengan versi lama dari objek yang diimpor. Jika modul berisi definisi kelas, instance kelas yang ada tidak akan diperbarui untuk menggunakan definisi kelas yang baru. Hal ini dapat mengakibatkan perilaku paradoks berikut

Bagaimana Anda mengubah variabel dalam satu lingkaran dengan Python?

Untuk mengubah nama variabel dalam satu lingkaran dengan Python, Anda cukup menugaskan ulang nilai baru ke variabel . Sebagai contoh. untuk saya dalam jangkauan(10). x = saya.

Bisakah Anda mengubah variabel di while loop Python?

Tidak seperti pernyataan if, kondisi dalam perulangan while pada akhirnya harus menjadi False. Jika ini tidak terjadi, while loop akan terus berjalan selamanya. Cara terbaik untuk mengubah kondisi dari True ke False adalah dengan menggunakan variabel sebagai bagian dari ekspresi Boolean. Kita kemudian dapat mengubah variabel di dalam while loop .

Bisakah saya memodifikasi variabel loop?

Gunakan hanya ekspresi kenaikan dalam perulangan for untuk memodifikasi variabel perulangan , atau ubah ke perulangan while jika perulangan memerlukan iterasi variabel yang lebih rumit.

Apa yang dilakukan += dalam perulangan for?

Operator penugasan tambahan ( += ) menambahkan nilai operan kanan ke variabel dan menetapkan hasilnya ke variabel .