Ini adalah pilihan baru tips dan trik tentang Python dan pemrograman dari saluran-Telegram saya @pythonetc.
â
Publikasi sebelumnyapernyataan
break menekan pengecualian jika digunakan dalam klausa
finally bahkan ketika blok
except tidak disajikan:
for i in range(10): try: 1 / i finally: print('finally') break print('after try') print('after while')
Keluaran:
finally after while
Hal yang sama berlaku untuk
continue , namun akhirnya tidak dapat digunakan sampai Python 3.8:
SyntaxError: 'continue' not supported inside 'finally' clause
Anda dapat menambahkan karakter Unicode dalam string literal tidak hanya dengan nomornya, tetapi juga dengan namanya.
>>> '\N{EM DASH}' 'â' >>> '\u2014' 'â'
Ini juga kompatibel dengan f-string:
>>> width = 800 >>> f'Width \N{EM DASH} {width}' 'Width â 800'
Ada enam metode ajaib untuk objek Python yang menentukan aturan perbandingan:
__lt__ untuk <
__gt__ untuk >
__le__ untuk <=
__ge__ untuk >=
__eq__ untuk ==
__ne__ untuk !=
Jika beberapa metode ini tidak didefinisikan atau mengembalikan
NotImplemented , aturan berikut ini diterapkan:
a.__lt__(b) sama dengan b.__gt__(a)a.__le__(b) sama dengan b.__ge__(a)a.__eq__(b) sama dengan not a.__ne__(b) (pikiran bahwa a dan b tidak ditukar dalam kasus ini)
Namun,
a >= b dan
a != b tidak secara otomatis menyiratkan
a > b . Dekorator
functools.total_ordering membuat semua enam metode berdasarkan
__eq__ dan salah satu dari yang berikut:
__lt__ ,
__gt__ ,
__le__ , atau
__ge__ .
from functools import total_ordering @total_ordering class User: def __init__(self, pk, name): self.pk = pk self.name = name def __le__(self, other): return self.pk <= other.pk def __eq__(self, other): return self.pk == other.pk assert User(2, 'Vadim') < User(13, 'Catherine')
Terkadang Anda ingin menggunakan versi fungsi yang didekorasi dan tidak didekorasi. Cara termudah untuk mencapai itu adalah dengan melepaskan sintaks dekorator khusus (yang dengan
@ ) dan membuat fungsi yang didekorasi secara manual:
import json def ensure_list(f): def decorated(*args, **kwargs): result = f(*args, **kwargs) if isinstance(result, list): return result else: return [result] return decorated def load_data_orig(string): return json.loads(string) load_data = ensure_list(load_data_orig) print(load_data('3'))
Atau, Anda dapat menulis dekorator lain, yang menghiasi suatu fungsi sambil mempertahankan versi aslinya dalam atribut asli dari yang baru:
import json def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_list(f): ... @saving_orig(ensure_list) def load_data(string): return json.loads(string) print(load_data('3'))
Jika semua dekorator yang bekerja dengan Anda dibuat melalui
functools.wraps Anda dapat menggunakan atribut
__wrapped__ untuk mengakses fungsi yang tidak didekorasi:
import json from functools import wraps def ensure_list(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) if isinstance(result, list): return result else: return [result] return decorated @ensure_list def load_data(string): return json.loads(string) print(load_data('3'))
Namun,
__wrapped__ bahwa itu tidak berfungsi untuk fungsi yang didekorasi oleh lebih dari satu dekorator: Anda harus mengakses
__wrapped__ untuk setiap dekorator yang diterapkan:
def ensure_list(f): ... def ensure_ints(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) return [int(x) for x in result] return decorated @ensure_ints @ensure_list def load_data(string): return json.loads(string) for f in ( load_data, load_data.__wrapped__, load_data.__wrapped__.__wrapped__, ): print(repr(f('"4"')))
Keluaran:
[4] ['4'] '4'
@saving_orig disebutkan di atas menerima dekorator lain sebagai argumen. Bagaimana jika dekorator itu dapat parametrized? Nah, karena dekorator yang diparameterisasi adalah fungsi yang mengembalikan dekorator yang sebenarnya, kasing ini ditangani secara otomatis:
import json from functools import wraps def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_ints(*, default=None): def decorator(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) ints = [] for x in result: try: x_int = int(x) except ValueError: if default is None: raise else: x_int = default ints.append(x_int) return ints return decorated return decorator @saving_orig(ensure_ints(default=0)) def load_data(string): return json.loads(string) print(repr(load_data('["2", "3", "A"]'))) print(repr(load_data.orig('["2", "3", "A"]')))
Dekorator
@saving_orig tidak benar-benar melakukan apa yang kita inginkan jika ada lebih dari satu dekorator yang diterapkan pada suatu fungsi. Kita harus memanggil
orig untuk setiap dekorator seperti:
import json from functools import wraps def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_list(f): ... def ensure_ints(*, default=None): ... @saving_orig(ensure_ints(default=42)) @saving_orig(ensure_list) def load_data(string): return json.loads(string) for f in ( load_data, load_data.orig, load_data.orig.orig, ): print(repr(f('"X"')))
Keluaran:
[42] ['X'] 'X'
Kami dapat memperbaikinya dengan mendukung sejumlah dekorator
saving_orig sebagai argumen
saving_orig :
def saving_orig(*decorators): def decorator(f): decorated = f for d in reversed(decorators): decorated = d(decorated) decorated.orig = f return decorated return decorator ... @saving_orig( ensure_ints(default=42), ensure_list, ) def load_data(string): return json.loads(string) for f in ( load_data, load_data.orig, ): print(repr(f('"X"')))
Keluaran:
[42] 'X'
Solusi lain adalah membuat
saving_orig cukup pintar untuk melewatkan
orig dari satu fungsi yang didekorasi:
def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) if hasattr(f, 'orig'): decorated.orig = f.orig else: decorated.orig = f return decorated return decorator @saving_orig(ensure_ints(default=42)) @saving_orig(ensure_list) def load_data(string): return json.loads(string)
Jika dekorator yang Anda tulis menjadi terlalu rumit, mungkin masuk akal untuk mengubahnya dari fungsi ke kelas dengan metode
__call__ class SavingOrig: def __init__(self, another_decorator): self._another = another_decorator def __call__(self, f): decorated = self._another(f) if hasattr(f, 'orig'): decorated.orig = f.orig else: decorated.orig = f return decorated saving_orig = SavingOrig
Baris terakhir memungkinkan Anda berdua untuk memberi nama kelas dengan case unta dan menyimpan nama dekorator dalam case ular.
Alih-alih memodifikasi fungsi yang didekorasi, Anda dapat membuat kelas yang dapat dipanggil lainnya untuk mengembalikan instansnya alih-alih fungsi:
class CallableWithOrig: def __init__(self, to_call, orig): self._to_call = to_call self._orig = orig def __call__(self, *args, **kwargs): return self._to_call(*args, **kwargs) @property def orig(self): if isinstance(self._orig, type(self)): return self._orig.orig else: return self._orig class SavingOrig: def __init__(self, another_decorator): self._another = another_decorator def __call__(self, f): return CallableWithOrig(self._another(f), f) saving_orig = SavingOrig
Lihat seluruh kode
di sini