воскресенье, 7 мая 2023 г.

Памятка по Python

Сравнение объектов

== - сравнивает содержимое
is - сравнивает указатель на объект
аналог between в sql:
>>> 0 <= a <= 10


Итерация и генерация

Объект итератор

class Counter:
    def __init__(self, low, high):
        self.current = low - 1
        self.high = high

    def __iter__(self):
        return self

    def __next__(self): 
        self.current += 1
        if self.current < self.high:
            return self.current
        raise StopIteration

Выражение генератор

def counter(text):
    return (n for n in [1,2,3,4,5])
если заменить () на [] то это будет обычный list compreh

Преобразование листа в итератор

iter([1,2,3,4,5])

Генератор

def counter(low, high):
    current = low
    while current < high:
        yield current
        current += 1
- при завершении итерации генератора вызывается StopIteration
- return в генераторе вызывает StopIteration с переданным значением
- передача сообщения в генератор:
def counter(low, high):
    current = low
    while current < high:
        value = yield current
        print(value)
        current += 1
gen = counter(1,5)
next(gen) #1
gen.send(55) #55 2
- последовательный вызов 2 генераторов:
def gen_two():
    yield from gen
    yield from gen2
сначала обойдется gen, потом gen_2

- без from сначала будет возвращен итератор 1, потом итератор 2
def gen_two():
    yield gen
    yield gen2

gt=gen_two()
gt1=next(gt)
gt2=next(gt)
next(gt1) #1
next(gt2) #6

Обход

- foreach
for country in ["Denmark", "Finland", "Norway", "Sweden"]:
    print(country)

- обход мапы:
for name, email in emails.items():
items() - преобразовывает мапу в список туплов

- обход списка с условием
[expression for item in iterable if condition]




Добавление и извелечение

добавление элемента в список (список мутирует)
>>> seeds = ["sesame", "sunflower"]
>>> seeds += ["pumpkin"]
объединение словарей (3,5+):
zs = {**xs, **ys}
или
zs = xs | ys
substr
seq[start:end:step]
reverse str
s[::-1]
кроме regex есть встроенные проверки на разные типы у строки:
s.isdigit()

Интерполяция и подстановка

подстановка через f (3.6+)
f'{datetime.now():%Y-%m-%d %H:%M:%S%z}'
подставновка по старому через format
"The novel '{0}' was published in {1}".format("Hard Times", 1854)
"{who} turned {age} this year".format(who="She", age=88)
подстановка с обращением к элементу множества:
{0[1]} #так же можно обращаться объектам через точку
подстановка с форматированием
"{0:25}".format(s) # минимальная ширина поля вывода 25
"{:%B %d, %Y}".format(datetime.now()) #форматирование даты
Удобный просмотрт объекта:
json.dumps(mapping, indent=4, sort_keys=True)
поддерживает не все типы данных, для всего лучше подходит:
pprint.pprint(mapping)

Коллекции

именнованный tuple (аналог case class scala)
from dataclasses import dataclass

@dataclass
class Book:
    title: str
    author: str
аналог
class RegularBook:
    def __init__(self, title, author):
        self.title = title
        self.author = author
получаем из коробки доп. методы __init__, __repr__, __str__ и __eq__

генерация списка
list(range(n))

В set могут попадать только immutable объекты, к которым применим hash (int, float, str, tuple, bool)
почти все коллекции в питоне мутабельные
Хэш массивы по умолчанию сортированы по очередности вставки, т.к. хэш массив состоит из обычного массива с данными и хэш массив, где в значениях индекс этого обычного массива.

Клонирование коллекции
поверхностное копирование - если в списке есть ссылочные объекты, то копируется ссылка
songs[:] #через равно была бы ссылка
для рекурсивного клонирования всего, нужно исопльзовать:
copy.deepcopy(x)

доступ к словарю с значением по умолчанию
userid.get(userid, 'всем') #всем будет, если userid не найдется
через мапу с гетом удобно делать ветвление, если в значение положить функцию

Сортировка по элементу объекта
sorted(xs.items(), key=lambda x: x[1]) #пример: по значению мапы

оператор членства
>>> p = (4, "frog", 9, -33, 9, 2)
>>> 2 in p #True
работает со строками как со списком байт:
"v" in "Peace is no longer permitted during Winterval"
разыменование массива в переменные функции
def f(a,b):
	return a + b

f(*[1,2]) #3

Параметры функций

неизменяемые типы: int, float, str, tuple, bool - передается по значению
остальное изменяемое: list, dict, set - передается по ссылке

По этому нельзя передавать пустой лист, т.к. его изменение в функции, изменит его везде
def append_if_even(x, lst=[]): # плохо!
распаковка параметров функции
def product(*args):
    for arg in args:
**kwargs - содержит именнованные параметры в виде мапы ({name: value})
def print_setup(**options):
    print(options['a'])
print_setup(a="123")

отключение позиционных параметров:
def heron2(a, b, c, *, units="meters"):
к параметру units можно обратиться только по имени

функции можно создавать с аннотациями типов:
def is_unicode_punctuation(s : str) -> bool:
vscode в этом случе использует типы для подсветки и подсказок.

Lambda функции

s = lambda x: "" if x == 1 else "s"
s(123) #s

Функции фильтрации, обхода и reduce:
list(map(lambda x: x ** 2, [1, 2, 3, 4]))
list(filter(lambda x: x > 0, [1, -2, 3, -4]))
functools.reduce(lambda x, y: x * y, [1, 2, 3, 4])

Импорт модулей

import os.path
аналог:
from os import path
пакет - каталог и файл __init__.py
в файле можно указывать:
- список модулей для загрузки всего:
__all__ = ["Bmp", "Jpeg", "Png", "Tiff", "Xpm"]

