Di bagian
sebelumnya, kami melihat irisan, membongkar / mengemas koleksi, dan beberapa fitur operasi dan tipe Boolean.
Komentar menyebutkan kemungkinan mengalikan koleksi dengan skalar:
a = [0] * 3 s = 'a' * 2 print(a, s)
Pengembang python yang kurang lebih berpengalaman tahu bahwa ia tidak memiliki mekanisme
penyalinan saat menulis a = [0] b = a b[0] = 1 print(a, b)
Lalu apa yang akan menampilkan kode berikut?
b = a * 2 b[0] = 2 print(a, b)
Python dalam kasus ini bekerja berdasarkan prinsip paling tidak mengejutkan: dalam variabel a kita memiliki satu unit, yaitu, b dapat dideklarasikan dan bagaimana
b = [1] * 2
Perilaku dalam kasus ini akan sama:
b = a * 2 b[0] = 2 print(a, b)
Tapi tidak sesederhana itu, python menyalin isi daftar sebanyak yang Anda kalikan dan membangun daftar baru. Dan dalam daftar tautan ke nilai disimpan, dan jika, ketika menyalin referensi ke tipe yang tidak dapat diubah dengan cara ini, semuanya baik-baik saja, tetapi dengan bisa berubah efek tidak adanya penyalinan selama penulisan keluar.
row = [0] * 2 matrix = [row] * 2 print(matrix)
Daftar generator dan numpy untuk membantu Anda dalam hal ini.
Daftar dapat ditambahkan dan bahkan ditambahkan, dan setiap iterator dapat di sebelah kanan:
a = [0] a += (1,) a += {2} a += "ab" a += {1: 2} print(a)
Pertanyaan dengan trik (untuk wawancara): dalam python, parameter dilewatkan oleh referensi atau berdasarkan nilai?
def inc(a): a += 1 return a a = 5 print(inc(a)) print(a)
Seperti yang kita lihat, perubahan nilai dalam fungsi tubuh tidak mengubah nilai objek di luarnya, dari sini kita dapat menyimpulkan bahwa parameter dilewatkan oleh nilai dan menulis kode berikut:
def appended(a): a += [1] return a a = [5] print(appended(a))
Bahasa seperti C ++ memiliki variabel yang disimpan di stack dan di memori dinamis. Ketika fungsi dipanggil, kami menempatkan semua argumen di stack, setelah itu kami mentransfer kontrol ke fungsi. Dia tahu ukuran dan offset variabel pada stack, sehingga dia bisa menafsirkannya dengan benar.
Pada saat yang sama, kami memiliki dua opsi: menyalin memori variabel ke stack atau meletakkan referensi ke objek dalam memori dinamis (atau pada level stack yang lebih tinggi).
Jelas, ketika mengubah nilai-nilai pada tumpukan fungsi, nilai-nilai dalam memori dinamis tidak akan berubah, tetapi ketika mengubah area memori dengan referensi, masing-masing kami memodifikasi memori bersama, semua tautan ke area memori yang sama akan "melihat" nilai baru.
Dalam python, mereka meninggalkan mekanisme yang sama, penggantian adalah mekanisme penugasan nama variabel dengan objek, misalnya, saat membuat variabel:
var = "john"
Penerjemah menciptakan objek "john" dan "nama" var, dan kemudian mengaitkan objek dengan nama yang diberikan.
Ketika suatu fungsi dipanggil, tidak ada objek baru yang dibuat; sebaliknya, sebuah nama dibuat dalam ruang lingkupnya yang dikaitkan dengan objek yang ada.
Tapi python memiliki tipe yang bisa berubah dan tidak berubah. Yang kedua, misalnya, termasuk angka: selama operasi aritmatika, objek yang ada tidak berubah, tetapi objek baru dibuat, yang dengannya nama yang ada kemudian dikaitkan. Jika, setelah ini, tidak ada satu nama pun yang dikaitkan dengan objek yang lama, itu akan dihapus menggunakan mekanisme penghitungan tautan.
Jika nama dikaitkan dengan variabel tipe variabel, maka selama operasi dengan itu memori objek berubah, sehingga semua nama yang terkait dengan area memori ini βmelihatβ perubahan.
Anda dapat membacanya di
dokumentasi , dijelaskan secara lebih rinci di
sini .
Contoh lain:
a = [1, 2, 3, 4, 5] def rev(l): l.reverse() return l l = a print(a, l)
Tetapi bagaimana jika kita memutuskan untuk mengubah variabel di luar fungsi? Dalam hal ini, pengubah global akan membantu kami:
def change(): global a a += 1 a = 5 change() print(a)
Catatan: jangan lakukan ini (tidak, serius, jangan gunakan variabel global dalam program Anda, dan terutama tidak di Anda sendiri). Lebih baik mengembalikan beberapa nilai dari fungsi:
def func(a, b): return a + 1, b + 1
Namun, dalam python ada ruang lingkup lain dan kata kunci yang sesuai:
def private(value=None): def getter(): return value def setter(v): nonlocal value value = v return getter, setter vget, vset = private(42) print(vget())
Dalam contoh ini, kami membuat variabel yang dapat diubah (dan yang nilainya diperoleh) hanya melalui metode, Anda dapat menggunakan mekanisme serupa di kelas:
def private(value=None): def getter(): return value def setter(v): nonlocal value value = v return getter, setter class Person: def __init__(self, name): self.getid, self.setid = private(name) adam = Person("adam") print(adam.getid()) print(adam.setid("john")) print(adam.getid()) print(dir(adam))
Tetapi mungkin akan lebih baik untuk membatasi diri kita pada
sifat -
sifat atau definisi
__getattr__ ,
__setattr__ .
Anda bahkan dapat mendefinisikan
__delattr__ .
Fitur lain dari python adalah adanya dua metode untuk mendapatkan atribut: __getattr__ dan
__getattribute__ .
Apa perbedaan di antara mereka? Yang pertama dipanggil hanya jika atribut di kelas tidak ditemukan, dan yang kedua adalah tanpa syarat. Jika keduanya dideklarasikan di kelas, maka __getattr__ dipanggil hanya jika secara eksplisit disebut dalam __getattribute__ atau jika __getattribute__ mengangkat AttributeError.
class Person(): def __getattr__(self, item): print("__getattr__") if item == "name": return "john" raise AttributeError def __getattribute__(self, item): print("__getattribute__") raise AttributeError person = Person() print(person.name)
Dan terakhir, contoh bagaimana python bebas memanipulasi variabel dan cakupan:
e = 42 try: 1 / 0 except Exception as e: pass print(e)
Ngomong-ngomong, ini mungkin satu-satunya contoh di mana python kedua lebih baik dari yang ketiga, karena menghasilkan:
... print(e)