@Pythonetc编译,2019年5月



这是我的@pythonetc feed中的Python技巧和编程的第11种选择。

以前的收藏


即使在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) (请注意,在这种情况下, ab不会改变位置)

但是,条件a >= ba != b并不自动意味着a > bfunctools.total_ordering装饰器基于__eq__创建所有六个方法,这些方法之一是__lt__ __gt____le__ __ge__ __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')) # [3] print(load_data_orig('4')) 4 

或者,您可以编写一个装饰器来装饰该函数,同时在其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')) # [3] print(load_data.orig('4')) # 4 

如果所有装饰器都是通过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')) # [3] print(load_data.__wrapped__('4')) # 4 

但是请记住,这种方法不适用于由多个装饰器装饰的函数:您必须引用每个已应用装饰器的__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装饰器将另一个装饰器作为参数。 并且是否将其参数化? 由于参数化装饰器是返回实际装饰器的函数,因此会自动处理这种情况:

 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装饰器@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参数来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_origorig从一个装饰函数传递给另一个:

 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 

最后一行允许您在Camel情况下命名类,并在Snake情况下保存装饰器名称。

您可以创建另一个称为类的方法,而不是转换修饰的函数,然后返回该函数的实例而不是该函数:

 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 

所有代码都在这里。

Source: https://habr.com/ru/post/zh-CN454646/


All Articles