欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

程序员文章站 2022-05-17 08:07:10
...

电报频道

电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

It is a new selection of tips and tricks about Python and programming from my ********-channel @pythonetc.

这是我的********频道@pythonetc中有关Python和编程的一些新技巧和窍门。

Previous publications

以前的出版物

电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

即使在 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
电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

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'
电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

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 as b.__gt__(a)

    a.__lt__(b)b.__gt__(a)

  • a.__le__(b) is the same as b.__ge__(a)

    a.__le__(b)b.__ge__(a)

  • a.__eq__(b) is the same as not a.__ne__(b) (mind that a and b are not swapped in this case)

    a.__eq__(b)not a.__ne__(b) (请注意,在这种情况下不交换ab )

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 >= ba != b不会自动暗示a > bfunctools.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')
电报频道_我的电报频道@pythonetc的提示和技巧,2019年5月

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

这里查看整个代码

翻译自: https://habr.com/en/company/mailru/blog/454648/

电报频道