Esta é a décima primeira seleção de dicas e programação em Python no meu feed @pythonetc.
← 
Coleções anterioresA 
break bloqueia uma exceção se aplicada em um bloco 
finally , mesmo que não exista, 
except bloco:
 for i in range(10): try: 1 / i finally: print('finally') break print('after try') print('after while') 
Resultado:
 finally after while 
O mesmo vale para 
continue , no entanto, essa expressão só pode ser usada 
finally até o Python versão 3.8:
 SyntaxError: 'continue' not supported inside 'finally' clause 
Você pode adicionar caracteres Unicode a literais de cadeia de caracteres, não apenas por seus índices, mas também por nome.
 >>> '\N{EM DASH}' '—' >>> '\u2014' '—' 
Este método também é compatível com as linhas f:
 >>> width = 800 >>> f'Width \N{EM DASH} {width}' 'Width — 800' 
Existem seis métodos "mágicos" para objetos Python que definem as regras para comparação:
- __lt__para- <
- __gt__para- >
- __le__para- <=
- __ge__para- >=
- __eq__para- ==
- __ne__for- !=
Se algum desses métodos não estiver definido ou retornar 
NotImplemented , as seguintes regras serão aplicadas:
- a.__lt__(b)o mesmo que- b.__gt__(a)
- a.__le__(b)o mesmo que- b.__ge__(a)
- a.__eq__(b)o mesmo que- not a.__ne__(b)(observe que, neste caso,- bnão mudaram de lugar)
No entanto, as condições 
a >= b e 
a != b não significam automaticamente que 
a > b . O decorador 
functools.total_ordering cria todos os seis métodos baseados em 
__eq__ e um deles: 
__lt__ , 
__gt__ , 
__le__ ou 
__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') 
Às vezes, você precisa usar as versões decorada e não decorada da função. A maneira mais fácil de conseguir isso é se você não usar uma sintaxe especial de decoração (com o símbolo 
@ ) e criar uma função de decoração manualmente:
 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'))  
Ou você pode escrever um decorador que decora a função, preservando a versão original em seu atributo 
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'))  
Se todos os seus decoradores forem criados por meio de 
functools.wraps , você poderá usar o atributo 
__wrapped__ para acessar uma função não decorada:
 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'))  
Mas lembre-se de que essa abordagem não funciona para funções decoradas com mais de um decorador: você deve consultar 
__wrapped__ cada um dos decoradores aplicados:
 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"'))) 
Resultado:
 [4] ['4'] '4' 
O decorador 
@saving_orig mencionado acima aceita outro decorador como argumento. E se será parametrizado? Como um decorador parametrizado é uma função que retorna um decorador real, essa situação é processada automaticamente:
 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"]'))) 
O decorador 
@saving_orig não fará o que queremos se vários decoradores forem aplicados à função. Então, para cada um deles, você deve chamar 
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"'))) 
Resultado:
 [42] ['X'] 'X' 
Isso pode ser 
saving_orig suportando um número arbitrário de decoradores como argumentos para 
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"'))) 
Resultado:
 [42] 'X' 
Existe outra solução: faça o 
saving_orig passar 
orig de uma função decorada para outra:
 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) 
Quando o decorador fica muito complicado, é melhor convertê-lo de uma função para uma classe com o método 
__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 
A última linha permite que você nomeie a classe no estojo Camel e salve o nome do decorador no estojo Snake.
Em vez de converter a função decorada, você pode criar outra classe chamada e retornar instâncias da função em vez da função:
 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 
Todo o código está disponível 
aqui.