电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月
电报频道
It is a new selection of tips and tricks about Python and programming from my ********-channel @pythonetc.
这是我的********频道@pythonetc中有关Python和编程的一些新技巧和窍门。
← 以前的出版物
即使在
break
statement suppresses exception if used in the
finally
子句中使用
finally
clause even when the
break
语句,它也会抑制异常,即使未显示
except
block is not presented:
except
块也是如此:
for i in range(10):
try:
1 / i
finally:
print('finally')
break
print('after try')
print('after while')
Output:
输出:
finally
after while
The same is true for continue
, however it can’t be used in finally
until Python 3.8:
对于continue
也是如此,但是直到Python 3.8才能finally
使用它:
SyntaxError: 'continue' not supported inside 'finally' clause
You can add Unicode characters in a string literal not only by its number, but by also by its name.
您可以在字符串文字中添加Unicode字符,不仅可以按其编号,还可以按其名称。
>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'
It’s also compatible with f-strings:
它也与f字符串兼容:
>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'
There are six magic methods for Python objects that define comparison rules:
Python对象有六种定义比较规则的魔术方法:
-
__lt__
for<
__lt__
表示<
-
__gt__
for>
__gt__
表示>
-
__le__
for<=
__le__
for<=
-
__ge__
for>=
__ge__
代表>=
-
__eq__
for==
__eq__
==
-
__ne__
for!=
__ne__
代表!=
If some of these methods are not defined or return NotImplemented
, the following rules applied:
如果其中一些方法未定义或返回NotImplemented
,则将应用以下规则:
-
a.__lt__(b)
is the same asb.__gt__(a)
a.__lt__(b)
与b.__gt__(a)
-
a.__le__(b)
is the same asb.__ge__(a)
a.__le__(b)
与b.__ge__(a)
-
a.__eq__(b)
is the same asnot a.__ne__(b)
(mind thata
andb
are not swapped in this case)a.__eq__(b)
与not a.__ne__(b)
(请注意,在这种情况下不交换a
和b
)
However, a >= b
and a != b
don’t automatically imply a > b
. The functools.total_ordering
decorator create all six methods based on __eq__
and one of the following: __lt__
, __gt__
, __le__
, or __ge__
.
但是, 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')
Sometimes you want to use both decorated and undecorated versions of a function. The easiest way to achieve that is to forgo the special decorator syntax (the one with @
) and create the decorated function manually:
有时您想同时使用功能的修饰版本和非修饰版本。 最简单的方法是放弃特殊的装饰器语法(带有@
装饰器语法)并手动创建装饰的函数:
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
Alternatively, you can write another decorator, that decorate a function while preserving its original version in the orig
attribute of the new one:
另外,您可以编写另一个装饰器,该装饰器在对函数进行装饰的同时将其原始版本保留在新函数的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
If all decorators you are working with are created via functools.wraps
you can use the __wrapped__
attribute to access the undecorated function:
如果您正在使用的所有装饰器都是通过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
Mind, however, that it doesn’t work for functions that are decorated by more than one decorator: you have to access __wrapped__
for each decorator applied:
但是请注意,它不适用于由多个装饰器装饰的函数:您必须为每个应用的装饰器访问__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"')))
Output:
输出:
[4]
['4']
'4'
The @saving_orig
mentioned above accepts another decorator as an argument. What if that decorator can be parametrized? Well, since parameterized decorator is a function that returns an actual decorator, this case is handled automatically:
@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"]')))
The @saving_orig
decorator doesn’t really do what we want if there are more than one decorator applied to a function. We have to call orig
for each such decorator:
如果一个函数中应用了多个装饰器,则@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"')))
Output:
输出:
[42]
['X']
'X'
We can fix it by supporting arbitrary number of decorators as saving_orig
arguments:
我们可以通过支持任意数量的装饰器作为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"')))
Output:
输出:
[42]
'X'
Another solution is to make saving_orig
smart enough to pass orig
from one decorated function to another:
另一种解决方案是使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)
If a decorator you are writing becomes too complicated, it may be reasonable to transform it from a function to a class with the __call__
method
如果您正在编写的装饰器变得过于复杂,则可以使用__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
The last line allows you both to name class with camel case and keep the decorator name in snake case.
最后一行允许您使用驼峰式大小写类,并以蛇形保持装饰器名称。
Instead of modifying the decorated function you can create another callable class to return its instances instead of a function:
无需修改修饰的函数,您可以创建另一个可调用类以返回其实例而不是函数:
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
View the whole code here
在这里查看整个代码
电报频道