
Size: a a a


items = [1, 3, 5, -17, 20, 3, -6]
for x in items:
    if x < 0:
        print(x)
        break
else:
    print('not found')list(filter(lambda x: x < 0, items))[0][x for x in items if x < 0][0]next(x for x in items if x < 0)items = [1, 2, 4]
result = next((x for x in items if x < 0), 'not found')
print(result)  # not founddef is_odd(x):
    return x % 2 != 0
next(x for x in items if is_odd(x))
# или еще лучше
next(filter(is_odd, items))
product() – прямое (Декартово) произведение одной или нескольких последовательностей.permutations() – перестановки и размещения элементов последовательности. combinations() – уникальные комбинации из элементов последовательности.combinations_with_replacement() – комбинации с замещениями.>>> print(*product([1, 2, 3], 'XY'))
(1, 'X') (1, 'Y') (2, 'X') (2, 'Y') (3, 'X') (3, 'Y')
>>> print(*combinations([1, 2, 3], 2))
(1, 2) (1, 3) (2, 3)
>>> print(*permutations([1, 2, 3], 2))
(1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2)combinations_with_replacement(). Не зря же они ее туда добавили? :)



def make_adder(a):
    def adder(x):
        return a + x
    return adder
plus_5 = make_adder(5)
print(plus_5(3))  # 8def make_adders():
    adders = []
    for a in range(3):
        def adder(x):
            return a + x
        adders.append(adder)
    return adders
adders = make_adders()
for adder in adders:
    print(adder(2))  # 4 4 4a, значение которой останется равным 2 после выполнения всего цикла целиком.a=a:def make_adders():
    adders = []
    for a in range(3):
        def adder(x, a=a):  # FIX!
            return a + x
        adders.append(adder)
    return adders
adders = make_adders()
for adder in adders:
    print(adder(2))  # 2 3 4def adder(x, that_a=a):  # FIX!
    return that_a + x
def make_adders():
    for a in range(3):
        def adder(x):
            return a + x
        yield addera=a из поста выше! Казалось бы, что код должен также содержать в себе баг и выводить "4 4 4", но он работает, как задумано изначально:adders = make_adders()
for adder in adders:
    print(adder(2))  # 2 3 4adders = list(make_adders())
for adder in adders:
    print(adder(2))  # 4 4 4a = 0adder(2) = 0 + 2 = 2a = 1adder(2) = 1 + 2 = 2a = 2, и все функции выдают одинаковый результат.adders = make_adders()
for adder in adders:
    print(adder(2))  # 2 3 4next(adders)  # StopIteration

loop.run_in_executor.import asyncio
from functools import wraps, partial
def async_wrap(func):
    @wraps(func)
    async def run(*args, loop=None, executor=None, **kwargs):
        if loop is None:
            loop = asyncio.get_event_loop()
        pfunc = partial(func, *args, **kwargs)
        return await loop.run_in_executor(executor, pfunc)
    return run # превращаем обычный sleep в асинхронный
import time
async_sleep = async_wrap(time.sleep)  
# асинхронное удаление файлов
import os
async_remove = async_wrap(os.remove)
# своя функция
@async_wrap
def my_func(duration):
    print("my_func start")
    time.sleep(1)
    print("my_func end")# применение
async def main():
    await asyncio.gather(async_sleep(3), my_func(2))
    await my_func(1)
asyncio.run(main())await my_func(1, loop=myloop)ProcessPoolExecutor), что может повлечь за собой неожиданные эффекты. Например, matplotlib на macOS часто падает, если построение графика совершается через executor.



from collections import namedtuple
# новый класс User с полями name и phone
User = namedtuple('User', ['name', 'phone'])
# конкретный юзер - экземпляр
user1 = User('John', phone='+79991002030')
print(user1)  # User(name='John', phone='+79991002030')'name, phone' или даже 'name phone'. Имена полей любые, кроме зарезервированных слов и имен, начинающихся с подчеркивания.>> Point = namedtuple('Point', ['x', 'y'])
>> p = Point(x=11, y=22)  # создание
>>> p[0] + p[1]    # доступ по индексам
33
>>> p.x + p.y   # доступ по именам
33
>>> x, y = p   # распаковка, как обычный кортеж
>>> x, y
(11, 22)
>>> p    # читабельное repr
Point(x=11, y=22)
>>> Point._make((22, 33))  # создание из обычного кортежа или другого итерируемого объекта
Point(x=22, y=33)
>>> p._asdict()  # представление в форме словаря
{'x': 11, 'y': 22}
from collections import namedtuple
User = namedtuple('User', ['name', 'phone'])
user = User(name='Jack')  # ошибка! не указан phone!User = namedtuple('User', ['name', 'phone'], defaults=('NoName', 'NoPhone'))
user = User(name='Jack') 
>> user
User(name='Jack', phone='NoPhone')
>>> User()  # вообще без параметров
User(name='NoName', phone='NoPhone')fields = ('val', 'left', 'right')
Node = namedtuple('Node', fields, defaults=(None,) * len(fields))
>>> Node()
Node(val=None, left=None, right=None)Node = namedtuple('Node', ('val', 'left', 'right'), defaults=(100, 200))'left' и 'right', а 'val' останется без дефолтного значения, вынуждая нас всегда его указывать:>>> Node()
TypeError: __new__() missing 1 required positional argument: 'val'
>>> Node(13)
Node(val=13, left=100, right=200)
>>> Node(val=42)
Node(val=42, left=100, right=200)'users':Group = namedtuple('Node', ('name', 'users'), defaults=('5B', []))  # плохо!
g = Group()
g.users.append('Vanya')  # повлияет на g2 тоже!
g2 = Group()
print(g2.users)  # ['Vanya']def new_empty_group(name='5B'):
    return Group(name=name, users=[])
