它是我的Telegram频道@pythonetc中有关Python和编程的一些新技巧和窍门。
←
以前的出版物即使在
finally
子句中使用
break
语句,它也会抑制异常,即使未显示
except
块也是如此:
for i in range(10): try: 1 / i finally: print('finally') break print('after try') print('after while')
输出:
finally after while
对于
continue
也是如此,但是直到Python 3.8才能
finally
使用它:
SyntaxError: 'continue' not supported inside 'finally' clause
您可以在字符串文字中添加Unicode字符,不仅可以按其编号,还可以按其名称。
>>> '\N{EM DASH}' '—' >>> '\u2014' '—'
它也与f字符串兼容:
>>> width = 800 >>> f'Width \N{EM DASH} {width}' 'Width — 800'
Python对象有六种定义比较规则的魔术方法:
__lt__
表示<
__gt__
表示>
__le__
for <=
__ge__
代表>=
__eq__
==
__ne__
代表!=
如果其中一些方法未定义或返回
NotImplemented
,则将应用以下规则:
a.__lt__(b)
与b.__gt__(a)
a.__le__(b)
与b.__ge__(a)
a.__eq__(b)
与not a.__ne__(b)
(请注意,在这种情况下, a
和b
不会互换)
但是,
a >= b
和
a != b
不会自动暗示
a > b
。
functools.total_ordering
装饰器基于
__eq__
和以下之一创建所有六个方法:
__lt__
,
__gt__
,
__le__
或
__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')
有时您想同时使用功能的修饰版本和非修饰版本。 最简单的方法是放弃特殊的装饰器语法(带有
@
语法)并手动创建装饰的函数:
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'))
或者,您可以编写另一个装饰器,该装饰器在对函数进行装饰的同时将其原始版本保留在新函数的
orig
属性中:
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'))
如果您正在使用的所有装饰器都是通过
functools.wraps
创建的,则可以使用
__wrapped__
属性访问未修饰的函数:
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'))
但是请注意,它不适用于由多个装饰器装饰的功能:您必须为每个应用的装饰器访问
__wrapped__
:
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"')))
输出:
[4] ['4'] '4'
@saving_orig
提到的
@saving_orig
接受另一个装饰器作为参数。 如果该装饰器可以参数化怎么办? 好吧,由于参数化装饰器是一个返回实际装饰器的函数,因此这种情况会自动处理:
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"]')))
如果一个函数中应用了多个装饰器,则
@saving_orig
装饰器并不会真正实现我们想要的功能。 我们必须为每个这样的装饰器调用
orig
:
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"')))
输出:
[42] ['X'] 'X'
我们可以通过支持任意数量的装饰器作为
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"')))
输出:
[42] 'X'
另一种解决方案是使
saving_orig
足够智能,以将
orig
从一个装饰函数传递给另一个:
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)
如果您正在编写的装饰器变得过于复杂,则可以使用
__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
最后一行允许您同时使用驼峰式案例命名类,并将装饰器名称保持在蛇形案例中。
无需修改修饰的函数,您可以创建另一个可调用类以返回其实例而不是函数:
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
在
这里查看整个代码