Pengampunan Python vs. Izin dan Mengetik Bebek

44

Dalam Python, saya sering mendengar bahwa lebih baik "meminta maaf" (pengecualian menangkap) daripada "meminta izin" (pemeriksaan jenis / kondisi). Dalam hal menegakkan itik mengetikkan Python, apakah ini

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

lebih baik atau lebih buruk daripada

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

dalam hal kinerja, keterbacaan, "pythonic", atau faktor penting lainnya?

darkfeline
sumber
17
ada opsi ketiga, jangan lakukan apa pun dan memperlakukan foo apa pun tanpa bilah sebagai bug
jk.
Saya ingat pendengaran yang hasattrditerapkan dengan mencoba / menangkap yang tepat secara internal. Tidak yakin apakah itu benar ... (itu akan bertindak berbeda pada properti, bukan? Mungkin saya memikirkan getattr..)
Izkata
@Izkata: Implementasihasattr tidak menggunakan setara C-API getattr(kembali Truejika berhasil, Falsejika tidak), tetapi menangani pengecualian dalam C jauh lebih cepat.
Martijn Pieters
2
Saya telah menerima jawaban martijn, tetapi saya ingin menambahkan bahwa jika Anda mencoba untuk menetapkan atribut, Anda harus mempertimbangkan untuk menggunakan try / catch karena itu mungkin sebuah properti tanpa setter, dalam hal ini hasattr akan benar , tetapi masih akan meningkatkan AttributeError.
darkfeline

Jawaban:

60

Itu benar-benar tergantung pada seberapa sering Anda berpikir pengecualian akan dilempar.

Kedua pendekatan itu, menurut saya, sama-sama valid, setidaknya dalam hal keterbacaan dan ke-pythonic. Tetapi jika 90% dari objek Anda tidak memiliki atribut barAnda akan melihat perbedaan kinerja yang berbeda antara kedua pendekatan:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

Tetapi jika 90% dari benda Anda lakukan memiliki atribut, tabel telah berubah:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

Jadi, dari sudut pandang kinerja, Anda perlu memilih pendekatan yang paling cocok untuk keadaan Anda.

Pada akhirnya, beberapa penggunaan strategis timeitmodul mungkin merupakan hal paling Pythonic yang dapat Anda lakukan.

Martijn Pieters
sumber
1
Beberapa minggu yang lalu saya mengajukan pertanyaan ini: programmers.stackexchange.com/questions/161798/... Di sana saya bertanya apakah dalam bahasa yang diketik longgar Anda harus bekerja ekstra melakukan pemeriksaan ketik, dan saya dibombardir oleh orang-orang yang mengatakan Anda tidak melakukannya. Tahu saya melihat Anda miliki.
Tulains Córdova
@ user1598390: Ketika Anda mendefinisikan API yang mengharapkan campuran tipe yang homogen, Anda harus melakukan beberapa tes. Sebagian besar waktu, Anda tidak melakukannya. Ini adalah bidang khusus yang saya tidak dapat menurunkan aturan tentang paradigma secara keseluruhan.
Martijn Pieters
Ya, setiap pengembangan sistem yang serius melibatkan pendefinisian API. Jadi saya kira jenis bahasa yang ketat adalah yang terbaik untuk itu karena Anda harus kode kurang sejak kompiler memeriksa jenis untuk Anda dalam waktu kompilasi.
Tulains Córdova
1
@ GarethRees: itu menetapkan pola untuk bagian kedua dari jawaban di mana saya menyampaikan argumen ke fungsi-diuji.
Martijn Pieters
1
Perhatikan bahwa untuk hasattr, sebenarnya C-api setara dengan coba-kecuali di bawah kap, karena ternyata satu-satunya cara umum untuk menentukan apakah suatu objek memiliki atribut dalam Python adalah dengan mencoba mengaksesnya.
user2357112
11

Dalam python Anda sering mendapatkan kinerja yang lebih baik dalam melakukan berbagai hal dengan cara Python. Dengan bahasa lain, menggunakan pengecualian untuk kontrol aliran umumnya dianggap sebagai ide yang buruk karena pengecualian biasanya membebankan biaya overhead yang luar biasa. Tetapi karena teknik ini secara eksplisit didukung dalam Python, juru bahasa dioptimalkan untuk jenis kode ini.

Seperti halnya semua pertanyaan kinerja, satu-satunya cara untuk memastikan adalah membuat profil kode Anda. Tulis kedua versi dan lihat mana yang berjalan lebih cepat. Meskipun dalam pengalaman saya, "cara Python" biasanya merupakan cara tercepat.

tylerl
sumber
3

Kinerja, saya rasa, adalah masalah sekunder. Jika itu muncul, seorang profiler akan membantu Anda fokus pada kemacetan nyata, yang mungkin atau mungkin bukan cara Anda memperlakukan kemungkinan argumen ilegal.

Di sisi lain, keterbacaan dan kesederhanaan selalu menjadi perhatian utama. Tidak ada aturan keras di sini, cukup gunakan penilaian Anda.

Ini adalah masalah universal, tetapi konvensi khusus lingkungan atau bahasa relevan. Sebagai contoh, dalam Python biasanya baik-baik saja menggunakan atribut yang Anda harapkan dan membiarkan AttributeError yang mungkin menjangkau pemanggil.

orip
sumber
-1

Dalam hal kebenaran , saya pikir penanganan pengecualian adalah cara untuk pergi (saya kadang-kadang menggunakan pendekatan hasattr () sendiri). Masalah mendasar dengan mengandalkan hasattr () adalah ia mengubah pelanggaran kontrak kode menjadi kegagalan diam (ini adalah masalah besar dalam JavaScript, yang tidak membuang properti yang tidak ada).

Joe
sumber
3
Jawaban Anda tampaknya tidak menambah banyak hal di luar apa yang telah dinyatakan oleh orang lain. Tidak seperti situs lain, Programmer mengharapkan jawaban yang menjelaskan mengapa di balik jawaban. Anda menyentuh titik bagus dengan masalah kegagalan diam, tetapi tidak memberikan keadilan untuk itu. Membaca yang berikut ini mungkin membantu: programmers.stackexchange.com/help/how-to-answer
Jawaban terakhir yang saya buat dikritik karena terlalu luas. Saya pikir saya akan mencoba singkat dan ringkas.
Joe