Подборка @pythonetc, октябрь 2019

в 13:32, , рубрики: python, pythonetc, Блог компании Mail.Ru Group, Программирование
Подборка @pythonetc, октябрь 2019 - 1

Новая подборка советов про Python и программирование из моего авторского канала @pythonetc.

Предыдущие подборки

Подборка @pythonetc, октябрь 2019 - 2

Если хотите итерировать сразу несколько итерируемых объектов, то можете использовать функцию zip (не имеет никакого отношения к файловому формату ZIP):

from datetime import timedelta

names = [
    'Eleven. Return and Revert',
    'Wilderness',
    'The Menagerie Inside',
    'Evaporate',
]

years = [
    2010,
    2013,
    2015,
    2018,
]

durations = [
    timedelta(minutes=57, seconds=38),
    timedelta(minutes=48, seconds=5),
    timedelta(minutes=46, seconds=34),
    timedelta(minutes=43, seconds=25),
]

print('Midas Fall LPs:')
for name, year, duration in zip(
    names, years, durations
):
    print(f'  * {name} ({year}) — {duration}')

Результат:

Midas Fall LPs:
  * Eleven. Return and Revert (2010) — 0:57:38
  * Wilderness (2013) — 0:48:05
  * The Menagerie Inside (2015) — 0:46:34
  * Evaporate (2018) — 0:43:25

Подборка @pythonetc, октябрь 2019 - 3

Генератор можно остановить явным вызовом g.close(), но чаще всего сборщик мусора делает это за вас. После вызова close, в точке, где генерирующая функция была поставлена на паузу, инициируется GeneratorExit:

def gen():
    try:
        yield 1
        yield 2
    finally:
        print('END')


g = gen()
print(next(g))  # prints '1'
g.close()  # prints 'END'

Не забывайте о трёх аспектах. Во-первых, вы не можете продолжать генерировать значения при обработке GeneratorExit:

def gen():
    try:
        yield 1
    finally:
        yield 3


g = gen()
next(g)
g.close()  # RuntimeError

Во-вторых, если генератор ещё не запущен, то исключение не будет брошено, но генератор все равно перейдет в состояние «остановлен»:

def gen():
    try:
        yield 1
    finally:
        print('END')


g = gen()
g.close()  # nothing
print(list(g))  # prints '[]'

В-третьих, close ничего не делает, если генератор уже закончил работу:

def gen():
    try:
        yield 1
        yield 2
    finally:
        print('END')


g = gen()
print(list(g))
print('Closing now')
g.close()

# END
# [1, 2]
# Closing now

Подборка @pythonetc, октябрь 2019 - 4

f-строки позволяют задавать ширину выводимого значения, а также другие форматирующие спецификаторы:

>>> x = 42
>>> f'{x:5}+{x:15f}'
'   42+      42.000000'

Ещё они могут содержать вычисленные выражения, что бывает полезно, когда ширина не известна заранее:

def print_table(matrix):
    cols_width = [
        max(len(str(row[col])) for row in matrix)
        for col in range(len(matrix[0]))
    ]

    for row in matrix:
        for i, cell in enumerate(row):
            print(
                f'{cell:{cols_width[i]}} ',
                end=''
            )
        print()

albums = [
    ['Eleven. Return and Revert', 2010],
    ['Wilderness', 2013],
    ['The Menagerie Inside', 2015],
    ['Evaporate', 2018],
]

print_table(albums)

Результат:

Eleven. Return and Revert 2010
Wilderness                2013
The Menagerie Inside      2015
Evaporate                 2018

Подборка @pythonetc, октябрь 2019 - 5

Если ваш класс является производным от другого, то метакласс вашего класса тоже должен быть производным от метакласса того класса:

from collections import UserDict
from abc import ABCMeta

# ABCMeta is a metaclass of UserDict
class MyDictMeta(ABCMeta):
    def __new__(cls, name, bases, dct):
        return super().__new__(cls, name, bases, dct)

class MyDict(UserDict, metaclass=MyDictMeta):
    pass

Может быть целесообразно автоматически получать метакласс этого другого класса:

def create_my_dict_class(parents):
    class MyDictMeta(*[type(c) for c in parents]):
        def __new__(cls, name, bases, dct):
            return super().__new__(cls, name, bases, dct)

    class MyDict(*parents, metaclass=MyDictMeta):
        pass


MyDict = create_my_dict_class((UserDict,))

Подборка @pythonetc, октябрь 2019 - 6

__init__ позволяет модифицировать объект сразу после его создания. Если хотите контролировать созданное, то используйте __new__:

from typing import Tuple, Dict
from cached_property import cached_property


class Numbers:
    _LOADED: Dict[Tuple[int, ...], 'Numbers'] = {}

    def __new__(cls, ints: Tuple[int, ...]):
        if ints not in cls._LOADED:
            obj = super().__new__(cls)
            cls._LOADED[ints] = obj

        return cls._LOADED[ints]

    def __init__(self, ints: Tuple[int, ...]):
        self._ints = ints

    @cached_property
    def biggest(self):
        print('calculating...')
        return max(self._ints)


print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 6)).biggest)

Автор: pushtaev

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js