_replace возвращает новый объект, в котором отредактированы выбраные поля, а все остальные равны значениям из предыдущего кортежа.from collections import namedtuple
Book = namedtuple('Book', ['id', 'title', 'authors'])
book1 = Book(1, 'Игрок', 'Достоевский Ф.М.')
book2 = book1._replace(id=2, title='Преступление и наказание')
>>> book1
Book(id=1, title='Игрок', authors='Достоевский Ф.М.')
>>> book2
Book(id=2, title='Преступление и наказание', authors='Достоевский Ф.М.')# сам класс
Book.__doc__ += ': Hardcover book in active collection'
# его поля
Book.id.__doc__ = '13-digit ISBN'
Book.title.__doc__ = 'Title of first printing'
Book.authors.__doc__ = 'List of authors sorted by last name'help(Book) и увидите, что у Book есть теперь описание класса и всех полей. А также по умолчанию namedtuple добавляет кучу стандартной документации по методам класса.
hypot, рассчитывающий расстояние от начала координат.class Point(namedtuple('Point', ['x', 'y'])):
    _ _ slots = ()
    @property
    def hypot(self):
        return (self.x  2 + self.y  2) ** 0.5
    def __str__(self):
        return f'Point: x={self.x}  y={self.y}  hypot={self.hypot}'
>>> print(Point(1, 2))
Point: x=1  y=2  hypot=2.23606797749979from typing import NamedTuple
class Employee(NamedTuple):
    name: str
    id: intEmployee = namedtuple('Employee', ['name', 'id'])_ _ slots = () здесь играет особую роль. Я еще не рассказывал про слоты, но если вкратце, то таким образом мы предотвращаем создание внутреннего словаря для данного класса, что экономит еще 8 байт.
class Computer:
    def __init__(self, cores, ram=2048):
        self.cores = cores
        self.ram = ram
    def __repr__(self):
        return f'Computer({self.cores}, {self.ram})'
    # прочие методы, eqfrom dataclasses import dataclass
@dataclass
class Computer:
    cores: int
    ram: int = 2048
print(Computer(2))  # Computer(cores=2, ram=2048)
@dataclass
class Number:
    value: int  # поле, сгенерируется код
    foo = 10  # просто переменная уровня класса, будет проигнорирована
print(Number(10, foo=20))  # ошибка!from dataclasses import make_dataclass
Computer = make_dataclass('Computer', ('ip', 'cores', 'ram'))from dataclasses import asdict
asdict(Computer(cores=8, ram=2**15))  # {'cores': 8, 'ram': 32768}
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class C:
   ...
init (вкл) – генерация метода инициализации. Игнорируется, если у класса уже есть свой init.repr (вкл) – генерации метода repr для класса. Дает удобное представление класса при печати его в консоль или в логи.eq (вкл) – генерировать ли метод равенства ==? Сравниваются все поля по очереди (похоже на tuple).order (выкл) – добавить ли методы сравнения (>, ≥, ≤, <)? Принцип сравнения такой же, как у tuple – поля сравниваются по порядку сверху вниз.frozen (выкл) – заморозка полей. При попытке присвоения к полям класса возникнет исключение. Таким образом класс становится только для чтения (чем-то похож на namedtuple).unsafe_hash (выкл) – отвечает за генерацию метода hash. Почему unsafe (то есть небезопасный)? Потому что ваш класс может быть мутабельным (изменяемым), поэтому вы сами берете на себя ответственность за соответствия содержимого класса и его хэша. Ситуация, когда сначала от экземпляра берут хэш, чтобы, например, сохранить его во множестве, а потом поля класса меняются, приводит к наличию двух одинаковых элементов во множестве. Это противоречит его смыслу, но возможно. У меня был пост об этом. frozen=True и eq=True, то класс считается уже неизменяемым, и к нему автоматически генерируется hash-функция, поэтому не требуется дополнительно ставить unsafe_hash=True.from dataclasses import dataclass
@dataclass(order=True)
class Player:
    name: str
    score: int
p1 = Player('Maksim', 10)
p2 = Player('Ivan', 30)
p3 = Player('Ivan', 40)
print(f'{p1} > {p2} = {p1 > p2}')  # True: Maksim > Ivan
print(f'{p2} > {p3} = {p2 > p3}')  # False: Ivan == Ivan, но 30 < 40!Maksim > Ivan, получается что p1 > p2. Сравнение даже не смотрит на поле score (счет).score. Следовательно, p2 > p3 == False, потому что p2.score < p3.score.field).field позволяет настраивать каждое поле класса индивидуально. Это тема для отдельного поста.
field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)@dataclass
class Foo:
    x: int = field(default=10)  # x: int = 10= []. В последних версиях Python здесь будет исключение уже на этапе определения класса! Иначе бы все экземпляры Class разделяли бы один и тот же объект списка в поле students:@dataclass
class Class:
    number: int
    students: list = []  # Ошибка!field с указанием default_factory.students: list = field(default_factory=lambda: [])
# или еще короче и правильнее вот так:
students: list = field(default_factory=list)default_factory=set или default_factory=dict или даже default_factory=MyClass, если у класса MyClass есть конструктор без обязательных аргументов.@dataclass
class User:
    name: str
    password: str = field(repr=False)
admin = User('admin', '1234')
print(admin)  # User(name='admin') init=False – ваш выбор.compare=False:@dataclass(order=True)
class User:
    name: str = field(compare=False)
    age: int
ken = User('Ken', 20)
linda = User('Linda', 18)
print(ken > linda)  # Trueage. Попробуйте убрать compare=False и увидите, что результат сравнения изменится.