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