5 архитектурных ошибок, которые мы совершаем при старте проектов
Многие из нас с головой уходят в реализацию идеи, не задавая себе главный вопрос: а что будет, когда проект вырастет?
Аспекты вроде масштабирования, как и фундамент дома, нужно продумывать сначала, иначе потом вас ждет не апгрейд, а перестройка с нуля. А еще именно в самом начале проекта закладывается почва для ада зависимостей: спонтанные решения, быстрые фиксы, «временные» костыли — всё это превращается в хаос, который сложно контролировать.
В статье Tproger 5 самых частых архитектурных ошибок, которые мешают проектам расти и развиваться.
#основы
@zen_of_python
🙊 — Если сам так писал
Многие из нас с головой уходят в реализацию идеи, не задавая себе главный вопрос: а что будет, когда проект вырастет?
Аспекты вроде масштабирования, как и фундамент дома, нужно продумывать сначала, иначе потом вас ждет не апгрейд, а перестройка с нуля. А еще именно в самом начале проекта закладывается почва для ада зависимостей: спонтанные решения, быстрые фиксы, «временные» костыли — всё это превращается в хаос, который сложно контролировать.
В статье Tproger 5 самых частых архитектурных ошибок, которые мешают проектам расти и развиваться.
#основы
@zen_of_python
🙊 — Если сам так писал
☃2👍2🎉1
isinstance()
: Проверка типовВ динамически типизированных языках нам особенно важно знать тип объекта, которым мы оперируем. С этим помогают две встроенные функции —
type()
и isinstance()
, и мы поговорим сегодня о второй из них.
isinstance(object, classinfo)
—
object
: объект, тип которого вы хотите проверить—
classinfo
: класс, тип или кортеж типов
# Является ли 42 целочисленным значением?
isinstance(42, int) # True
# Относится ли "hello" к одному из типов str / list (логическое «ИЛИ»)?
isinstance("hello", (str, list)) # True
isinstance() vs type()
Поначалу может показаться, что
type()
делает то же самое:
type(42) == int # True
Но
isinstance()
, в свою очередь, учитывает наследование:
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
type(dog) == Animal # False
isinstance(dog, Animal) # True
Это делает
isinstance()
предпочтительным выбором при работе с иерархиями классов.#основы
👍13
Вы точно знаете отличия между
Каким будет результат выражения:
Большинство людей уверены, что Python вернёт True. Но если запустить код, мы увидим 5. Это происходит, потому что Python возвращает само значение операнда, если оно Truthy / Falsy.
Truthy / Falsy
Любой объект Python — либо «трушный», либо «ложный»:
— Falsy:
— Truthy: всё остальное
Оператор
Поведение or
Оператор возвращает первый Truthy операнд, если таковой есть; иначе — последний Falsy.
Идея: достаточно одной истины, и дальше Python не продолжает (Short‑Circuit Evaluation).
Поведение and
Оператор возвращает первый Falsy операнд, если он встретится; иначе — последний Truthy.
Логика: and требует, чтобы обе стороны были истинными, иначе выражение — ложь.
Приоритет not, and, or
Операторы имеют встроенный приоритет:
1.
2.
3.
Рекомендации
— Используйте скобки, чтобы явно показывать порядок операций;
— Не пишите слишком длинные цепочки с a
— Именованные логические условия помогают читать код.
#основы
or
и and
?Каким будет результат выражения:
5 or 0
Большинство людей уверены, что Python вернёт True. Но если запустить код, мы увидим 5. Это происходит, потому что Python возвращает само значение операнда, если оно Truthy / Falsy.
Truthy / Falsy
Любой объект Python — либо «трушный», либо «ложный»:
— Falsy:
0
, ""
, []
, {}
, None
, 0.0
— Truthy: всё остальное
Оператор
bool(obj)
конвертирует объект в логическое значение, но сами and
/ or
возвращают ненулевые сущности: последний вычисленный операнд.Поведение or
Оператор возвращает первый Truthy операнд, если таковой есть; иначе — последний Falsy.
5 or 0 # → 5 (первый truthy)
0 or 7 # → 7 (второе значение, truthy)
0 or '' or None # → None (все falsy, возвращается последний)
Идея: достаточно одной истины, и дальше Python не продолжает (Short‑Circuit Evaluation).
Поведение and
Оператор возвращает первый Falsy операнд, если он встретится; иначе — последний Truthy.
5 and 0 # → 0 (первый Falsy)
5 and 7 # → 7 (оба Truthy, возвращаем последний)
0 and 5 # → 0 (первый Falsy — возвращён)
Логика: and требует, чтобы обе стороны были истинными, иначе выражение — ложь.
Приоритет not, and, or
Операторы имеют встроенный приоритет:
1.
not
(наивысший)2.
and
3.
or
(наинизший)
True or False and False # → True, т. к. это эквивалентно True or (False and False)
(not False) or True # → True, сначала выполняется not.
Рекомендации
— Используйте скобки, чтобы явно показывать порядок операций;
— Не пишите слишком длинные цепочки с a
nd
/ or
без промежуточных переменных;— Именованные логические условия помогают читать код.
#основы
❤7👍6
Byte of Python | Бесплатный учебник, ставший классикой
Сейчас лето, и при должном везении ваш работодатель слегка расслабился. Можно и книжку почитать.
«Укус питона» — известный бесплатный учебник, который можно скачать в форматах .pdf / .epub вместе с Python-скриптами прямо из репозитория автора Сварупа.
Спасибо @Chellbas за рекомендацию.
#основы
@zen_of_python
Сейчас лето, и при должном везении ваш работодатель слегка расслабился. Можно и книжку почитать.
«Укус питона» — известный бесплатный учебник, который можно скачать в форматах .pdf / .epub вместе с Python-скриптами прямо из репозитория автора Сварупа.
Спасибо @Chellbas за рекомендацию.
#основы
@zen_of_python
❤8
argparse: зачем нужен и при чем здесь sys.argv[]?
При создании скриптов, которые запускаются из командной строки, часто возникает необходимость принимать аргументы. Чтобы эффективно разбирать и обрабатывать эти параметры, в стандартной библиотеке Python есть модуль
➡️ Какую роль играет sys.argv?
Например, если вы вызовете скрипт так:
то выведется такой перечень:
Однако
➡️ argparse: как с ним обращаться
Часто бывает так, что скрипт требует передать обязательный параметр — имя файла. Это называется позиционным аргументом, так как его положение в командной строке имеет значение.
Теперь, если запустить скрипт так:
Программа выведет:
Если попытаться запустить без аргумента:
то argparse автоматически покажет сообщение об ошибке и краткую справку:
Документация
#основы
@zen_of_python
🙏 — Если спасибо за такой контент
При создании скриптов, которые запускаются из командной строки, часто возникает необходимость принимать аргументы. Чтобы эффективно разбирать и обрабатывать эти параметры, в стандартной библиотеке Python есть модуль
argparse
. В этом лонгриде мы покажем на примере, как его использовать, покажем взаимосвязь с sys.argv
.sys.argv
— это список, содержащий аргументы командной строки, с которыми был запущен скрипт. Первый элемент списка sys.argv[0]
— это имя самого скрипта.Например, если вы вызовете скрипт так:
python script.py filename.txt -v
то выведется такой перечень:
import sys
print(sys.argv) # ['script.py', 'filename.txt', '-v']
Однако
sys.argv
— это просто список строк, и если работать с ним вручную, то придётся самостоятельно обрабатывать порядок, типы данных, проверять правильность и т.п., что может стать сложной задачей. Здесь на помощь приходит argparse
. Он автоматизирует парсинг аргументов и предоставляет удобные способы проверки параметров CLI.argparse
позволяет описать, какие параметры принимает ваш скрипт, какие из них обязательны, какие опциональны, какие могут быть флагами (включить / выключить). Он также автоматически генерирует справку и обрабатывает ошибки в вводе.Часто бывает так, что скрипт требует передать обязательный параметр — имя файла. Это называется позиционным аргументом, так как его положение в командной строке имеет значение.
import argparse
# Создаём парсер
parser = argparse.ArgumentParser(description='Обработка файла.') # Создаем объект ArgumentParser;
# Добавляем позиционный аргумент 'filename'
parser.add_argument('filename', help='имя файла для обработки') # Добавляем описание каждого аргумента или опции
# Разбираем аргументы командной строки
args = parser.parse_args() # Разберём аргументы sys.argv
# Fргументы доступны как атрибуты: args.filename
print(f'Обрабатываем файл: {args.filename}')
Теперь, если запустить скрипт так:
python script.py example.txt
Программа выведет:
Обрабатываем файл: example.txt
Если попытаться запустить без аргумента:
python script.py
то argparse автоматически покажет сообщение об ошибке и краткую справку:
usage: script.py [-h] filename
script.py: error: the following arguments are required: filename
Документация
#основы
@zen_of_python
🙏 — Если спасибо за такой контент
Please open Telegram to view this post
VIEW IN TELEGRAM
🙏12
Паттерн Mediator | Что это и зачем нужен
Mediator — паттерн проектирования, который вводит объект-посредник для координации взаимодействий между другими объектами. Вместо того, чтобы объекты напрямую вызывали методы друг друга и пытались «договариваться», они отправляют сообщения посреднику, а он решает, кто и как должен на них отреагировать. Классическая аналогия — диспетчерская в аэропорту: пилоты не связываются друг с другом напрямую, а говорят с диспетчером.
При прямой связи «каждый с каждым» количество зависимостей растёт как квадрат числа компонентов: изменения в одном классе часто заставляют править десятки других. Посредник помещает логику взаимодействия в одну точку:
Плюсы
1️⃣ Централизация логики взаимодействия (проще править и тестировать).
2️⃣ Снижение связности между компонентами.
3️⃣ Легче добавлять новые стратегии взаимодействия, не меняя классы коллег.
Минусы
1️⃣ Риск «божественного объекта» (God Object): медиатор может накопить слишком много логики и стать сложным.
2️⃣ Централизация порождает узкое место — медиатор становится более сложным и менее прозрачным.
#основы
@zen_of_python
Mediator — паттерн проектирования, который вводит объект-посредник для координации взаимодействий между другими объектами. Вместо того, чтобы объекты напрямую вызывали методы друг друга и пытались «договариваться», они отправляют сообщения посреднику, а он решает, кто и как должен на них отреагировать. Классическая аналогия — диспетчерская в аэропорту: пилоты не связываются друг с другом напрямую, а говорят с диспетчером.
При прямой связи «каждый с каждым» количество зависимостей растёт как квадрат числа компонентов: изменения в одном классе часто заставляют править десятки других. Посредник помещает логику взаимодействия в одну точку:
class Mediator:
"""Интерфейс медиатора."""
def notify(self, sender, event):
raise NotImplementedError
class CourseMediator(Mediator):
"""Конкретный медиатор — координатор курсов и пользователей."""
def __init__(self):
self.users = []
def register(self, user):
self.users.append(user)
user.mediator = self
def notify(self, sender, course_name):
# В простом варианте медиатор просто логирует сообщение
print(f"[{sender}] выбрал курс: {course_name}")
# Можно добавить дополнительную логику: фильтрация, отправка уведомлений и т.п.
class User:
def __init__(self, name):
self.name = name
self.mediator = None
def send_course(self, course_name):
if not self.mediator:
raise RuntimeError("User не зарегистрирован у медиатора")
self.mediator.notify(self, course_name)
def __str__(self):
return self.name
m = CourseMediator()
u1 = User("Майкл"); u2 = User("Оля")
m.register(u1); m.register(u2)
u1.send_course("DSA")
u2.send_course("Software Development")
User
не знает про других пользователей. Вся координация — в CourseMediator
. Такой подход облегчает изменение логики (например, добавить рассылку уведомлений другим пользователям) без модификации User
. Медиатор снижает связанность (Coupling) между компонентами и упрощает поддержку. Плюсы
Минусы
#основы
@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤5
@pytest.mark.parametrize
: Как параметризировать тестыТестирование кода может быть утомительным процессом. Когда у вас есть множество похожих тестовых случаев, написание отдельных функций для каждого часто приводит к дублированию кода. Именно здесь на помощь приходит функция @pytest.mark.parametrize.
Начнем с простого примера. У нас есть функция
add_nums()
, которая складывает числа из списка:
def add_nums(numbers):
return sum(numbers)
Без parametrize тесты могли бы выглядеть так:
def test_123():
assert add_nums([1, 2, 3]) == 6
def test_negatives():
assert add_nums([1, 2, -3]) == 0
def test_empty():
assert add_nums([]) == 0
Что не так с этим подходом? Дублирование кода: каждая тестовая функция повторяет одну и ту же структуру. Вместо написания трех отдельных функций, мы можем создать одну параметризованную функцию:
import pytest
@pytest.mark.parametrize(
"nums, expected_total",
[
([1, 2, 3], 6),
([1, 2, -3], 0),
([], 0),
]
)
def test_add_nums(nums, expected_total):
assert add_nums(nums) == expected_total
1.
@pytest.mark.parametrize
— это специальный декоратор pytest2. Параметры "
nums, expected_total
" — имена параметров функции3. Тестовые данные — список кортежей, где каждый содержит значения для одного теста
Pytest автоматически вызывает вашу функцию с каждым набором параметров:
# Первый вызов
test_add_nums([1, 2, 3], 6)
# Второй вызов
test_add_nums([1, 2, -3], 0)
# Третий вызов
test_add_nums([], 0)
Результат: 3 отдельных теста, каждый из которых может пройти или упасть.
Кастомные ID для тестов
По умолчанию pytest генерирует автоматические ID для тестов, но они могут быть не очень понятными. Вы можете задать свои:
@pytest.mark.parametrize(
"nums, expected_total",
[
([1, 2, 3], 6),
([1, 2, -3], 0),
([], 0),
],
ids=["positive_numbers", "mixed_numbers", "empty_list"]
)
def test_add_nums(nums, expected_total):
assert add_nums(nums) == expected_total
Теперь при запуске тестов вы увидите:
test_add_nums[positive_numbers] PASSED
test_add_nums[mixed_numbers] PASSED
test_add_nums[empty_list] PASSED
Вложенная параметризация
Можно комбинировать несколько параметризаций:
@pytest.mark.parametrize("x", [1, 2, 3])
@pytest.mark.parametrize("y", [10, 20])
def test_multiply(x, y):
assert x * y == x * y
Это создаст 6 тестов: (1,10), (1,20), (2,10), (2,20), (3,10), (3,20).
#основы
@zen_of_python
👍13❤1🔥1