Предопредленные переменные:
__name__ - будет равно имени модуля при импорте, и __main__ - при запуске из консоли
if __name__ == "__main__":
    import doctest
    doctest.testmod()
__init.py__ - файл выполняется при импорте модуля (может быть создание каких то переменных, конекстов и т.д.)
динамическое программирование
__import__(...) - импорт модуля по имени
compile(source, file, - объект с результатом компиляции объекта

Классы

все классы неявные наследники object

встроенные методы:
__new__() - заготовка объекта, вызывается до init
__init__() - конструктор
__eq__() - для сравнения
__repr__() - строка, которую с помощью eval можно превратить обратно в объект
__str__() - представление при выводе в консоль

переменная объявленная в классе может модифицироваться из всех объектов
если создать внутри функции как self.var1 , то только из конкретного объекта

нет приватных методов, но их можно эмулировать через __var (префикс __ - дандер)
переменная переименовывается в _класс__переменная

декоратор @property - метод только для чтения

метод класса - удобно использовать для фабрики, когда возращается новый класс
@classmethod
def margherita(cls):
    return cls(['моцарелла', 'помидоры'])

есть множественное наследование
class FileStack(Undo, LoadSave):
- если есть одинаковые методы, то приоритет будет у самого левого класса.
вместо общих ошибок, с кастомным текстом, лучше делать сразу кастомные exception через наследование
class NameTooShortError(ValueError):
    pass
def validate(name):
    if len(name) < 10:
        raise NameTooShortError(name)

Декораторы

реализация своего декоратора - определяет новую локальную функцию, которая вызывает оригинальную функцию
def positive_result(function):
    def wrapper(*args, **kwargs):
        result = function(*args, **kwargs)
        assert result >= 0, function.__name__ + "() result isn't >= 0"
        return result
    wrapper.__name__ = function.__name__
    wrapper.__doc__ = function.__doc__
    return wrapper
более котороткий вариант, включает доку и название
def positive_result(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        result = function(*args, **kwargs)
        assert result >= 0, function.__name__ + "() result isn't >= 0"
        return result
    return wrapper

декоратор с параметрами: @bounded(0, 100)
def bounded(minimum, maximum):
    def decorator(function):
        @functools.wraps(function)
        def wrapper(*args, **kwargs):
            result = function(*args, **kwargs)
            if result < minimum:
                return minimum
            elif result > maximum:
                return maximum
            return result
        return wrapper
    return decorator

в декораторе есть название функции и передаваемые параметры, т.е. его удобно использовать для логирования и отладки (__debug__ - признак отладки)

к функции может быть применено несколько декораторов

Функции высшего порядка

Реализация через класс:
class Strip:
    def __init__(self, characters):
        self.characters = characters
    def __call__(self, string):
        return string.strip(self.characters)
        
strip_punctuation = Strip(",;:.!?") #__init__ класса
strip_punctuation("Land ahoy!") # __call__: 'Land ahoy'

Через вложенные функции:
def make_strip_function(characters):
    def strip_function(string):
        return string.strip(characters)
    return strip_function

Частично подготовить функции можно через functools:
reader = functools.partial(open, mode="rt", encoding="utf8")
reader("file.txt")

With - менеджер контекста

замена try + finnaly
with expression as variable:
    suite
expression должен реализовывать __init__, __enter__, __exit__(self, exc_type, exc_value, exc_traceback) - т.е. можно поглотить ошибку

contextlib - однострочный вариант нескольких конекстов:
with contextlib.nested(open(source), open(target, "w")) as (fin, fout):
    for line in fin:

Логирование

Пример логирования в базу данных:
class LogDBHandler(logging.Handler):
    def __init__(self, sql_conn, sql_cursor, db_tbl_log):
        logging.Handler.__init__(self)
        self.sql_cursor = sql_cursor
        self.sql_conn = sql_conn

    def emit(self, record):
        sql = 'INSERT INTO ...'
        try:
            self.sql_cursor.execute(sql)
            self.sql_conn.commit()
        except pymssql.Error as e:
            print sql

# Make the connection to database for the logger
log_conn = pymssql.connect(db_server, db_user, db_password, db_dbname, 30)
log_cursor = log_conn.cursor()
logdb = LogDBHandler(log_conn, log_cursor, db_tbl_log)
logging.handlers.LogDBHandler = LogDBHandler

Виртуальное окружение

создание виртуальной среды питон в папке ./venv:
python3 -m venv venv

- активация (переход) в виртуальную среду
source ~/venv/bin/activate
При активации прописываются пути в path:
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
export PATH
в коде дополнительно появляются переменные __VENV*__ с информацией об окружении

- установка пакетов будет идти в виртуальной папке и теперь не нужны права админа:
pip install schedule

- сохранение установленных пакетов в файл
pip freeze > requirements.txt
установка сохраненных зависимостей:
pip install -r requirements.txt

- список виртуальных окружений
sudo find ~ -name "activate" -type f

- выход из виртуальной среды
deactivate

Операторы Airflow, которые используют виртуальное окружение

- Использование существующего окружения:
my_isolated_task = ExternalPythonOperator(
    task_id="my_isolated_task",
    python_callable=my_isolated_function,
    python=os.environ["ASTRO_PYENV_epo_pyenv"]
)
- Создание своего окружения Должно быть установлен virtualenv в apache-airflow
pip install apache-airflow[virtualenv]
версии питона должны быть установлены и доступы аирфлоу заранее
my_isolated_task = PythonVirtualenvOperator(
    task_id="my_isolated_task",
    python_callable=my_isolated_function,
    requirements=["pandas==1.5.1"],
    python_version="3.10",  # specify the Python version
)

Популярные библиотеки

для обработки данных и машииного обучения:

Комментариев нет:

Отправить комментарий