Menjelajahi dan memahami Python melalui cuplikan yang mengejutkan.
Terjemahan: Cina 中文 | Tiếng Việt Vietnam | Spanyol Spanyol | Korea 한국어 | Rusia Русский | Jerman Jerman | Tambahkan terjemahan
Mode lainnya: Situs Web Interaktif | Buku Catatan Interaktif
Python, sebagai bahasa pemrograman tingkat tinggi dan berbasis interpreter yang dirancang dengan indah, memberi kita banyak fitur untuk kenyamanan programmer. Namun terkadang, hasil cuplikan Python mungkin tidak terlihat jelas pada pandangan pertama.
Inilah proyek menyenangkan yang mencoba menjelaskan apa yang sebenarnya terjadi untuk beberapa cuplikan kontra-intuitif dan fitur yang kurang dikenal di Python.
Meskipun beberapa contoh yang Anda lihat di bawah mungkin bukan WTF dalam arti sebenarnya, namun contoh tersebut akan mengungkapkan beberapa bagian menarik dari Python yang mungkin tidak Anda sadari. Menurut saya ini cara yang bagus untuk mempelajari bagian dalam bahasa pemrograman, dan saya yakin Anda juga akan menganggapnya menarik!
Jika Anda seorang pemrogram Python berpengalaman, Anda dapat menganggapnya sebagai tantangan untuk menyelesaikan sebagian besar dari semuanya dengan benar pada percobaan pertama. Anda mungkin pernah mengalami beberapa di antaranya sebelumnya, dan saya mungkin bisa menghidupkan kembali kenangan manis lama Anda! ?
PS: Jika Anda adalah pembaca kembali, Anda dapat mempelajari modifikasi baru di sini (contoh yang ditandai dengan asterisk adalah yang ditambahkan pada revisi besar terbaru).
Jadi, ini dia...
is
is not ...
tidak is (not ...)
del
yang keras kepalagoto
, tapi kenapa?+=
lebih cepatdict
*dict
s *Semua contoh disusun seperti di bawah ini:
▶ Judul yang mewah
# Set up the code. # Preparation for the magic...Keluaran (versi Python):
> >> triggering_statement Some unexpected output(Opsional): Satu baris yang menjelaskan keluaran yang tidak terduga.
Penjelasan:
- Penjelasan singkat tentang apa yang terjadi dan mengapa hal itu terjadi.
# Set up code # More examples for further clarification (if necessary)Keluaran (versi Python):
> >> trigger # some example that makes it easy to unveil the magic # some justified output
Catatan: Semua contoh diuji pada penerjemah interaktif Python 3.5.2, dan contoh tersebut harus berfungsi untuk semua versi Python kecuali ditentukan secara eksplisit sebelum keluaran.
Menurut pendapat saya, cara terbaik untuk mendapatkan hasil maksimal dari contoh-contoh ini adalah dengan membacanya secara berurutan, dan untuk setiap contoh:
Untuk beberapa alasan, operator "Walrus" Python 3.8 ( :=
) telah menjadi cukup populer. Mari kita periksa,
1.
# Python version 3.8+
> >> a = "wtf_walrus"
> >> a
'wtf_walrus'
> >> a := "wtf_walrus"
File "<stdin>" , line 1
a := "wtf_walrus"
^
SyntaxError : invalid syntax
>> > ( a := "wtf_walrus" ) # This works though
'wtf_walrus'
> >> a
'wtf_walrus'
2.
# Python version 3.8+
> >> a = 6 , 9
> >> a
( 6 , 9 )
> >> ( a := 6 , 9 )
( 6 , 9 )
> >> a
6
> >> a , b = 6 , 9 # Typical unpacking
> >> a , b
( 6 , 9 )
>> > ( a , b = 16 , 19 ) # Oops
File "<stdin>" , line 1
( a , b = 16 , 19 )
^
SyntaxError : invalid syntax
>> > ( a , b := 16 , 19 ) # This prints out a weird 3-tuple
( 6 , 16 , 19 )
>> > a # a is still unchanged?
6
>> > b
16
Penyegaran operator walrus yang cepat
Operator Walrus ( :=
) diperkenalkan di Python 3.8, ini dapat berguna dalam situasi di mana Anda ingin memberikan nilai ke variabel dalam ekspresi.
def some_func ():
# Assume some expensive computation here
# time.sleep(1000)
return 5
# So instead of,
if some_func ():
print ( some_func ()) # Which is bad practice since computation is happening twice
# or
a = some_func ()
if a :
print ( a )
# Now you can concisely write
if a := some_func ():
print ( a )
Keluaran (> 3.8):
5
5
5
Ini menghemat satu baris kode, dan secara implisit mencegah pemanggilan some_func
dua kali.
"Ekspresi penugasan" yang tidak diberi tanda kurung (penggunaan operator walrus), dibatasi di tingkat atas, oleh karena itu SyntaxError
dalam pernyataan a := "wtf_walrus"
pada cuplikan pertama. Mengurungnya berfungsi seperti yang diharapkan dan menetapkan a
.
Seperti biasa, pemberian tanda kurung pada ekspresi yang mengandung operator =
tidak diperbolehkan. Oleh karena itu kesalahan sintaksis di (a, b = 6, 9)
.
Sintaks operator Walrus berbentuk NAME:= expr
, dengan NAME
adalah pengidentifikasi yang valid, dan expr
adalah ekspresi yang valid. Oleh karena itu, pengepakan dan pembongkaran yang berulang tidak didukung yang berarti,
(a := 6, 9)
setara dengan ((a := 6), 9)
dan akhirnya (a, 9)
(di mana nilai a
adalah 6')
> >> ( a := 6 , 9 ) == (( a := 6 ), 9 )
True
> >> x = ( a := 696 , 9 )
> >> x
( 696 , 9 )
> >> x [ 0 ] is a # Both reference same memory location
True
Demikian pula, (a, b := 16, 19)
setara dengan (a, (b := 16), 19)
yang tidak lain hanyalah tupel 3.
1.
> >> a = "some_string"
> >> id ( a )
140420665652016
> >> id ( "some" + "_" + "string" ) # Notice that both the ids are same.
140420665652016
2.
> >> a = "wtf"
> >> b = "wtf"
> >> a is b
True
> >> a = "wtf!"
> >> b = "wtf!"
> >> a is b
False
3.
> >> a , b = "wtf!" , "wtf!"
> >> a is b # All versions except 3.7.x
True
> >> a = "wtf!" ; b = "wtf!"
> >> a is b # This will print True or False depending on where you're invoking it (python shell / ipython / as a script)
False
# This time in file some_file.py
a = "wtf!"
b = "wtf!"
print ( a is b )
# prints True when the module is invoked!
4.
Keluaran (< Python3.7 )
> >> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
> >> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
Masuk akal, bukan?
'wtf'
akan diinternir tetapi ''.join(['w', 't', 'f'])
tidak akan diinternir)'wtf!'
tidak diinternir karena !
. Implementasi CPython dari aturan ini dapat ditemukan di sini a
dan b
disetel ke "wtf!"
di baris yang sama, juru bahasa Python membuat objek baru, lalu mereferensikan variabel kedua secara bersamaan. Jika Anda melakukannya pada baris terpisah, ia tidak "tahu" bahwa sudah ada "wtf!"
sebagai objek (karena "wtf!"
tidak diinternir secara implisit sesuai fakta yang disebutkan di atas). Ini adalah optimasi waktu kompilasi. Pengoptimalan ini tidak berlaku untuk CPython versi 3.7.x (lihat masalah ini untuk diskusi lebih lanjut).a, b = "wtf!", "wtf!"
adalah pernyataan tunggal, sedangkan a = "wtf!"; b = "wtf!"
adalah dua pernyataan dalam satu baris. Ini menjelaskan mengapa identitasnya berbeda dalam a = "wtf!"; b = "wtf!"
, dan jelaskan juga mengapa keduanya sama ketika dipanggil di some_file.py
'a'*20
diganti dengan 'aaaaaaaaaaaaaaaaaaaa'
selama kompilasi untuk menghemat beberapa siklus jam selama runtime. Pelipatan konstan hanya terjadi untuk string yang panjangnya kurang dari 21. (Mengapa? Bayangkan ukuran file .pyc
yang dihasilkan sebagai hasil dari ekspresi 'a'*10**10
). Inilah sumber implementasinya. > >> ( False == False ) in [ False ] # makes sense
False
> >> False == ( False in [ False ]) # makes sense
False
> >> False == False in [ False ] # now what?
True
> >> True is False == False
False
> >> False is False is False
True
> >> 1 > 0 < 1
True
> >> ( 1 > 0 ) < 1
False
> >> 1 > ( 0 < 1 )
False
Sesuai https://docs.python.org/3/reference/expressions.html#comparisons
Secara formal, jika a, b, c, ..., y, z adalah ekspresi dan op1, op2, ..., opN adalah operator perbandingan, maka a op1 b op2 c ... y opN z setara dengan a op1 b dan b op2 c dan ... y opN z, kecuali setiap ekspresi dievaluasi paling banyak satu kali.
Meskipun perilaku seperti itu mungkin tampak konyol bagi Anda dalam contoh di atas, perilaku tersebut luar biasa dengan hal-hal seperti a == b == c
dan 0 <= x <= 100
.
False is False is False
setara dengan (False is False) and (False is False)
True is False == False
setara dengan (True is False) and (False == False)
dan karena bagian pertama pernyataan ( True is False
) bernilai False
, ekspresi keseluruhan bernilai False
.1 > 0 < 1
setara dengan (1 > 0) and (0 < 1)
yang bernilai True
.(1 > 0) < 1
setara dengan True < 1
dan > >> int ( True )
1
> >> True + 1 #not relevant for this example, but just for fun
2
1 < 1
bernilai False
is
Berikut ini adalah contoh yang sangat terkenal yang ada di internet.
1.
> >> a = 256
> >> b = 256
> >> a is b
True
> >> a = 257
> >> b = 257
> >> a is b
False
2.
> >> a = []
> >> b = []
> >> a is b
False
> >> a = tuple ()
> >> b = tuple ()
> >> a is b
True
3. Keluaran
> >> a , b = 257 , 257
> >> a is b
True
Keluaran (khusus Python 3.7.x)
> >> a , b = 257 , 257
> >> a is b
False
Perbedaan antara is
dan ==
is
memeriksa apakah kedua operan merujuk ke objek yang sama (yaitu memeriksa apakah identitas operan cocok atau tidak).==
membandingkan nilai kedua operan dan memeriksa apakah keduanya sama.is
juga untuk kesetaraan referensi dan ==
untuk kesetaraan nilai. Sebuah contoh untuk menjernihkan segalanya, > >> class A : pass
> >> A () is A () # These are two empty objects at two different memory locations.
False
256
adalah objek yang sudah ada tetapi 257
tidak
Saat Anda memulai python, angka dari -5
hingga 256
akan dialokasikan. Angka-angka ini sering digunakan, jadi masuk akal untuk menyiapkannya saja.
Mengutip dari https://docs.python.org/3/c-api/long.html
Implementasi saat ini menyimpan array objek bilangan bulat untuk semua bilangan bulat antara -5 dan 256, saat Anda membuat int dalam rentang tersebut, Anda baru saja mendapatkan kembali referensi ke objek yang ada. Jadi nilai 1 seharusnya bisa diubah. Saya menduga perilaku Python, dalam hal ini, tidak terdefinisi. :-)
> >> id ( 256 )
10922528
> >> a = 256
> >> b = 256
> >> id ( a )
10922528
> >> id ( b )
10922528
> >> id ( 257 )
140084850247312
> >> x = 257
> >> y = 257
> >> id ( x )
140084850247440
> >> id ( y )
140084850247344
Di sini penerjemah tidak cukup pintar saat mengeksekusi y = 257
untuk mengenali bahwa kita telah membuat bilangan bulat dengan nilai 257,
dan seterusnya membuat objek lain di memori.
Pengoptimalan serupa juga berlaku untuk objek abadi lainnya seperti tupel kosong. Karena daftar dapat diubah, itu sebabnya [] is []
akan mengembalikan False
dan () is ()
akan mengembalikan True
. Ini menjelaskan cuplikan kedua kami. Mari kita beralih ke yang ketiga,
Baik a
dan b
merujuk ke objek yang sama ketika diinisialisasi dengan nilai yang sama di baris yang sama.
Keluaran
> >> a , b = 257 , 257
> >> id ( a )
140640774013296
> >> id ( b )
140640774013296
> >> a = 257
> >> b = 257
> >> id ( a )
140640774013392
> >> id ( b )
140640774013488
Ketika a dan b disetel ke 257
pada baris yang sama, interpreter Python membuat objek baru, lalu mereferensikan variabel kedua secara bersamaan. Jika Anda melakukannya pada baris terpisah, ia tidak "mengetahui" bahwa sudah ada 257
sebagai objek.
Ini adalah optimasi kompiler dan secara khusus berlaku untuk lingkungan interaktif. Saat Anda memasukkan dua baris dalam penerjemah langsung, keduanya dikompilasi secara terpisah, oleh karena itu dioptimalkan secara terpisah. Jika Anda mencoba contoh ini dalam file .py
, Anda tidak akan melihat perilaku yang sama, karena file tersebut dikompilasi sekaligus. Pengoptimalan ini tidak terbatas pada bilangan bulat, ini berfungsi untuk tipe data abadi lainnya seperti string (periksa "String adalah contoh rumit") dan juga float,
> >> a , b = 257.0 , 257.0
> >> a is b
True
Mengapa ini tidak berhasil untuk Python 3.7? Alasan abstraknya adalah karena optimasi kompiler tersebut bersifat spesifik untuk implementasi (yaitu dapat berubah seiring dengan versi, OS, dll). Saya masih mencari tahu perubahan implementasi apa yang menyebabkan masalah ini, Anda dapat memeriksa masalah ini untuk mengetahui pembaruannya.
1.
some_dict = {}
some_dict [ 5.5 ] = "JavaScript"
some_dict [ 5.0 ] = "Ruby"
some_dict [ 5 ] = "Python"
Keluaran:
> >> some_dict [ 5.5 ]
"JavaScript"
> >> some_dict [ 5.0 ] # "Python" destroyed the existence of "Ruby"?
"Python"
> >> some_dict [ 5 ]
"Python"
> >> complex_five = 5 + 0j
> >> type ( complex_five )
complex
> >> some_dict [ complex_five ]
"Python"
Jadi mengapa Python ada dimana-mana?
Keunikan kunci dalam kamus Python terletak pada kesetaraannya , bukan identitasnya. Jadi meskipun 5
, 5.0
, dan 5 + 0j
adalah objek berbeda dengan tipe berbeda, karena keduanya sama, keduanya tidak bisa berada dalam dict
(atau set
) yang sama. Segera setelah Anda memasukkan salah satunya, upaya mencari kunci yang berbeda namun setara akan berhasil dengan nilai asli yang dipetakan (daripada gagal dengan KeyError
):
> >> 5 == 5.0 == 5 + 0j
True
> >> 5 is not 5.0 is not 5 + 0j
True
> >> some_dict = {}
> >> some_dict [ 5.0 ] = "Ruby"
> >> 5.0 in some_dict
True
> >> ( 5 in some_dict ) and ( 5 + 0j in some_dict )
True
Hal ini juga berlaku saat menyetel item. Jadi ketika Anda melakukan some_dict[5] = "Python"
, Python menemukan item yang ada dengan kunci yang setara 5.0 -> "Ruby"
, menimpa nilainya di tempatnya, dan membiarkan kunci aslinya.
> >> some_dict
{ 5.0 : 'Ruby' }
> >> some_dict [ 5 ] = "Python"
> >> some_dict
{ 5.0 : 'Python' }
Jadi bagaimana kita bisa memperbarui kunci ke 5
(bukannya 5.0
)? Kami sebenarnya tidak dapat melakukan pembaruan ini secara langsung, namun yang dapat kami lakukan adalah menghapus kuncinya terlebih dahulu ( del some_dict[5.0]
), lalu menyetelnya ( some_dict[5]
) untuk mendapatkan bilangan bulat 5
sebagai kunci, bukan mengambang 5.0
, meskipun hal ini diperlukan dalam kasus yang jarang terjadi.
Bagaimana Python menemukan 5
dalam kamus yang berisi 5.0
? Python melakukan ini dalam waktu yang konstan tanpa harus memindai setiap item dengan menggunakan fungsi hash. Ketika Python mencari kunci foo
dalam dict, pertama-tama ia menghitung hash(foo)
(yang berjalan dalam waktu konstan). Karena dalam Python diperlukan objek yang membandingkan sama juga memiliki nilai hash yang sama (dokumen di sini), 5
, 5.0
, dan 5 + 0j
memiliki nilai hash yang sama.
> >> 5 == 5.0 == 5 + 0j
True
> >> hash ( 5 ) == hash ( 5.0 ) == hash ( 5 + 0j )
True
Catatan: Kebalikannya belum tentu benar: Objek dengan nilai hash yang sama mungkin saja tidak sama. (Hal ini menyebabkan apa yang dikenal sebagai tabrakan hash, dan menurunkan kinerja waktu konstan yang biasanya disediakan oleh hashing.)
class WTF :
pass
Keluaran:
> >> WTF () == WTF () # two different instances can't be equal
False
> >> WTF () is WTF () # identities are also different
False
> >> hash ( WTF ()) == hash ( WTF ()) # hashes _should_ be different as well
True
> >> id ( WTF ()) == id ( WTF ())
True
Ketika id
dipanggil, Python membuat objek kelas WTF
dan meneruskannya ke fungsi id
. Fungsi id
mengambil id
(lokasi memorinya), dan membuang objeknya. Objeknya hancur.
Ketika kita melakukan ini dua kali berturut-turut, Python juga mengalokasikan lokasi memori yang sama ke objek kedua ini. Karena (dalam CPython) id
menggunakan lokasi memori sebagai id objek, id kedua objek tersebut sama.
Jadi, id objek tersebut unik hanya selama masa hidup objek tersebut. Setelah objek dihancurkan, atau sebelum dibuat, objek lain dapat memiliki id yang sama.
Tetapi mengapa operator is
mengevaluasi ke False
? Mari kita lihat dengan cuplikan ini.
class WTF ( object ):
def __init__ ( self ): print ( "I" )
def __del__ ( self ): print ( "D" )
Keluaran:
> >> WTF () is WTF ()
I
I
D
D
False
> >> id ( WTF ()) == id ( WTF ())
I
D
I
D
True
Seperti yang mungkin Anda amati, urutan penghancuran benda itulah yang membuat perbedaan besar di sini.
from collections import OrderedDict
dictionary = dict ()
dictionary [ 1 ] = 'a' ; dictionary [ 2 ] = 'b' ;
ordered_dict = OrderedDict ()
ordered_dict [ 1 ] = 'a' ; ordered_dict [ 2 ] = 'b' ;
another_ordered_dict = OrderedDict ()
another_ordered_dict [ 2 ] = 'b' ; another_ordered_dict [ 1 ] = 'a' ;
class DictWithHash ( dict ):
"""
A dict that also implements __hash__ magic.
"""
__hash__ = lambda self : 0
class OrderedDictWithHash ( OrderedDict ):
"""
An OrderedDict that also implements __hash__ magic.
"""
__hash__ = lambda self : 0
Keluaran
> >> dictionary == ordered_dict # If a == b
True
> >> dictionary == another_ordered_dict # and b == c
True
> >> ordered_dict == another_ordered_dict # then why isn't c == a ??
False
# We all know that a set consists of only unique elements,
# let's try making a set of these dictionaries and see what happens...
> >> len ({ dictionary , ordered_dict , another_ordered_dict })
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
TypeError : unhashable type : 'dict'
# Makes sense since dict don't have __hash__ implemented, let's use
# our wrapper classes.
> >> dictionary = DictWithHash ()
> >> dictionary [ 1 ] = 'a' ; dictionary [ 2 ] = 'b' ;
> >> ordered_dict = OrderedDictWithHash ()
> >> ordered_dict [ 1 ] = 'a' ; ordered_dict [ 2 ] = 'b' ;
> >> another_ordered_dict = OrderedDictWithHash ()
> >> another_ordered_dict [ 2 ] = 'b' ; another_ordered_dict [ 1 ] = 'a' ;
> >> len ({ dictionary , ordered_dict , another_ordered_dict })
1
> >> len ({ ordered_dict , another_ordered_dict , dictionary }) # changing the order
2
Apa yang terjadi di sini?
Alasan mengapa kesetaraan intransitif tidak berlaku di antara dictionary
, ordered_dict
dan another_ordered_dict
adalah karena cara metode __eq__
diimplementasikan di kelas OrderedDict
. Dari dokumen
Tes kesetaraan antara objek OrderedDict peka terhadap urutan dan diimplementasikan sebagai
list(od1.items())==list(od2.items())
. Tes kesetaraan antara objekOrderedDict
dan objek Pemetaan lainnya tidak peka terhadap urutan seperti kamus biasa.
Alasan persamaan perilaku ini adalah karena memungkinkan objek OrderedDict
diganti secara langsung di mana pun kamus biasa digunakan.
Oke, jadi mengapa mengubah urutan mempengaruhi panjang objek set
yang dihasilkan? Jawabannya adalah tidak adanya kesetaraan intransitif saja. Karena kumpulan adalah kumpulan elemen unik yang "tidak berurutan", urutan penyisipan elemen tidak menjadi masalah. Namun dalam kasus ini, itu penting. Mari kita uraikan sedikit,
> >> some_set = set ()
> >> some_set . add ( dictionary ) # these are the mapping objects from the snippets above
> >> ordered_dict in some_set
True
> >> some_set . add ( ordered_dict )
> >> len ( some_set )
1
> >> another_ordered_dict in some_set
True
> >> some_set . add ( another_ordered_dict )
> >> len ( some_set )
1
> >> another_set = set ()
> >> another_set . add ( ordered_dict )
> >> another_ordered_dict in another_set
False
> >> another_set . add ( another_ordered_dict )
> >> len ( another_set )
2
> >> dictionary in another_set
True
> >> another_set . add ( another_ordered_dict )
> >> len ( another_set )
2
Jadi ketidakkonsistenan ini disebabkan oleh another_ordered_dict in another_set
menjadi False
karena ordered_dict
sudah ada di another_set
dan seperti yang diamati sebelumnya, ordered_dict == another_ordered_dict
adalah False
.
def some_func ():
try :
return 'from_try'
finally :
return 'from_finally'
def another_func ():
for _ in range ( 3 ):
try :
continue
finally :
print ( "Finally!" )
def one_more_func (): # A gotcha!
try :
for i in range ( 3 ):
try :
1 / i
except ZeroDivisionError :
# Let's throw it here and handle it outside for loop
raise ZeroDivisionError ( "A trivial divide by zero error" )
finally :
print ( "Iteration" , i )
break
except ZeroDivisionError as e :
print ( "Zero division error occurred" , e )
Keluaran:
> >> some_func ()
'from_finally'
> >> another_func ()
Finally !
Finally !
Finally !
>> > 1 / 0
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
ZeroDivisionError : division by zero
> >> one_more_func ()
Iteration 0
return
, break
atau continue
dieksekusi dalam rangkaian try
dari pernyataan "try...finally", klausa finally
juga dieksekusi pada saat keluar.return
terakhir yang dieksekusi. Karena klausa finally
selalu dieksekusi, pernyataan return
yang dieksekusi di klausa finally
akan selalu menjadi yang terakhir dieksekusi.return
atau break
, pengecualian yang disimpan sementara akan dibuang. some_string = "wtf"
some_dict = {}
for i , some_dict [ i ] in enumerate ( some_string ):
i = 10
Keluaran:
> >> some_dict # An indexed dict appears.
{ 0 : 'w' , 1 : 't' , 2 : 'f' }
Pernyataan for
didefinisikan dalam tata bahasa Python sebagai:
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
Dimana exprlist
adalah target penugasannya. Ini berarti setara dengan {exprlist} = {next_value}
dieksekusi untuk setiap item dalam iterable. Contoh menarik yang menggambarkan hal ini:
for i in range ( 4 ):
print ( i )
i = 10
Keluaran:
0
1
2
3
Apakah Anda mengharapkan loop berjalan sekali saja?
Penjelasan:
i = 10
tidak pernah mempengaruhi iterasi perulangan karena cara perulangan for bekerja dengan Python. Sebelum memulai setiap iterasi, item berikutnya yang disediakan oleh iterator ( range(4)
dalam kasus ini) dibongkar dan diberi variabel daftar target ( i
dalam kasus ini). Fungsi enumerate(some_string)
menghasilkan nilai baru i
(penghitung naik) dan karakter dari some_string
di setiap iterasi. Ini kemudian menetapkan kunci i
(yang baru saja ditetapkan) dari kamus some_dict
ke karakter itu. Pembukaan gulungan loop dapat disederhanakan sebagai:
> >> i , some_dict [ i ] = ( 0 , 'w' )
> >> i , some_dict [ i ] = ( 1 , 't' )
> >> i , some_dict [ i ] = ( 2 , 'f' )
> >> some_dict
1.
array = [ 1 , 8 , 15 ]
# A typical generator expression
gen = ( x for x in array if array . count ( x ) > 0 )
array = [ 2 , 8 , 22 ]
Keluaran:
> >> print ( list ( gen )) # Where did the other values go?
[ 8 ]
2.
array_1 = [ 1 , 2 , 3 , 4 ]
gen_1 = ( x for x in array_1 )
array_1 = [ 1 , 2 , 3 , 4 , 5 ]
array_2 = [ 1 , 2 , 3 , 4 ]
gen_2 = ( x for x in array_2 )
array_2 [:] = [ 1 , 2 , 3 , 4 , 5 ]
Keluaran:
> >> print ( list ( gen_1 ))
[ 1 , 2 , 3 , 4 ]
> >> print ( list ( gen_2 ))
[ 1 , 2 , 3 , 4 , 5 ]
3.
array_3 = [ 1 , 2 , 3 ]
array_4 = [ 10 , 20 , 30 ]
gen = ( i + j for i in array_3 for j in array_4 )
array_3 = [ 4 , 5 , 6 ]
array_4 = [ 400 , 500 , 600 ]
Keluaran:
> >> print ( list ( gen ))
[ 401 , 501 , 601 , 402 , 502 , 602 , 403 , 503 , 603 ]
Dalam ekspresi generator, klausa in
dievaluasi pada waktu deklarasi, namun klausa kondisional dievaluasi pada saat runtime.
Jadi sebelum runtime, array
ditugaskan kembali ke daftar [2, 8, 22]
, dan karena dari 1
, 8
dan 15
, hanya hitungan 8
yang lebih besar dari 0
, generator hanya menghasilkan 8
.
Perbedaan keluaran g1
dan g2
di bagian kedua disebabkan oleh cara variabel array_1
dan array_2
ditetapkan ulang nilainya.
Dalam kasus pertama, array_1
terikat ke objek baru [1,2,3,4,5]
dan karena klausa in
dievaluasi pada waktu deklarasi, klausa tersebut masih merujuk ke objek lama [1,2,3,4]
(yang tidak dimusnahkan).
Dalam kasus kedua, penetapan irisan ke array_2
memperbarui objek lama yang sama [1,2,3,4]
menjadi [1,2,3,4,5]
. Oleh karena itu g2
dan array_2
masih memiliki referensi ke objek yang sama (yang kini telah diperbarui menjadi [1,2,3,4,5]
).
Oke, berdasarkan logika yang dibahas sejauh ini, bukankah nilai list(gen)
di cuplikan ketiga seharusnya [11, 21, 31, 12, 22, 32, 13, 23, 33]
? (karena array_3
dan array_4
akan berperilaku seperti array_1
). Alasan mengapa (hanya) nilai array_4
diperbarui dijelaskan di PEP-289
Hanya ekspresi for terluar yang dievaluasi segera, ekspresi lainnya ditangguhkan hingga generator dijalankan.
is not ...
tidak is (not ...)
> >> 'something' is not None
True
> >> 'something' is ( not None )
False
is not
adalah operator biner tunggal, dan memiliki perilaku yang berbeda dari penggunaan is
dan not
dipisahkan.is not
bernilai False
jika variabel di kedua sisi operator menunjuk ke objek yang sama dan True
sebaliknya.(not None)
bernilai True
karena nilai None
adalah False
dalam konteks boolean, sehingga ekspresi menjadi 'something' is True
. # Let's initialize a row
row = [ "" ] * 3 #row i['', '', '']
# Let's make a board
board = [ row ] * 3
Keluaran:
> >> board
[[ '' , '' , '' ], [ '' , '' , '' ], [ '' , '' , '' ]]
> >> board [ 0 ]
[ '' , '' , '' ]
> >> board [ 0 ][ 0 ]
''
> >> board [ 0 ][ 0 ] = "X"
> >> board
[[ 'X' , '' , '' ], [ 'X' , '' , '' ], [ 'X' , '' , '' ]]
Kita tidak menetapkan tiga huruf "X"
, bukan?
Saat kita menginisialisasi variabel row
, visualisasi ini menjelaskan apa yang terjadi di memori
Dan ketika board
diinisialisasi dengan mengalikan row
, inilah yang terjadi di dalam memori (masing-masing elemen board[0]
, board[1]
dan board[2]
adalah referensi ke daftar yang sama yang dirujuk oleh row
)
Kita dapat menghindari skenario ini dengan tidak menggunakan variabel row
untuk menghasilkan board
. (Ditanyakan dalam edisi ini).
> >> board = [[ '' ] * 3 for _ in range ( 3 )]
> >> board [ 0 ][ 0 ] = "X"
> >> board
[[ 'X' , '' , '' ], [ '' , '' , '' ], [ '' , '' , '' ]]
funcs = []
results = []
for x in range ( 7 ):
def some_func ():
return x
funcs . append ( some_func )
results . append ( some_func ()) # note the function call here
funcs_results = [ func () for func in funcs ]
Keluaran (versi Python):
> >> results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
> >> funcs_results
[ 6 , 6 , 6 , 6 , 6 , 6 , 6 ]
Nilai x
berbeda di setiap iterasi sebelum menambahkan some_func
ke funcs
, tetapi semua fungsi mengembalikan 6 saat dievaluasi setelah loop selesai.
> >> powers_of_x = [ lambda x : x ** i for i in range ( 10 )]
> >> [ f ( 2 ) for f in powers_of_x ]
[ 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 ]
x
dalam konteks sekitarnya, daripada menggunakan nilai x
pada saat fungsi tersebut dibuat. Jadi semua fungsi menggunakan nilai terbaru yang ditetapkan ke variabel untuk komputasi. Kita dapat melihat bahwa ia menggunakan x
dari konteks sekitarnya (yaitu bukan variabel lokal) dengan: > >> import inspect
> >> inspect . getclosurevars ( funcs [ 0 ])
ClosureVars ( nonlocals = {}, globals = { 'x' : 6 }, builtins = {}, unbound = set ())
Karena x
adalah nilai global, kita dapat mengubah nilai yang akan dicari dan dikembalikan oleh funcs
dengan memperbarui x
:
> >> x = 42
> >> [ func () for func in funcs ]
[ 42 , 42 , 42 , 42 , 42 , 42 , 42 ]
x
pada saat itu. funcs = []
for x in range ( 7 ):
def some_func ( x = x ):
return x
funcs . append ( some_func )
Keluaran:
> >> funcs_results = [ func () for func in funcs ]
> >> funcs_results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
Itu tidak lagi menggunakan x
dalam lingkup global:
> >> inspect . getclosurevars ( funcs [ 0 ])
ClosureVars ( nonlocals = {}, globals = {}, builtins = {}, unbound = set ())
1.
> >> isinstance ( 3 , int )
True
> >> isinstance ( type , object )
True
> >> isinstance ( object , type )
True
Jadi yang manakah kelas dasar "terakhir"? Ngomong-ngomong, masih ada lagi kebingungan,
2.
> >> class A : pass
> >> isinstance ( A , A )
False
> >> isinstance ( type , type )
True
> >> isinstance ( object , object )
True
3.
> >> issubclass ( int , object )
True
> >> issubclass ( type , object )
True
> >> issubclass ( object , type )
False
type
adalah metaclass dengan Python.object
dengan Python, yang mencakup kelas serta objeknya (instance).type
kelas adalah metakelas dari kelas object
, dan setiap kelas (termasuk type
) diwarisi secara langsung atau tidak langsung dari object
.object
dan type
. Kebingungan dalam cuplikan di atas muncul karena kita memikirkan tentang hubungan ini ( issubclass
dan isinstance
) dalam kaitannya dengan kelas Python. Hubungan antara object
dan type
tidak dapat direproduksi dengan python murni. Untuk lebih tepatnya, hubungan berikut tidak dapat direproduksi dengan Python murni,object
dan type
(baik yang merupakan contoh satu sama lain maupun dirinya sendiri) ada di Python karena "kecurangan" pada tingkat implementasi.Keluaran:
> >> from collections . abc import Hashable
> >> issubclass ( list , object )
True
> >> issubclass ( object , Hashable )
True
> >> issubclass ( list , Hashable )
False
Hubungan Subkelas diharapkan bersifat transitif, bukan? (yaitu, jika A
adalah subkelas dari B
, dan B
adalah subkelas dari C
, maka A
seharusnya merupakan subkelas dari C
)
__subclasscheck__
mereka sendiri secara sewenang-wenang dalam metaclass.issubclass(cls, Hashable)
dipanggil, ia hanya mencari metode " __hash__
" non-Falsey di cls
atau apa pun yang diwarisinya.object
dapat di-hash, tetapi list
tidak dapat di-hash, maka relasi transitivitas akan rusak. class SomeClass :
def method ( self ):
pass
@ classmethod
def classm ( cls ):
pass
@ staticmethod
def staticm ():
pass
Keluaran:
> >> print ( SomeClass . method is SomeClass . method )
True
> >> print ( SomeClass . classm is SomeClass . classm )
False
> >> print ( SomeClass . classm == SomeClass . classm )
True
> >> print ( SomeClass . staticm is SomeClass . staticm )
True
Mengakses classm
dua kali, kita mendapatkan objek yang sama, tetapi tidak sama ? Mari kita lihat apa yang terjadi dengan instance SomeClass
:
o1 = SomeClass ()
o2 = SomeClass ()
Keluaran:
> >> print ( o1 . method == o2 . method )
False
> >> print ( o1 . method == o1 . method )
True
> >> print ( o1 . method is o1 . method )
False
> >> print ( o1 . classm is o1 . classm )
False
> >> print ( o1 . classm == o1 . classm == o2 . classm == SomeClass . classm )
True
> >> print ( o1 . staticm is o1 . staticm is o2 . staticm is SomeClass . staticm )
True
Mengakses classm
atau method
dua kali, menciptakan objek yang sama tetapi tidak sama untuk instance SomeClass
yang sama.
self
sebagai argumen pertama, meskipun tidak meneruskannya secara eksplisit). > >> o1 . method
< bound method SomeClass . method of < __main__ . SomeClass object at ... >>
o1.method is o1.method
tidak pernah benar. Namun, mengakses fungsi sebagai atribut kelas (sebagai lawan dari instance) tidak menciptakan metode; jadi SomeClass.method is SomeClass.method
yang benar. > >> SomeClass . method
< function SomeClass . method at ... >
classmethod
mengubah fungsi menjadi metode kelas. Metode kelas adalah deskriptor yang, ketika diakses, membuat objek metode yang mengikat kelas (tipe) objek tersebut, bukan objek itu sendiri. > >> o1 . classm
< bound method SomeClass . classm of < class '__main__.SomeClass' >>
classmethod
s akan membuat metode juga ketika diakses sebagai atribut kelas (dalam hal ini mereka mengikat kelas, bukan tipenya). Jadi SomeClass.classm is SomeClass.classm
salah. > >> SomeClass . classm
< bound method SomeClass . classm of < class '__main__.SomeClass' >>
o1.method == o1.method
benar, meskipun objeknya tidak sama di memori.staticmethod
mengubah fungsi menjadi deskriptor "no-op", yang mengembalikan fungsi apa adanya. Tidak ada objek metode yang pernah dibuat, jadi perbandingan dengan is
adalah benar. > >> o1 . staticm
< function SomeClass . staticm at ... >
> >> SomeClass . staticm
< function SomeClass . staticm at ... >
self
secara buruk. CPython 3.7 menyelesaikannya dengan memperkenalkan opcode baru yang menangani metode pemanggilan tanpa membuat objek metode sementara. Ini hanya digunakan ketika fungsi yang diakses benar-benar dipanggil, sehingga cuplikan di sini tidak terpengaruh, dan masih menghasilkan metode :) > >> all ([ True , True , True ])
True
> >> all ([ True , True , False ])
False
> >> all ([])
True
> >> all ([[]])
False
> >> all ([[[]]])
True
Mengapa perubahan Benar-Salah ini?
Implementasi all
fungsi setara dengan
def all ( iterable ):
for element in iterable :
if not element :
return False
return True
all([])
mengembalikan True
karena iterable kosong.
all([[]])
mengembalikan False
karena array yang diteruskan memiliki satu elemen, []
, dan dalam python, daftar kosong adalah palsu.
all([[[]]])
dan varian rekursif yang lebih tinggi selalu True
. Hal ini karena elemen tunggal array yang diteruskan ( [[...]]
) tidak lagi kosong, dan daftar dengan nilai sudah benar.
Keluaran (<3.6):
> >> def f ( x , y ,):
... print ( x , y )
...
> >> def g ( x = 4 , y = 5 ,):
... print ( x , y )
...
> >> def h ( x , ** kwargs ,):
File "<stdin>" , line 1
def h ( x , ** kwargs ,):
^
SyntaxError : invalid syntax
>> > def h ( * args ,):
File "<stdin>" , line 1
def h ( * args ,):
^
SyntaxError : invalid syntax
Keluaran:
> >> print ( " " " )
"
>> > print ( r""" )
"
>> > print ( r" " )
File "<stdin>" , line 1
print ( r" " )
^
SyntaxError : EOL while scanning string literal
>> > r''' == " \ '"
True
> >> "wt " f"
'wt"f'
r
), garis miring terbalik meneruskan dirinya sebagaimana adanya bersama dengan perilaku keluar dari karakter berikut. > >> r'wt"f' == 'wt \ "f'
True
> >> print ( repr ( r'wt"f' ))
'wt \ "f'
> >> print ( " n " )
> >> print ( r"\n" )
' \ n'
print(r"")
), garis miring terbalik lolos dari tanda kutip tambahan, meninggalkan parser tanpa tanda kutip terminasi (oleh karena itu SyntaxError
). Itu sebabnya garis miring terbalik tidak berfungsi di akhir string mentah. x = True
y = False
Keluaran:
> >> not x == y
True
> >> x == not y
File "<input>" , line 1
x == not y
^
SyntaxError : invalid syntax
==
memiliki prioritas lebih tinggi daripada operator not
di Python.not x == y
setara dengan not (x == y)
yang setara dengan not (True == False)
yang akhirnya dievaluasi menjadi True
.x == not y
memunculkan SyntaxError
karena dapat dianggap setara dengan (x == not) y
dan bukan x == (not y)
yang mungkin Anda duga pada pandangan pertama.not
token menjadi bagian dari operator not in
(karena operator ==
dan not in
memiliki prioritas yang sama), tetapi setelah tidak dapat menemukan token in
setelah not
token, maka akan muncul SyntaxError
.Keluaran:
> >> print ( 'wtfpython' '' )
wtfpython
> >> print ( "wtfpython" "" )
wtfpython
> >> # The following statements raise `SyntaxError`
>> > # print('''wtfpython')
>> > # print("""wtfpython")
File "<input>" , line 3
print ("""wtfpython")
^
SyntaxError: EOF while scanning triple-quoted string literal
>>> print("wtf" "python")
wtfpython
>>> print("wtf" "") # or "wtf"""
wtf
'''
dan """
juga merupakan pembatas string dalam Python yang menyebabkan SyntaxError karena penerjemah Python mengharapkan penghentian tanda kutip rangkap tiga sebagai pembatas saat memindai literal string tanda kutip rangkap tiga yang ditemui saat ini.1.
# A simple example to count the number of booleans and
# integers in an iterable of mixed data types.
mixed_list = [ False , 1.0 , "some_string" , 3 , True , [], False ]
integers_found_so_far = 0
booleans_found_so_far = 0
for item in mixed_list :
if isinstance ( item , int ):
integers_found_so_far += 1
elif isinstance ( item , bool ):
booleans_found_so_far += 1
Keluaran:
> >> integers_found_so_far
4
> >> booleans_found_so_far
0
2.
> >> some_bool = True
> >> "wtf" * some_bool
'wtf'
> >> some_bool = False
> >> "wtf" * some_bool
''
3.
def tell_truth ():
True = False
if True == False :
print ( "I have lost faith in truth!" )
Keluaran (<3.x):
> >> tell_truth ()
I have lost faith in truth !
bool
adalah subkelas dari int
dengan Python
> >> issubclass ( bool , int )
True
> >> issubclass ( int , bool )
False
Jadi, True
dan False
adalah contoh dari int
> >> isinstance ( True , int )
True
> >> isinstance ( False , int )
True
Nilai bilangan bulat True
adalah 1
dan False
adalah 0
.
> >> int ( True )
1
> >> int ( False )
0
Lihat jawaban StackOverflow ini untuk mengetahui alasan di baliknya.
Awalnya, Python tidak memiliki tipe bool
(orang menggunakan 0 untuk nilai salah dan bukan nol seperti 1 untuk nilai benar). True
, False
, dan tipe bool
telah ditambahkan dalam versi 2.x, tetapi, untuk kompatibilitas ke belakang, True
dan False
tidak dapat dijadikan konstanta. Mereka hanyalah variabel bawaan, dan variabel tersebut dapat ditetapkan kembali
Python 3 tidak kompatibel dengan versi sebelumnya, masalahnya akhirnya diperbaiki, sehingga cuplikan terakhir tidak akan berfungsi dengan Python 3.x!
1.
class A :
x = 1
class B ( A ):
pass
class C ( A ):
pass
Keluaran:
> >> A . x , B . x , C . x
( 1 , 1 , 1 )
>> > B . x = 2
>> > A . x , B . x , C . x
( 1 , 2 , 1 )
>> > A . x = 3
>> > A . x , B . x , C . x # C.x changed, but B.x didn't
( 3 , 2 , 3 )
>> > a = A ()
>> > a . x , A . x
( 3 , 3 )
>> > a . x + = 1
>> > a . x , A . x
( 4 , 3 )
2.
class SomeClass :
some_var = 15
some_list = [ 5 ]
another_list = [ 5 ]
def __init__ ( self , x ):
self . some_var = x + 1
self . some_list = self . some_list + [ x ]
self . another_list += [ x ]
Keluaran:
> >> some_obj = SomeClass ( 420 )
> >> some_obj . some_list
[ 5 , 420 ]
> >> some_obj . another_list
[ 5 , 420 ]
> >> another_obj = SomeClass ( 111 )
> >> another_obj . some_list
[ 5 , 111 ]
> >> another_obj . another_list
[ 5 , 420 , 111 ]
> >> another_obj . another_list is SomeClass . another_list
True
> >> another_obj . another_list is some_obj . another_list
True
+=
memodifikasi objek yang dapat diubah di tempatnya tanpa membuat objek baru. Jadi mengubah atribut dari satu instance akan mempengaruhi instance lainnya dan atribut class juga. some_iterable = ( 'a' , 'b' )
def some_func ( val ):
return "something"
Keluaran (<= 3.7.x):
> >> [ x for x in some_iterable ]
[ 'a' , 'b' ]
> >> [( yield x ) for x in some_iterable ]
< generator object < listcomp > at 0x7f70b0a4ad58 >
> >> list ([( yield x ) for x in some_iterable ])
[ 'a' , 'b' ]
> >> list (( yield x ) for x in some_iterable )
[ 'a' , None , 'b' , None ]
> >> list ( some_func (( yield x )) for x in some_iterable )
[ 'a' , 'something' , 'b' , 'something' ]
yield
di generator dan pemahaman CPython.yield
di dalam daftar dan akan memunculkan SyntaxError
.1.
def some_func ( x ):
if x == 3 :
return [ "wtf" ]
else :
yield from range ( x )
Keluaran (> 3.3):
> >> list ( some_func ( 3 ))
[]
Kemana perginya "wtf"
itu? Apakah ini karena efek khusus dari yield from
? Mari kita validasi itu,
2.
def some_func ( x ):
if x == 3 :
return [ "wtf" ]
else :
for i in range ( x ):
yield i
Keluaran:
> >> list ( some_func ( 3 ))
[]
Hasil yang sama, ini juga tidak berhasil.
return
dapat digunakan dengan nilai di dalam generator (Lihat PEP380). Dokumen resmi mengatakan bahwa,"...
return expr
di generator menyebabkanStopIteration(expr)
dimunculkan saat keluar dari generator."
Dalam kasus some_func(3)
, StopIteration
dimunculkan di awal karena pernyataan return
. Pengecualian StopIteration
secara otomatis terperangkap di dalam pembungkus list(...)
dan loop for
. Oleh karena itu, dua cuplikan di atas menghasilkan daftar kosong.
Untuk mendapatkan ["wtf"]
dari generator some_func
kita perlu menangkap pengecualian StopIteration
,
try :
next ( some_func ( 3 ))
except StopIteration as e :
some_string = e . value
> >> some_string
[ "wtf" ]
1.
a = float ( 'inf' )
b = float ( 'nan' )
c = float ( '-iNf' ) # These strings are case-insensitive
d = float ( 'nan' )
Keluaran:
> >> a
inf
> >> b
nan
> >> c
- inf
> >> float ( 'some_other_string' )
ValueError : could not convert string to float : some_other_string
> >> a == - c # inf==inf
True
> >> None == None # None == None
True
> >> b == d # but nan!=nan
False
> >> 50 / a
0.0
> >> a / a
nan
> >> 23 + b
nan
2.
> >> x = float ( 'nan' )
> >> y = x / x
> >> y is y # identity holds
True
> >> y == y # equality fails of y
False
> >> [ y ] == [ y ] # but the equality succeeds for the list containing y
True
'inf'
dan 'nan'
adalah string khusus (tidak peka huruf besar-kecil), yang, ketika secara eksplisit diketikkan ke tipe float
, digunakan untuk merepresentasikan "tak terhingga" dan "bukan angka" secara matematis.
Karena menurut standar IEEE NaN != NaN
, mematuhi aturan ini mematahkan asumsi refleksivitas elemen koleksi dengan Python yaitu jika x
adalah bagian dari koleksi seperti list
, implementasi seperti perbandingan didasarkan pada asumsi bahwa x == x
. Karena asumsi ini, identitas dibandingkan terlebih dahulu (karena lebih cepat) sambil membandingkan dua elemen, dan nilainya hanya dibandingkan jika identitasnya tidak cocok. Cuplikan berikut akan memperjelas,
> >> x = float ( 'nan' )
> >> x == x , [ x ] == [ x ]
( False , True )
> >> y = float ( 'nan' )
> >> y == y , [ y ] == [ y ]
( False , True )
> >> x == y , [ x ] == [ y ]
( False , False )
Karena identitas x
dan y
berbeda, maka dianggap nilai yang juga berbeda; maka perbandingannya mengembalikan False
kali ini.
Bacaan menarik: Refleksivitas, dan Pilar Peradaban Lainnya
Ini mungkin tampak sepele jika Anda mengetahui cara kerja referensi dengan Python.
some_tuple = ( "A" , "tuple" , "with" , "values" )
another_tuple = ([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 ])
Keluaran:
> >> some_tuple [ 2 ] = "change this"
TypeError : 'tuple' object does not support item assignment
> >> another_tuple [ 2 ]. append ( 1000 ) #This throws no error
> >> another_tuple
([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 , 1000 ])
> >> another_tuple [ 2 ] += [ 99 , 999 ]
TypeError : 'tuple' object does not support item assignment
> >> another_tuple
([ 1 , 2 ], [ 3 , 4 ], [ 5 , 6 , 1000 , 99 , 999 ])
Tapi saya pikir tupel tidak bisa diubah...
Mengutip dari https://docs.python.org/3/reference/datamodel.html
Urutan yang tidak dapat diubah Sebuah objek dengan tipe urutan yang tidak dapat diubah tidak dapat diubah setelah dibuat. (Jika objek berisi referensi ke objek lain, objek lain ini mungkin dapat diubah dan dapat dimodifikasi; namun, kumpulan objek yang direferensikan secara langsung oleh objek yang tidak dapat diubah tidak dapat diubah.)
+=
operator mengubah daftar di tempatnya. Penetapan item tidak berfungsi, namun ketika pengecualian terjadi, item telah diubah pada tempatnya.
Ada juga penjelasan di FAQ resmi Python.
e = 7
try :
raise Exception ()
except Exception as e :
pass
Keluaran (Python 2.x):
> >> print ( e )
# prints nothing
Keluaran (Python 3.x):
> >> print ( e )
NameError : name 'e' is not defined
Sumber: https://docs.python.org/3/reference/compound_stmts.html#kecuali
Ketika pengecualian telah ditetapkan as
target, pengecualian tersebut dihapus di akhir klausa except
. Ini seolah-olah
except E as N :
foo
diterjemahkan ke dalam
except E as N :
try :
foo
finally :
del N
Ini berarti pengecualian harus diberi nama yang berbeda agar dapat merujuknya setelah klausa kecuali. Pengecualian dihapus karena, dengan traceback yang melekat padanya, mereka membentuk siklus referensi dengan frame tumpukan, menjaga semua penduduk lokal dalam frame tersebut tetap hidup hingga pengumpulan sampah berikutnya terjadi.
Klausa tidak tercakup dalam Python. Segala sesuatu dalam contoh hadir dalam lingkup yang sama, dan variabel e
dihapus karena eksekusi klausa except
. Hal yang sama tidak terjadi pada fungsi yang memiliki cakupan dalam yang terpisah. Contoh di bawah ini menggambarkan ini:
def f ( x ):
del ( x )
print ( x )
x = 5
y = [ 5 , 4 , 3 ]
Keluaran:
> >> f ( x )
UnboundLocalError : local variable 'x' referenced before assignment
>> > f ( y )
UnboundLocalError : local variable 'x' referenced before assignment
>> > x
5
> >> y
[ 5 , 4 , 3 ]
Dalam Python 2.x, nama variabel e
akan ditetapkan ke instance Exception()
, jadi ketika Anda mencoba mencetak, itu tidak mencetak apa -apa.
Output (Python 2.x):
> >> e
Exception ()
> >> print e
# Nothing is printed!
class SomeClass ( str ):
pass
some_dict = { 's' : 42 }
Keluaran:
> >> type ( list ( some_dict . keys ())[ 0 ])
str
> >> s = SomeClass ( 's' )
> >> some_dict [ s ] = 40
> >> some_dict # expected: Two different keys-value pairs
{ 's' : 40 }
> >> type ( list ( some_dict . keys ())[ 0 ])
str
Baik objek s
dan string "s"
hash ke nilai yang sama karena SomeClass
mewarisi metode __hash__
dari kelas str
.
SomeClass("s") == "s"
mengevaluasi ke True
karena SomeClass
juga mewarisi metode __eq__
dari kelas str
.
Karena kedua objek hash dengan nilai yang sama dan sama, mereka diwakili oleh kunci yang sama dalam kamus.
Untuk perilaku yang diinginkan, kita dapat mendefinisikan kembali metode __eq__
dalam SomeClass
class SomeClass ( str ):
def __eq__ ( self , other ):
return (
type ( self ) is SomeClass
and type ( other ) is SomeClass
and super (). __eq__ ( other )
)
# When we define a custom __eq__, Python stops automatically inheriting the
# __hash__ method, so we need to define it as well
__hash__ = str . __hash__
some_dict = { 's' : 42 }
Keluaran:
> >> s = SomeClass ( 's' )
> >> some_dict [ s ] = 40
> >> some_dict
{ 's' : 40 , 's' : 42 }
> >> keys = list ( some_dict . keys ())
> >> type ( keys [ 0 ]), type ( keys [ 1 ])
( __main__ . SomeClass , str )
a , b = a [ b ] = {}, 5
Keluaran:
> >> a
{ 5 : ({...}, 5 )}
(target_list "=")+ (expression_list | yield_expression)
Pernyataan penugasan mengevaluasi daftar ekspresi (ingat bahwa ini dapat berupa ekspresi tunggal atau daftar yang dipisahkan koma, yang terakhir menghasilkan tuple) dan menetapkan objek yang dihasilkan tunggal untuk masing-masing daftar target, dari kiri ke kanan.
The +
in (target_list "=")+
berarti mungkin ada satu atau lebih daftar target. Dalam hal ini, daftar target adalah a, b
dan a[b]
(perhatikan daftar ekspresi tepat satu, yang dalam kasus kami adalah {}, 5
).
Setelah daftar ekspresi dievaluasi, nilainya dibongkar ke daftar target dari kiri ke kanan . Jadi, dalam kasus kami, pertama {}, 5
tuple dibongkar ke a, b
dan kami sekarang memiliki a = {}
dan b = 5
.
a
sekarang ditugaskan ke {}
, yang merupakan objek yang dapat berubah.
Daftar target kedua adalah a[b]
(Anda mungkin berharap ini melakukan kesalahan karena baik a
dan b
belum didefinisikan dalam pernyataan sebelumnya. Tetapi ingat, kami hanya menetapkan a
ke {}
dan b
hingga 5
).
Sekarang, kami mengatur kunci 5
dalam kamus ke tuple ({}, 5)
membuat referensi melingkar ( {...}
dalam output mengacu pada objek yang sama yang sudah dirujuk a
). Contoh lain yang lebih sederhana dari referensi melingkar
> >> some_list = some_list [ 0 ] = [ 0 ]
> >> some_list
[[...]]
> >> some_list [ 0 ]
[[...]]
> >> some_list is some_list [ 0 ]
True
> >> some_list [ 0 ][ 0 ][ 0 ][ 0 ][ 0 ][ 0 ] == some_list
True
Serupa adalah kasus dalam contoh kami ( a[b][0]
adalah objek yang sama dengan a
)
Jadi untuk menyimpulkannya, Anda dapat memecah contohnya
a , b = {}, 5
a [ b ] = a , b
Dan referensi melingkar dapat dibenarkan dengan fakta bahwa a[b][0]
adalah objek yang sama dengan a
> >> a [ b ][ 0 ] is a
True
> >> # Python 3.10.6
>> > int ( "2" * 5432 )
> >> # Python 3.10.8
>> > int ( "2" * 5432 )
Keluaran:
> >> # Python 3.10.6
222222222222222222222222222222222222222222222222222222222222222. ..
>> > # Python 3.10.8
Traceback ( most recent call last ):
...
ValueError : Exceeds the limit ( 4300 ) for integer string conversion :
value has 5432 digits ; use sys . set_int_max_str_digits ()
to increase the limit .
Panggilan ini ke int()
berfungsi dengan baik di Python 3.10.6 dan meningkatkan nilai di Python 3.10.8. Perhatikan bahwa Python masih dapat bekerja dengan bilangan bulat besar. Kesalahan hanya dinaikkan saat mengonversi antara bilangan bulat dan string.
Untungnya, Anda dapat meningkatkan batas untuk jumlah digit yang diizinkan ketika Anda mengharapkan operasi untuk melampaui itu. Untuk melakukan ini, Anda dapat menggunakan salah satu dari yang berikut:
Periksa dokumentasi untuk detail lebih lanjut tentang mengubah batas default jika Anda mengharapkan kode Anda melebihi nilai ini.
x = { 0 : None }
for i in x :
del x [ i ]
x [ i + 1 ] = None
print ( i )
Output (Python 2.7- Python 3.5):
0
1
2
3
4
5
6
7
Ya, itu berjalan tepat delapan kali dan berhenti.
RuntimeError: dictionary keys changed during iteration
jika Anda mencoba melakukan ini.del
yang keras kepala class SomeClass :
def __del__ ( self ):
print ( "Deleted!" )
Output: 1.
> >> x = SomeClass ()
> >> y = x
> >> del x # this should print "Deleted!"
> >> del y
Deleted !
Fiuh, akhirnya dihapus. Anda mungkin telah menebak apa yang disimpan __del__
dari dipanggil dalam upaya pertama kami untuk menghapus x
. Mari kita tambahkan lebih banyak tikungan ke contoh.
2.
> >> x = SomeClass ()
> >> y = x
> >> del x
> >> y # check if y exists
< __main__ . SomeClass instance at 0x7f98a1a67fc8 >
> >> del y # Like previously, this should print "Deleted!"
> >> globals () # oh, it didn't. Let's check all our global variables and confirm
Deleted !
{ '__builtins__' : < module '__builtin__' ( built - in ) > , 'SomeClass' : < class __main__ . SomeClass at 0x7f98a1a5f668 > , '__package__' : None , '__name__' : '__main__' , '__doc__' : None }
Oke, sekarang sudah dihapus?
del x
tidak secara langsung menghubungi x.__del__()
.del x
ditemui, Python menghapus nama x
dari ruang lingkup saat ini dan penurunan dengan 1 jumlah referensi objek x
yang dirujuk. __del__()
dipanggil hanya ketika jumlah referensi objek mencapai nol.__del__()
tidak dipanggil karena pernyataan sebelumnya ( >>> y
) None
interpreter interaktif membuat referensi lain ke objek yang sama ( _
Ekspresi pada repl), sehingga mencegah jumlah referensi mencapai nol ketika del y
ditemui.globals
(atau benar -benar, melaksanakan apa pun yang akan memiliki hasil yang tidak None
) menyebabkan _
merujuk hasil baru, menjatuhkan referensi yang ada. Sekarang jumlah referensi mencapai 0 dan kita dapat melihat "Dihapus!" dicetak (akhirnya!).1.
a = 1
def some_func ():
return a
def another_func ():
a += 1
return a
2.
def some_closure_func ():
a = 1
def some_inner_func ():
return a
return some_inner_func ()
def another_closure_func ():
a = 1
def another_inner_func ():
a += 1
return a
return another_inner_func ()
Keluaran:
> >> some_func ()
1
> >> another_func ()
UnboundLocalError : local variable 'a' referenced before assignment
>> > some_closure_func ()
1
> >> another_closure_func ()
UnboundLocalError : local variable 'a' referenced before assignment
Saat Anda membuat tugas ke variabel dalam ruang lingkup, itu menjadi lokal untuk ruang lingkup itu. Jadi a
menjadi lokal ke ruang lingkup another_func
, tetapi belum diinisialisasi sebelumnya dalam ruang lingkup yang sama, yang melempar kesalahan.
Untuk memodifikasi variabel lingkup luar a
di another_func
, kita harus menggunakan kata kunci global
.
def another_func ()
global a
a += 1
return a
Keluaran:
> >> another_func ()
2
Di another_closure_func
, a
menjadi lokal ke ruang lingkup another_inner_func
, tetapi belum diinisialisasi sebelumnya dalam ruang lingkup yang sama, itulah sebabnya ia melakukan kesalahan.
Untuk memodifikasi variabel lingkup luar a
di another_inner_func
, gunakan kata kunci nonlocal
. Pernyataan nonlokal digunakan untuk merujuk ke variabel yang ditentukan dalam ruang lingkup luar (tidak termasuk global) terdekat.
def another_func ():
a = 1
def another_inner_func ():
nonlocal a
a += 1
return a
return another_inner_func ()
Keluaran:
> >> another_func ()
2
Kata kunci global
dan nonlocal
memberi tahu juru bahasa Python untuk tidak mendeklarasikan variabel baru dan mencarinya di lingkup luar yang sesuai.
Baca ini pendek tetapi panduan yang luar biasa untuk mempelajari lebih lanjut tentang bagaimana ruang nama dan resolusi ruang lingkup bekerja di Python.
list_1 = [ 1 , 2 , 3 , 4 ]
list_2 = [ 1 , 2 , 3 , 4 ]
list_3 = [ 1 , 2 , 3 , 4 ]
list_4 = [ 1 , 2 , 3 , 4 ]
for idx , item in enumerate ( list_1 ):
del item
for idx , item in enumerate ( list_2 ):
list_2 . remove ( item )
for idx , item in enumerate ( list_3 [:]):
list_3 . remove ( item )
for idx , item in enumerate ( list_4 ):
list_4 . pop ( idx )
Keluaran:
> >> list_1
[ 1 , 2 , 3 , 4 ]
> >> list_2
[ 2 , 4 ]
> >> list_3
[]
> >> list_4
[ 2 , 4 ]
Bisakah Anda menebak mengapa outputnya [2, 4]
?
Tidak pernah merupakan ide yang baik untuk mengubah objek yang Anda berikan. Cara yang benar untuk melakukannya adalah dengan mengulangi salinan objek sebagai gantinya, dan list_3[:]
melakukan hal itu.
> >> some_list = [ 1 , 2 , 3 , 4 ]
> >> id ( some_list )
139798789457608
> >> id ( some_list [:]) # Notice that python creates new object for sliced list.
139798779601192
Perbedaan antara del
, remove
, dan pop
:
del var_name
baru saja menghapus ikatan var_name
dari namespace lokal atau global (itulah sebabnya list_1
tidak terpengaruh).remove
menghilangkan nilai pencocokan pertama, bukan indeks tertentu, meningkatkan ValueError
jika nilainya tidak ditemukan.pop
menghapus elemen pada indeks tertentu dan mengembalikannya, meningkatkan IndexError
jika indeks yang tidak valid ditentukan. Mengapa outputnya [2, 4]
?
1
dari list_2
atau list_4
, isi daftar sekarang [2, 3, 4]
. Elemen yang tersisa digeser ke bawah, yaitu, 2
berada pada indeks 0, dan 3
berada pada indeks 1. Karena iterasi berikutnya akan melihat indeks 1 (yang merupakan 3
), 2
akan dilewati sepenuhnya. Hal serupa akan terjadi dengan setiap elemen alternatif dalam urutan daftar. > >> numbers = list ( range ( 7 ))
> >> numbers
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
> >> first_three , remaining = numbers [: 3 ], numbers [ 3 :]
> >> first_three , remaining
([ 0 , 1 , 2 ], [ 3 , 4 , 5 , 6 ])
>> > numbers_iter = iter ( numbers )
>> > list ( zip ( numbers_iter , first_three ))
[( 0 , 0 ), ( 1 , 1 ), ( 2 , 2 )]
# so far so good, let's zip the remaining
>> > list ( zip ( numbers_iter , remaining ))
[( 4 , 3 ), ( 5 , 4 ), ( 6 , 5 )]
Kemana elemen 3
pergi dari daftar numbers
?
def zip ( * iterables ):
sentinel = object ()
iterators = [ iter ( it ) for it in iterables ]
while iterators :
result = []
for it in iterators :
elem = next ( it , sentinel )
if elem is sentinel : return
result . append ( elem )
yield tuple ( result )
result
dengan memanggil fungsi next
pada mereka, dan berhenti setiap kali salah satu yang dapat dikeluarkan.result
dibuang. Itulah yang terjadi dengan 3
di numbers_iter
.zip
adalah, > >> numbers = list ( range ( 7 ))
> >> numbers_iter = iter ( numbers )
> >> list ( zip ( first_three , numbers_iter ))
[( 0 , 0 ), ( 1 , 1 ), ( 2 , 2 )]
> >> list ( zip ( remaining , numbers_iter ))
[( 3 , 3 ), ( 4 , 4 ), ( 5 , 5 ), ( 6 , 6 )]
1.
for x in range ( 7 ):
if x == 6 :
print ( x , ': for x inside loop' )
print ( x , ': x in global' )
Keluaran:
6 : for x inside loop
6 : x in global
Tapi x
tidak pernah didefinisikan di luar ruang lingkup untuk loop ...
2.
# This time let's initialize x first
x = - 1
for x in range ( 7 ):
if x == 6 :
print ( x , ': for x inside loop' )
print ( x , ': x in global' )
Keluaran:
6 : for x inside loop
6 : x in global
3.
Output (Python 2.x):
> >> x = 1
> >> print ([ x for x in range ( 5 )])
[ 0 , 1 , 2 , 3 , 4 ]
> >> print ( x )
4
Output (Python 3.x):
> >> x = 1
> >> print ([ x for x in range ( 5 )])
[ 0 , 1 , 2 , 3 , 4 ]
> >> print ( x )
1
Dalam Python, for-loop menggunakan ruang lingkup tempat mereka ada dan meninggalkan loop-variabel yang ditentukan di belakang. Ini juga berlaku jika kami secara eksplisit mendefinisikan variabel for-loop di namespace global sebelumnya. Dalam hal ini, itu akan menindasikan variabel yang ada.
Perbedaan dalam output Python 2.x dan Python 3.x penafsir untuk contoh pemahaman daftar dapat dijelaskan dengan mengikuti perubahan yang didokumentasikan dalam apa yang baru dalam python 3.0 changelog:
"Daftar pemahaman tidak lagi mendukung formulir sintaksis
[... for var in item1, item2, ...]
. Gunakan[... for var in (item1, item2, ...)]
sebagai gantinya. Juga, perhatikan daftar itu Komprehensi memiliki semantik yang berbeda: mereka lebih dekat dengan gula sintaksis untuk ekspresi generator di dalam konstruktorlist()
, dan khususnya, variabel kontrol loop tidak lagi bocor ke ruang lingkup di sekitarnya. "
def some_func ( default_arg = []):
default_arg . append ( "some_string" )
return default_arg
Keluaran:
> >> some_func ()
[ 'some_string' ]
> >> some_func ()
[ 'some_string' , 'some_string' ]
> >> some_func ([])
[ 'some_string' ]
> >> some_func ()
[ 'some_string' , 'some_string' , 'some_string' ]
Argumen fungsional fungsi yang dapat berubah dalam Python tidak benar -benar diinisialisasi setiap kali Anda memanggil fungsi. Sebaliknya, nilai yang baru saja ditetapkan untuk mereka digunakan sebagai nilai default. Ketika kami secara eksplisit meneruskan []
ke some_func
sebagai argumen, nilai default dari variabel default_arg
tidak digunakan, sehingga fungsi dikembalikan seperti yang diharapkan.
def some_func ( default_arg = []):
default_arg . append ( "some_string" )
return default_arg
Keluaran:
> >> some_func . __defaults__ #This will show the default argument values for the function
([],)
> >> some_func ()
> >> some_func . __defaults__
([ 'some_string' ],)
> >> some_func ()
> >> some_func . __defaults__
([ 'some_string' , 'some_string' ],)
> >> some_func ([])
> >> some_func . __defaults__
([ 'some_string' , 'some_string' ],)
Praktik umum untuk menghindari bug karena argumen yang dapat berubah adalah untuk menetapkan None
sebagai nilai default dan kemudian memeriksa apakah nilai ada yang diteruskan ke fungsi yang sesuai dengan argumen tersebut. Contoh:
def some_func ( default_arg = None ):
if default_arg is None :
default_arg = []
default_arg . append ( "some_string" )
return default_arg
some_list = [ 1 , 2 , 3 ]
try :
# This should raise an ``IndexError``
print ( some_list [ 4 ])
except IndexError , ValueError :
print ( "Caught!" )
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except IndexError , ValueError :
print ( "Caught again!" )
Output (Python 2.x):
Caught !
ValueError : list . remove ( x ): x not in list
Output (Python 3.x):
File "<input>" , line 3
except IndexError , ValueError :
^
SyntaxError : invalid syntax
Untuk menambahkan beberapa pengecualian ke klausa kecuali Anda harus meneruskannya sebagai tanda kurung sebagai argumen pertama. Argumen kedua adalah nama opsional, yang ketika disediakan akan mengikat contoh pengecualian yang telah dinaikkan. Contoh,
some_list = [ 1 , 2 , 3 ]
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except ( IndexError , ValueError ), e :
print ( "Caught again!" )
print ( e )
Output (Python 2.x):
Caught again!
list.remove(x): x not in list
Output (Python 3.x):
File "<input>" , line 4
except ( IndexError , ValueError ), e :
^
IndentationError : unindent does not match any outer indentation level
Memisahkan pengecualian dari variabel dengan koma sudah usang dan tidak berfungsi di Python 3; Cara yang benar adalah menggunakan as
. Contoh,
some_list = [ 1 , 2 , 3 ]
try :
some_list . remove ( 4 )
except ( IndexError , ValueError ) as e :
print ( "Caught again!" )
print ( e )
Keluaran:
Caught again!
list.remove(x): x not in list
1.
a = [ 1 , 2 , 3 , 4 ]
b = a
a = a + [ 5 , 6 , 7 , 8 ]
Keluaran:
> >> a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
> >> b
[ 1 , 2 , 3 , 4 ]
2.
a = [ 1 , 2 , 3 , 4 ]
b = a
a += [ 5 , 6 , 7 , 8 ]
Keluaran:
> >> a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
> >> b
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
a += b
tidak selalu berperilaku sama seperti a = a + b
. Kelas dapat mengimplementasikan operator op=
secara berbeda, dan daftar melakukan ini.
Ekspresi a = a + [5,6,7,8]
menghasilkan daftar baru dan menetapkan referensi a
ke daftar baru itu, meninggalkan b
tidak berubah.
Ekspresi a += [5,6,7,8]
sebenarnya dipetakan ke fungsi "memperluas" yang beroperasi dalam daftar sehingga a
dan b
masih menunjuk ke daftar yang sama yang telah dimodifikasi di tempat.
1.
x = 5
class SomeClass :
x = 17
y = ( x for i in range ( 10 ))
Keluaran:
> >> list ( SomeClass . y )[ 0 ]
5
2.
x = 5
class SomeClass :
x = 17
y = [ x for i in range ( 10 )]
Output (Python 2.x):
> >> SomeClass . y [ 0 ]
17
Output (Python 3.x):
> >> SomeClass . y [ 0 ]
5
Mari kita terapkan fungsi naif untuk mendapatkan elemen tengah dari daftar:
def get_middle ( some_list ):
mid_index = round ( len ( some_list ) / 2 )
return some_list [ mid_index - 1 ]
Python 3.x:
> >> get_middle ([ 1 ]) # looks good
1
> >> get_middle ([ 1 , 2 , 3 ]) # looks good
2
> >> get_middle ([ 1 , 2 , 3 , 4 , 5 ]) # huh?
2
> >> len ([ 1 , 2 , 3 , 4 , 5 ]) / 2 # good
2.5
> >> round ( len ([ 1 , 2 , 3 , 4 , 5 ]) / 2 ) # why?
2
Sepertinya Python membulatkan 2,5 ke 2.
round()
menggunakan pembulatan bankir di mana fraksi .5 dibulatkan ke angka genap terdekat: > >> round ( 0.5 )
0
> >> round ( 1.5 )
2
> >> round ( 2.5 )
2
> >> import numpy # numpy does the same
> >> numpy . round ( 0.5 )
0.0
> >> numpy . round ( 1.5 )
2.0
> >> numpy . round ( 2.5 )
2.0
get_middle([1])
hanya dikembalikan 1 karena indeksnya round(0.5) - 1 = 0 - 1 = -1
, mengembalikan elemen terakhir dalam daftar.Saya belum pernah bertemu bahkan satu pengalaman Pythonist sampai saat ini yang belum menemukan satu atau lebih dari skenario berikut,
1.
x , y = ( 0 , 1 ) if True else None , None
Keluaran:
> >> x , y # expected (0, 1)
(( 0 , 1 ), None )
2.
t = ( 'one' , 'two' )
for i in t :
print ( i )
t = ( 'one' )
for i in t :
print ( i )
t = ()
print ( t )
Keluaran:
one
two
o
n
e
tuple ()
3.
ten_words_list = [
"some",
"very",
"big",
"list",
"that"
"consists",
"of",
"exactly",
"ten",
"words"
]
Keluaran
> >> len ( ten_words_list )
9
4. Tidak cukup menegaskan
a = "python"
b = "javascript"
Keluaran:
# An assert statement with an assertion failure message.
> >> assert ( a == b , "Both languages are different" )
# No AssertionError is raised
5.
some_list = [ 1 , 2 , 3 ]
some_dict = {
"key_1" : 1 ,
"key_2" : 2 ,
"key_3" : 3
}
some_list = some_list . append ( 4 )
some_dict = some_dict . update ({ "key_4" : 4 })
Keluaran:
> >> print ( some_list )
None
> >> print ( some_dict )
None
6.
def some_recursive_func ( a ):
if a [ 0 ] == 0 :
return
a [ 0 ] -= 1
some_recursive_func ( a )
return a
def similar_recursive_func ( a ):
if a == 0 :
return a
a -= 1
similar_recursive_func ( a )
return a
Keluaran:
> >> some_recursive_func ([ 5 , 0 ])
[ 0 , 0 ]
> >> similar_recursive_func ( 5 )
4
Untuk 1, pernyataan yang benar untuk perilaku yang diharapkan adalah x, y = (0, 1) if True else (None, None)
.
Untuk 2, pernyataan yang benar untuk perilaku yang diharapkan adalah t = ('one',)
atau t = 'one',
(koma yang hilang) jika tidak, penerjemah menganggap t
sebagai str
dan iterasi di atasnya karakter berdasarkan karakter.
()
adalah token khusus dan menunjukkan tuple
kosong.
Dalam 3, seperti yang mungkin sudah Anda ketahui, ada koma yang hilang setelah elemen ke -5 ( "that"
) dalam daftar. Jadi dengan gabungan literal string implisit,
> >> ten_words_list
[ 'some' , 'very' , 'big' , 'list' , 'thatconsists' , 'of' , 'exactly' , 'ten' , 'words' ]
Tidak ada AssertionError
yang diangkat dalam cuplikan ke -4 karena alih -alih menegaskan ekspresi individu a == b
, kami menegaskan seluruh tuple. Cuplikan berikut akan membersihkan semuanya,
> >> a = "python"
> >> b = "javascript"
> >> assert a == b
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AssertionError
>> > assert ( a == b , "Values are not equal" )
< stdin > : 1 : SyntaxWarning : assertion is always true , perhaps remove parentheses ?
>> > assert a == b , "Values are not equal"
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AssertionError : Values are not equal
Adapun cuplikan kelima, sebagian besar metode yang memodifikasi item dari objek urutan/pemetaan seperti list.append
, dict.update
, list.sort
, dll. Ubah objek di tempat dan kembalikan None
. Alasan di balik ini adalah untuk meningkatkan kinerja dengan menghindari membuat salinan objek jika operasi dapat dilakukan di tempat (dirujuk dari sini).
Yang terakhir harus cukup jelas, objek yang dapat berubah (seperti list
) dapat diubah dalam fungsi, dan penugasan kembali dari kekambuhan ( a -= 1
) bukanlah perubahan nilai.
Menyadari nitpicks ini dapat menghemat waktu upaya debugging Anda dalam jangka panjang.
> >> 'a' . split ()
[ 'a' ]
# is same as
> >> 'a' . split ( ' ' )
[ 'a' ]
# but
> >> len ( '' . split ())
0
# isn't the same as
> >> len ( '' . split ( ' ' ))
1
' '
, tetapi sesuai dokumenJika SEP tidak ditentukan atau
None
, algoritma pemisahan yang berbeda diterapkan: berjalan dari spasi putih berturut -turut dianggap sebagai pemisah tunggal, dan hasilnya tidak akan mengandung string kosong pada awal atau akhir jika string memiliki arus putih yang mengarah atau tertinggal. Akibatnya, membagi string kosong atau string yang hanya terdiri dari whitespace dengan pemisah tidak ada yang mengembalikan[]
. Jika SEP diberikan, pembatas berturut -turut tidak dikelompokkan bersama dan dianggap membatasi string kosong (misalnya,'1,,2'.split(',')
mengembalikan['1', '', '2']
). Memisahkan string kosong dengan pemisah yang ditentukan mengembalikan['']
.
> >> ' a ' . split ( ' ' )
[ '' , 'a' , '' ]
> >> ' a ' . split ()
[ 'a' ]
> >> '' . split ( ' ' )
[ '' ]
# File: module.py
def some_weird_name_func_ ():
print ( "works!" )
def _another_weird_name_func ():
print ( "works!" )
Keluaran
> >> from module import *
> >> some_weird_name_func_ ()
"works!"
> >> _another_weird_name_func ()
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name '_another_weird_name_func' is not defined
Seringkali disarankan untuk tidak menggunakan impor wildcard. Alasan pertama yang jelas untuk ini adalah, dalam impor wildcard, nama -nama dengan garis bawah terkemuka tidak diimpor. Ini dapat menyebabkan kesalahan selama runtime.
Seandainya kami menggunakan from ... import a, b, c
Sintaks, NameError
di atas tidak akan terjadi.
> >> from module import some_weird_name_func_ , _another_weird_name_func
> >> _another_weird_name_func ()
works !
Jika Anda benar -benar ingin menggunakan impor wildcard, maka Anda harus menentukan daftar __all__
di modul Anda yang akan berisi daftar objek publik yang akan tersedia saat kami melakukan impor wildcard.
__all__ = [ '_another_weird_name_func' ]
def some_weird_name_func_ ():
print ( "works!" )
def _another_weird_name_func ():
print ( "works!" )
Keluaran
> >> _another_weird_name_func ()
"works!"
> >> some_weird_name_func_ ()
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name 'some_weird_name_func_' is not defined
> >> x = 7 , 8 , 9
> >> sorted ( x ) == x
False
> >> sorted ( x ) == sorted ( x )
True
> >> y = reversed ( x )
> >> sorted ( y ) == sorted ( y )
False
Metode sorted
selalu mengembalikan daftar, dan membandingkan daftar dan tupel selalu mengembalikan False
di Python.
> >> [] == tuple ()
False
> >> x = 7 , 8 , 9
> >> type ( x ), type ( sorted ( x ))
( tuple , list )
Tidak seperti sorted
, metode reversed
mengembalikan iterator. Mengapa? Karena penyortiran membutuhkan iterator untuk dimodifikasi di tempat atau menggunakan wadah tambahan (daftar), sedangkan pembalikan hanya dapat bekerja dengan berulang dari indeks terakhir ke yang pertama.
Jadi selama perbandingan sorted(y) == sorted(y)
, panggilan pertama untuk sorted()
akan mengkonsumsi iterator y
, dan panggilan berikutnya hanya akan mengembalikan daftar kosong.
> >> x = 7 , 8 , 9
> >> y = reversed ( x )
> >> sorted ( y ), sorted ( y )
([ 7 , 8 , 9 ], [])
from datetime import datetime
midnight = datetime ( 2018 , 1 , 1 , 0 , 0 )
midnight_time = midnight . time ()
noon = datetime ( 2018 , 1 , 1 , 12 , 0 )
noon_time = noon . time ()
if midnight_time :
print ( "Time at midnight is" , midnight_time )
if noon_time :
print ( "Time at noon is" , noon_time )
Output (<3,5):
( 'Time at noon is' , datetime . time ( 12 , 0 ))
Waktu tengah malam tidak dicetak.
Sebelum Python 3.5, nilai boolean untuk objek datetime.time
dianggap False
jika diwakili tengah malam di UTC. Ini rentan terhadap kesalahan saat menggunakan sintaks if obj:
untuk memeriksa apakah obj
nol atau setara dengan "kosong."
Bagian ini berisi beberapa hal yang kurang dikenal dan menarik tentang Python yang kebanyakan pemula seperti saya tidak menyadari (yah, tidak lagi).
Nah, ini dia
import antigravity
Output: SSHH ... ini sangat rahasia.
antigravity
adalah salah satu dari sedikit telur Paskah yang dilepaskan oleh pengembang Python.import antigravity
membuka browser web yang menunjuk ke komik XKCD klasik tentang Python.goto
, tapi mengapa? from goto import goto , label
for i in range ( 9 ):
for j in range ( 9 ):
for k in range ( 9 ):
print ( "I am trapped, please rescue!" )
if k == 2 :
goto . breakout # breaking out from a deeply nested loop
label . breakout
print ( "Freedom!" )
Output (Python 2.3):
I am trapped , please rescue !
I am trapped , please rescue !
Freedom !
goto
di Python diumumkan sebagai lelucon April Mop pada 1 April 2004.goto
tidak hadir dalam Python.Jika Anda adalah salah satu orang yang tidak suka menggunakan Whitespace di Python untuk menunjukkan lingkup, Anda dapat menggunakan c-style {} dengan mengimpor,
from __future__ import braces
Keluaran:
File "some_file.py" , line 1
from __future__ import braces
SyntaxError : not a chance
Kawat gigi? Mustahil! Jika Anda berpikir itu mengecewakan, gunakan Java. Oke, hal yang mengejutkan lainnya, dapatkah Anda menemukan di mana SyntaxError
yang diangkat dalam kode modul __future__
?
__future__
biasanya digunakan untuk menyediakan fitur dari versi masa depan Python. Namun "masa depan" dalam konteks khusus ini adalah ironis.future.c
.future.c
sebelum memperlakukannya sebagai pernyataan impor normal.Output (Python 3.x)
> >> from __future__ import barry_as_FLUFL
> >> "Ruby" != "Python" # there's no doubt about it
File "some_file.py" , line 1
"Ruby" != "Python"
^
SyntaxError : invalid syntax
>> > "Ruby" <> "Python"
True
Ini dia.
Ini relevan dengan PEP-401 yang dirilis pada 1 April 2009 (sekarang Anda tahu, apa artinya).
Mengutip dari pep-401
Diakui bahwa! = Operator Ketimpangan dalam Python 3.0 adalah kesalahan yang mengerikan dan menonjolkan kesalahan, flowf memulihkan kembali operator berlian sebagai ejaan tunggal.
Ada lebih banyak hal yang harus dibagikan Paman Barry dalam hal itu; Anda dapat membacanya di sini.
Ini bekerja dengan baik di lingkungan interaktif, tetapi akan meningkatkan SyntaxError
saat Anda menjalankan melalui file Python (lihat masalah ini). Namun, Anda dapat membungkus pernyataan di dalam eval
atau compile
untuk membuatnya berfungsi,
from __future__ import barry_as_FLUFL
print ( eval ( '"Ruby" <> "Python"' ))
import this
Tunggu, apa ini ? this
cinta ❤️
Keluaran:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Ini Zen of Python!
> >> love = this
> >> this is love
True
> >> love is True
False
> >> love is False
False
> >> love is not True or False
True
> >> love is not True or False ; love is love # Love is complicated
True
this
adalah telur Paskah untuk zen Python (Pep 20).love is not True or False; love is love
, ironis tapi jelas (jika tidak, lihat contoh yang terkait dengan is
dan is not
operator). Klausul else
untuk Loop. Salah satu contoh khas mungkin:
def does_exists_num ( l , to_find ):
for num in l :
if num == to_find :
print ( "Exists!" )
break
else :
print ( "Does not exist" )
Keluaran:
> >> some_list = [ 1 , 2 , 3 , 4 , 5 ]
> >> does_exists_num ( some_list , 4 )
Exists !
>> > does_exists_num ( some_list , - 1 )
Does not exist
Klausa else
dalam penanganan pengecualian. Contoh,
try :
pass
except :
print ( "Exception occurred!!!" )
else :
print ( "Try block executed successfully..." )
Keluaran:
Try block executed successfully ...
else
setelah satu loop dieksekusi hanya ketika tidak ada break
eksplisit setelah semua iterasi. Anda dapat menganggapnya sebagai klausa "nobreak".else
setelah blok percobaan juga disebut "Klausul Penyelesaian" sebagai mencapai Klausul else
dalam pernyataan try
berarti bahwa blok percobaan benar -benar selesai dengan sukses. def some_func ():
Ellipsis
Keluaran
> >> some_func ()
# No output, No Error
> >> SomeRandomString
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
NameError : name 'SomeRandomString' is not defined
> >> Ellipsis
Ellipsis
Ellipsis
adalah objek bawaan yang tersedia secara global yang setara dengan ...
> >> ...
Ellipsis
pass
) > >> import numpy as np
> >> three_dimensional_array = np . arange ( 8 ). reshape ( 2 , 2 , 2 )
array ([
[
[ 0 , 1 ],
[ 2 , 3 ]
],
[
[ 4 , 5 ],
[ 6 , 7 ]
]
])
three_dimensional_array
kami adalah array array array. Katakanlah kami ingin mencetak elemen kedua (indeks 1
) dari semua array terdalam, kami dapat menggunakan ellipsis untuk memotong semua dimensi sebelumnya > >> three_dimensional_array [:,:, 1 ]
array ([[ 1 , 3 ],
[ 5 , 7 ]])
> >> three_dimensional_array [..., 1 ] # using Ellipsis.
array ([[ 1 , 3 ],
[ 5 , 7 ]])
n_dimensional_array[firs_dim_slice, ..., last_dim_slice]
))(Callable[..., int]
atau Tuple[str, ...]
)))Ejaannya dimaksudkan. Tolong, jangan mengirimkan tambalan untuk ini.
Output (Python 3.x):
> >> infinity = float ( 'infinity' )
> >> hash ( infinity )
314159
> >> hash ( float ( '-inf' ))
- 314159
float('-inf')
adalah "-10⁵ x π" dalam Python 3, sedangkan "-10⁵ xe" dalam Python 2.1.
class Yo ( object ):
def __init__ ( self ):
self . __honey = True
self . bro = True
Keluaran:
> >> Yo (). bro
True
> >> Yo (). __honey
AttributeError : 'Yo' object has no attribute '__honey'
> >> Yo (). _Yo__honey
True
2.
class Yo ( object ):
def __init__ ( self ):
# Let's try something symmetrical this time
self . __honey__ = True
self . bro = True
Keluaran:
> >> Yo (). bro
True
> >> Yo (). _Yo__honey__
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AttributeError : 'Yo' object has no attribute '_Yo__honey__'
Mengapa Yo()._Yo__honey
bekerja?
3.
_A__variable = "Some value"
class A ( object ):
def some_func ( self ):
return __variable # not initialized anywhere yet
Keluaran:
> >> A (). __variable
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
AttributeError : 'A' object has no attribute '__variable'
> >> A (). some_func ()
'Some value'
__
(ganda menggarisbawahi alias "Dunder") dan tidak berakhir dengan lebih dari satu trailing garis bawah dengan menambahkan _NameOfTheClass
di depan.__honey
di cuplikan pertama, kami harus menambahkan _Yo
ke depan, yang akan mencegah konflik dengan atribut nama yang sama yang didefinisikan di kelas lain.__variable
dalam pernyataan return __variable
digerakkan ke _A__variable
, yang juga merupakan nama variabel yang kami nyatakan dalam lingkup luar.Keluaran:
> >> value = 11
> >> valuе = 32
> >> value
11
Wut?
Catatan: Cara termudah untuk mereproduksi ini adalah dengan cukup menyalin pernyataan dari cuplikan di atas dan menempelkannya ke dalam file/shell Anda.
Beberapa karakter non-barat terlihat identik dengan huruf dalam alfabet Inggris tetapi dianggap berbeda oleh penerjemah.
> >> ord ( 'е' ) # cyrillic 'e' (Ye)
1077
> >> ord ( 'e' ) # latin 'e', as used in English and typed using standard keyboard
101
> >> 'е' == 'e'
False
> >> value = 42 # latin e
> >> valuе = 23 # cyrillic 'e', Python 2.x interpreter would raise a `SyntaxError` here
> >> value
42
Fungsi bawaan ord()
mengembalikan titik kode unicode karakter, dan posisi kode yang berbeda dari cyrillic 'e' dan latin 'e' membenarkan perilaku contoh di atas.
# `pip install numpy` first.
import numpy as np
def energy_send ( x ):
# Initializing a numpy array
np . array ([ float ( x )])
def energy_receive ():
# Return an empty numpy array
return np . empty ((), dtype = np . float ). tolist ()
Keluaran:
> >> energy_send ( 123.456 )
> >> energy_receive ()
123.456
Dimana Hadiah Nobel?
energy_send
tidak dikembalikan, sehingga ruang memori bebas untuk dialokasikan kembali.numpy.empty()
mengembalikan slot memori bebas berikutnya tanpa menghidupkannya kembali. Tempat memori ini kebetulan sama dengan yang baru saja dibebaskan (biasanya, tetapi tidak selalu). def square ( x ):
"""
A simple function to calculate the square of a number by addition.
"""
sum_so_far = 0
for counter in range ( x ):
sum_so_far = sum_so_far + x
return sum_so_far
Output (Python 2.x):
> >> square ( 10 )
10
Bukankah seharusnya itu 100?
Catatan: Jika Anda tidak dapat mereproduksi ini, coba jalankan file Mixed_tabs_and_spaces.py melalui shell.
Jangan mencampur tab dan spasi! Karakter baru sebelum pengembalian adalah "tab", dan kode diindentasi oleh beberapa "4 spasi" di tempat lain dalam contoh.
Beginilah Python menangani tab:
Pertama, tab diganti (dari kiri ke kanan) dengan satu hingga delapan ruang sedemikian rupa sehingga jumlah total karakter hingga dan termasuk penggantian adalah kelipatan dari delapan <...>
Jadi "tab" di baris terakhir fungsi square
diganti dengan delapan spasi, dan masuk ke loop.
Python 3 cukup baik untuk melempar kesalahan untuk kasus tersebut secara otomatis.
Output (Python 3.x):
TabError : inconsistent use of tabs and spaces in indentation
+=
lebih cepat # using "+", three strings:
> >> timeit . timeit ( "s1 = s1 + s2 + s3" , setup = "s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000" , number = 100 )
0.25748300552368164
# using "+=", three strings:
> >> timeit . timeit ( "s1 += s2 + s3" , setup = "s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000" , number = 100 )
0.012188911437988281
+=
lebih cepat dari +
untuk menggabungkan lebih dari dua string karena string pertama (contoh, s1
untuk s1 += s2 + s3
) tidak dihancurkan saat menghitung string lengkap. def add_string_with_plus ( iters ):
s = ""
for i in range ( iters ):
s += "xyz"
assert len ( s ) == 3 * iters
def add_bytes_with_plus ( iters ):
s = b""
for i in range ( iters ):
s += b"xyz"
assert len ( s ) == 3 * iters
def add_string_with_format ( iters ):
fs = "{}" * iters
s = fs . format ( * ([ "xyz" ] * iters ))
assert len ( s ) == 3 * iters
def add_string_with_join ( iters ):
l = []
for i in range ( iters ):
l . append ( "xyz" )
s = "" . join ( l )
assert len ( s ) == 3 * iters
def convert_list_to_string ( l , iters ):
s = "" . join ( l )
assert len ( s ) == 3 * iters
Keluaran:
# Executed in ipython shell using %timeit for better readability of results.
# You can also use the timeit module in normal python shell/scriptm=, example usage below
# timeit.timeit('add_string_with_plus(10000)', number=1000, globals=globals())
> >> NUM_ITERS = 1000
> >> % timeit - n1000 add_string_with_plus ( NUM_ITERS )
124 µs ± 4.73 µs per loop ( mean ± std . dev . of 7 runs , 100 loops each )
> >> % timeit - n1000 add_bytes_with_plus ( NUM_ITERS )
211 µs ± 10.5 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_format ( NUM_ITERS )
61 µs ± 2.18 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_join ( NUM_ITERS )
117 µs ± 3.21 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> l = [ "xyz" ] * NUM_ITERS
> >> % timeit - n1000 convert_list_to_string ( l , NUM_ITERS )
10.1 µs ± 1.06 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
Mari kita tingkatkan jumlah iterasi dengan faktor 10.
> >> NUM_ITERS = 10000
> >> % timeit - n1000 add_string_with_plus ( NUM_ITERS ) # Linear increase in execution time
1.26 ms ± 76.8 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_bytes_with_plus ( NUM_ITERS ) # Quadratic increase
6.82 ms ± 134 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_format ( NUM_ITERS ) # Linear increase
645 µs ± 24.5 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n1000 add_string_with_join ( NUM_ITERS ) # Linear increase
1.17 ms ± 7.25 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> l = [ "xyz" ] * NUM_ITERS
> >> % timeit - n1000 convert_list_to_string ( l , NUM_ITERS ) # Linear increase
86.3 µs ± 2 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
Anda dapat membaca lebih lanjut tentang waktu atau %waktu pada tautan ini. Mereka digunakan untuk mengukur waktu eksekusi potongan kode.
Jangan gunakan +
untuk menghasilkan string panjang - di Python, str
tidak dapat diubah, sehingga string kiri dan kanan harus disalin ke dalam string baru untuk setiap pasangan rangkaian. Jika Anda menggabungkan empat string dengan panjang 10, Anda akan menyalin (10+10)+((10+10) +10)+(((10+10) +10) +10) = 90 karakter bukan hanya 40 karakter. Segalanya menjadi lebih buruk secara kuadratik karena jumlah dan ukuran string meningkat (dibenarkan dengan waktu eksekusi fungsi add_bytes_with_plus
)
Oleh karena itu, disarankan untuk menggunakan .format.
atau %
sintaks (namun, mereka sedikit lebih lambat dari +
untuk string yang sangat pendek).
Atau lebih baik, jika sudah ada konten yang tersedia dalam bentuk objek iterable, lalu gunakan ''.join(iterable_object)
yang jauh lebih cepat.
Tidak seperti add_bytes_with_plus
karena +=
optimisasi yang dibahas dalam contoh sebelumnya, add_string_with_plus
tidak menunjukkan peningkatan kuadratik dalam waktu eksekusi. Seandainya pernyataan telah s = s + "x" + "y" + "z"
bukan s += "xyz"
, peningkatannya akan bersifat kuadratik.
def add_string_with_plus ( iters ):
s = ""
for i in range ( iters ):
s = s + "x" + "y" + "z"
assert len ( s ) == 3 * iters
> >> % timeit - n100 add_string_with_plus ( 1000 )
388 µs ± 22.4 µs per loop ( mean ± std . dev . of 7 runs , 1000 loops each )
> >> % timeit - n100 add_string_with_plus ( 10000 ) # Quadratic increase in execution time
9 ms ± 298 µs per loop ( mean ± std . dev . of 7 runs , 100 loops each )
Begitu banyak cara untuk memformat dan membuat string raksasa agak kontras dengan zen python, yang menurutnya,
Seharusnya ada satu-dan lebih disukai hanya satu-cara yang menarik untuk melakukannya.
dict
* some_dict = { str ( i ): 1 for i in range ( 1_000_000 )}
another_dict = { str ( i ): 1 for i in range ( 1_000_000 )}
Keluaran:
> >> % timeit some_dict [ '5' ]
28.6 ns ± 0.115 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> some_dict [ 1 ] = 1
> >> % timeit some_dict [ '5' ]
37.2 ns ± 0.265 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> % timeit another_dict [ '5' ]
28.5 ns ± 0.142 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
> >> another_dict [ 1 ] # Trying to access a key that doesn't exist
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
KeyError : 1
> >> % timeit another_dict [ '5' ]
38.5 ns ± 0.0913 ns per loop ( mean ± std . dev . of 7 runs , 10000000 loops each )
Mengapa pencarian yang sama menjadi lebih lambat?
str
, int
, objek apa pun ...), dan yang khusus untuk kasus umum kamus yang terdiri dari kunci str
-sonly.lookdict_unicode
dalam sumber Cpython) mengetahui semua kunci yang ada (termasuk kunci pencarian) adalah string, dan menggunakan perbandingan string yang lebih cepat & lebih sederhana untuk membandingkan kunci, alih-alih memanggil metode __eq__
.dict
diakses dengan kunci non- str
, ini dimodifikasi sehingga pencarian di masa depan menggunakan fungsi generik.dict
tertentu, dan kunci bahkan tidak harus ada di kamus. Itu sebabnya mencoba pencarian yang gagal memiliki efek yang sama.dict
s * import sys
class SomeClass :
def __init__ ( self ):
self . some_attr1 = 1
self . some_attr2 = 2
self . some_attr3 = 3
self . some_attr4 = 4
def dict_size ( o ):
return sys . getsizeof ( o . __dict__ )
Output: (Python 3.8, versi Python 3 lainnya mungkin sedikit bervariasi)
> >> o1 = SomeClass ()
> >> o2 = SomeClass ()
> >> dict_size ( o1 )
104
> >> dict_size ( o2 )
104
> >> del o1 . some_attr1
> >> o3 = SomeClass ()
> >> dict_size ( o3 )
232
> >> dict_size ( o1 )
232
Mari kita coba lagi ... di juru bahasa baru:
> >> o1 = SomeClass ()
> >> o2 = SomeClass ()
> >> dict_size ( o1 )
104 # as expected
> >> o1 . some_attr5 = 5
> >> o1 . some_attr6 = 6
> >> dict_size ( o1 )
360
> >> dict_size ( o2 )
272
> >> o3 = SomeClass ()
> >> dict_size ( o3 )
232
Apa yang membuat kamus itu menjadi kembung? Dan mengapa objek yang baru dibuat kembung juga?
__init__
dari instance yang dibuat pertama, tanpa menyebabkan sebuah "unshare"). Jika ada banyak contoh ketika mengubah ukuran terjadi, pembagian kunci dinonaktifkan untuk semua contoh di masa mendatang dari kelas yang sama: CPYTHON tidak dapat mengetahui apakah contoh Anda menggunakan set atribut yang sama lagi, dan memutuskan untuk menyelamatkan untuk upaya berbagi mereka kunci.__init__
Anda! join()
adalah operasi string alih -alih operasi daftar. (Semacam kontra-intuitif pada penggunaan pertama)
Penjelasan: Jika join()
adalah metode pada string, maka dapat beroperasi pada apa pun yang dapat diulang (daftar, tuple, iterator). Jika itu adalah metode dalam daftar, itu harus diimplementasikan secara terpisah oleh setiap jenis. Juga, tidak masuk akal untuk menempatkan metode spesifik string pada API objek list
generik.
Beberapa pernyataan aneh tapi semantik yang benar:
[] = ()
adalah pernyataan yang benar secara semantik (membongkar tuple
kosong ke dalam list
kosong)'a'[0][0][0][0][0]
juga benar secara semantik, karena Python tidak memiliki tipe data karakter seperti bahasa lain yang bercabang dari C. jadi memilih satu karakter dari string yang mengembalikan A string karakter tunggal.3 --0-- 5 == 8
dan --5 == 5
keduanya adalah pernyataan yang benar secara semantik dan mengevaluasi True
. Mengingat bahwa a
adalah angka, ++a
dan --a
keduanya adalah pernyataan python yang valid tetapi tidak berperilaku sama dibandingkan dengan pernyataan serupa dalam bahasa seperti C, C ++, atau Java.
> >> a = 5
> >> a
5
> >> + + a
5
> >> - - a
5
Penjelasan:
++
dalam tata bahasa Python. Sebenarnya dua operator +
.++a
parse sebagai +(+a)
yang diterjemahkan ke a
. Demikian pula, output dari pernyataan --a
dibenarkan.Anda harus mengetahui operator Walrus di Python. Tetapi apakah Anda pernah mendengar tentang operator ruang-invader ?
> >> a = 42
> >> a -= - 1
> >> a
43
Ini digunakan sebagai operator penambahan alternatif, bersama dengan yang lain
> >> a += + 1
> >> a
> >> 44
Penjelasan: Prank ini berasal dari tweet Raymond Hettinger. Operator Space Invader sebenarnya hanya a -= (-1)
yang diubah. Yang setara dengan a = a - (- 1)
. Serupa untuk case a += (+ 1)
.
Python memiliki operator implikasi Converse yang tidak berdokumen.
> >> False ** False == True
True
> >> False ** True == False
True
> >> True ** False == True
True
> >> True ** True == True
True
Penjelasan: Jika Anda mengganti False
dan True
oleh 0 dan 1 dan melakukan matematika, tabel kebenaran setara dengan operator implikasi yang sebaliknya. (Sumber)
Karena kita berbicara tentang operator, ada juga @
operator untuk multiplikasi matriks (jangan khawatir, kali ini nyata).
> >> import numpy as np
> >> np . array ([ 2 , 2 , 2 ]) @ np . array ([ 7 , 8 , 8 ])
46
Penjelasan: Operator @
ditambahkan dalam Python 3.5 dengan mengingat komunitas ilmiah. Objek apa pun dapat membebani metode sihir __matmul__
untuk mendefinisikan perilaku untuk operator ini.
Dari Python 3.8 dan seterusnya Anda dapat menggunakan sintaks f-string khas seperti f'{some_var=}
untuk debugging cepat. Contoh,
> >> some_string = "wtfpython"
> >> f' { some_string = } '
"some_string='wtfpython'"
Python menggunakan 2 byte untuk penyimpanan variabel lokal dalam fungsi. Secara teori, ini berarti bahwa hanya 65536 variabel yang dapat didefinisikan dalam suatu fungsi. Namun, Python memiliki solusi praktis yang dibangun yang dapat digunakan untuk menyimpan lebih dari 2^16 nama variabel. Kode berikut menunjukkan apa yang terjadi di tumpukan ketika lebih dari 65536 variabel lokal didefinisikan (peringatan: kode ini mencetak sekitar 2^18 baris teks, jadi bersiaplah!):
import dis
exec ( """
def f():
""" + """
""" . join ([ "X" + str ( x ) + "=" + str ( x ) for x in range ( 65539 )]))
f ()
print ( dis . dis ( f ))
Beberapa utas Python tidak akan menjalankan kode Python Anda secara bersamaan (ya, Anda mendengarnya dengan benar!). Ini mungkin tampak intuitif untuk menelurkan beberapa utas dan membiarkannya menjalankan kode Python Anda secara bersamaan, tetapi, karena kunci penerjemah global di Python, yang Anda lakukan hanyalah membuat utas Anda dieksekusi pada giliran inti yang sama demi giliran. Benang Python bagus untuk tugas-tugas yang terikat IO, tetapi untuk mencapai paralelisasi aktual dalam Python untuk tugas-tugas yang terikat CPU, Anda mungkin ingin menggunakan modul multiproses python.
Terkadang, metode print
mungkin tidak segera mencetak nilai. Misalnya,
# File some_file.py
import time
print ( "wtfpython" , end = "_" )
time . sleep ( 3 )
Ini akan mencetak wtfpython
setelah 3 detik karena argumen end
karena buffer output memerah baik setelah bertemu n
atau ketika program selesai eksekusi. Kita dapat memaksa buffer untuk menyiram dengan melewati flush=True
.
Sebutkan pengiris dengan indeks Bounds tidak melempar kesalahan
> >> some_list = [ 1 , 2 , 3 , 4 , 5 ]
> >> some_list [ 111 :]
[]
Mengiris iterable tidak selalu membuat objek baru. Misalnya,
> >> some_str = "wtfpython"
> >> some_list = [ 'w' , 't' , 'f' , 'p' , 'y' , 't' , 'h' , 'o' , 'n' ]
> >> some_list is some_list [:] # False expected because a new object is created.
False
> >> some_str is some_str [:] # True because strings are immutable, so making a new object is of not much use.
True
int('١٢٣٤٥٦٧٨٩')
mengembalikan 123456789
dalam Python 3. Dalam Python, karakter desimal termasuk karakter digit, dan semua karakter yang dapat digunakan untuk membentuk nomor desimal-radix, misalnya U+0660, Digit Zero Digit Arab-Indik. Berikut adalah kisah menarik yang terkait dengan perilaku Python ini.
Anda dapat memisahkan literal numerik dengan garis bawah (untuk keterbacaan yang lebih baik) dari Python 3 dan seterusnya.
> >> six_million = 6_000_000
> >> six_million
6000000
> >> hex_address = 0xF00D_CAFE
> >> hex_address
4027435774
'abc'.count('') == 4
. Berikut adalah perkiraan implementasi metode count
, yang akan membuat hal -hal lebih jelas
def count ( s , sub ):
result = 0
for i in range ( len ( s ) + 1 - len ( sub )):
result += ( s [ i : i + len ( sub )] == sub )
return result
Perilaku ini disebabkan oleh pencocokan substring kosong ( ''
) dengan irisan panjang 0 di string asli.
Beberapa cara di mana Anda dapat berkontribusi pada wtfpython,
Silakan lihat Contributing.md untuk lebih jelasnya. Jangan ragu untuk membuat masalah baru untuk membahas berbagai hal.
PS: Tolong jangan menjangkau dengan permintaan backlinking, tidak ada tautan yang akan ditambahkan kecuali mereka sangat relevan dengan proyek.
Gagasan dan desain untuk koleksi ini awalnya terinspirasi oleh proyek Denyys Dovhan yang luar biasa WTFJS. Dukungan luar biasa oleh Pythonistas memberikan bentuknya saat ini.
© Satwik Kansal
If you like wtfpython, you can use these quick links to share it with your friends,
Twitter | Linkedin | Facebook
I've received a few requests for the pdf (and epub) version of wtfpython. You can add your details here to get them as soon as they are finished.
Itu saja semuanya! For upcoming content like this, you can add your email here.