Compilación de @Pythonetc, enero de 2020



Una nueva selección de consejos y programación de Python de mi feed @pythonetc.

Publicaciones anteriores


El orden de los bloques es exceptimportante: si varios bloques pueden atrapar una excepción, entonces el bloque superior la atrapará. Este código no funcionará según lo previsto:

import logging

def get(storage, key, default):
    try:
        return storage[key]
    except LookupError:
        return default
    except IndexError:
        return get(storage, 0, default)
    except TypeError:
        logging.exception('unsupported key')
        return default

print(get([1], 0, 42))  # 1
print(get([1], 10, 42))  # 42
print(get([1], 'x', 42))  # error msg, 42

except IndexErrorno funcionará porque IndexErrores una subclase LookupError. Una excepción más específica siempre debe ser mayor:

import logging

def get(storage, key, default):
    try:
        return storage[key]
    except IndexError:
        return get(storage, 0, default)
    except LookupError:
        return default
    except TypeError:
        logging.exception('unsupported key')
    return default

print(get([1], 0, 42))  # 1
print(get([1], 10, 42))  # 1
print(get([1], 'x', 42))  # error msg, 42


Python admite la asignación concurrente. Esto significa que todas las variables cambian inmediatamente después de evaluar todas las expresiones. Además, puede usar cualquier expresión que admita la asignación, y no solo las variables:

def shift_inplace(lst, k):
    size = len(lst)
    lst[k:], lst[0:k] = lst[0:-k], lst[-k:]

lst = list(range(10))

shift_inplace(lst, -3)
print(lst)
# [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]

shift_inplace(lst, 5)
print(lst)
# [8, 9, 0, 1, 2, 3, 4, 5, 6, 7]


Python no usará automáticamente la suma de números negativos en lugar de restar. Considere un ejemplo:

class Velocity:
    SPEED_OF_LIGHT = 299_792_458

    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return type(self)(
            (self.amount + other.amount) /
            (
                1 +
                self.amount * other.amount /
                self.SPEED_OF_LIGHT ** 2
            )
        )

    def __neg__(self):
        return type(self)(-self.amount)

    def __str__(self):
        amount = int(self.amount)
        return f'{amount} m/s'

Este código no funciona:

v1 = Velocity(20_000_000)
v2 = Velocity(10_000_000)

print(v1 - v2)
# TypeError: unsupported operand type(s) for -: 'Velocity' and 'Velocity


Divertido, pero este código funciona:

v1 = Velocity(20_000_000)
v2 = Velocity(10_000_000)

print(v1 +- v2)
# 10022302 m/s


Esta parte está escrita por un usuario de Telegram. orsinium.

Una función no puede ser tanto un generador como una función regular. Si se usa en el cuerpo de una función yield, se convierte en un generador:

def zeros(*, count: int, lazy: bool):
        if lazy:
            for _ in range(count):
                yield 0
            else:
                return [0] * count

zeros(count=10, lazy=True)
# <generator object zeros at 0x7ff0062f2a98>

zeros(count=10, lazy=False)
# <generator object zeros at 0x7ff0073da570>

list(zeros(count=10, lazy=False))
# []

Sin embargo, una función regular puede devolver otro iterador:

def _lazy_zeros(*, count: int):
    for _ in range(count):
        yield 0
    
def zeros(*, count: int, lazy: bool):
    if lazy:
        return _lazy_zeros(count=count)
    return [0] * count

zeros(count=10, lazy=True)
# <generator object _lazy_zeros at 0x7ff0062f2750>

zeros(count=10, lazy=False)
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Y esta opción puede ser útil en casos con simples generadores de expresión:

def zeros(*, count: int, lazy: bool):
    if lazy:
        return (0 for _ in range(count))
    return [0] * count


Al crear la comprensión del generador, debe usar paréntesis:

>>> g = x**x for x in range(10)
    File "<stdin>", line 1
        g = x**x for x in range(10)
            ^
SyntaxError: invalid syntax
>>> g = (x**x for x in range(10))
>>> g
<generator object <genexpr> at 0x7f90ed650258>


Sin embargo, pueden omitirse si la comprensión es el único argumento para la función:

>>> list((x**x for x in range(4)))
[1, 1, 4, 27]
>>> list(x**x for x in range(4))
[1, 1, 4, 27]


Esto no es cierto para las funciones que tienen múltiples argumentos:

>>> print((x**x for x in range(4)), end='\n')
<generator object <genexpr> at 0x7f90ed650468>
>>>
>>>
>>> print(x**x for x in range(4), end='\n')
    File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

Source: https://habr.com/ru/post/undefined/


All Articles