Python | Вопросы собесов
13.4K subscribers
38 photos
4 videos
1 file
1.32K links
Cайт: easyoffer.ru
Реклама: @easyoffer_adv
ВП: @easyoffer_vp

Тесты t.me/+20tRfhrwPpM4NDQy
Задачи t.me/+nsl4meWmhfQwNDVi
Вакансии t.me/+cXGKkrOY2-w3ZTky
Download Telegram
🤔 Что такое абстракция?

Это принцип объектно-ориентированного программирования, позволяющий скрывать детали реализации и предоставлять пользователю только необходимый интерфейс. Это помогает уменьшить сложность системы, улучшить читаемость кода и упростить поддержку за счет сокрытия ненужных деталей.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍6🔥2💊1
🤔 Какие есть особенности исключения в Python?

В Python исключения (exceptions) — это специальные объекты, которые возникают при ошибках и прерывают выполнение программы, если их не обработать.

Все исключения в Python — это объекты, унаследованные от BaseException.
try:
1 / 0
except ZeroDivisionError as e:
print(type(e)) # <class 'ZeroDivisionError'>
print(e) # division by zero


Все исключения унаследованы от BaseException:
BaseException
├── Exception
│ ├── ArithmeticError
│ │ ├── ZeroDivisionError
│ │ ├── OverflowError
│ ├── ValueError
│ ├── IndexError
│ ├── KeyError
│ ├── TypeError
├── SystemExit
├── KeyboardInterrupt


Можно перехватывать несколько исключений
try:
x = int("abc") # Ошибка ValueError
except (ValueError, TypeError) as e:
print(f"Ошибка: {e}")


Если не знаем, какая ошибка может произойти:
try:
x = 1 / 0
except Exception as e:
print(f"Ошибка: {e}") # division by zero


finally выполняется всегда
try:
1 / 0
except ZeroDivisionError:
print("Ошибка!")
finally:
print("Этот код выполнится всегда")


raise позволяет выбрасывать исключения вручную
raise ValueError("Ошибка: неверное значение!")


Можно создать свой класс ошибки, унаследованный от Exception:
class MyError(Exception):
pass

raise MyError("Это моя ошибка!")


Ставь 👍 и забирай 📚 Базу знаний
👍6
🤔 Как работают вложенные исключения?

Вложенные исключения в Python — это конструкция, при которой один блок try находится внутри другого. Это позволяет локально обрабатывать определённые ошибки, не прерывая выполнения внешнего блока.
Если исключение перехвачено во внутреннем try-except, то внешний except не срабатывает. Если не перехвачено — передаётся дальше по стеку вверх.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍5
🤔 Какие есть виды файловых объектов?

В Python существует несколько типов файловых объектов, которые используются для работы с различными типами данных. Рассмотрим основные виды файловых объектов и их особенности.

🟠Текстовые файлы (`TextIOWrapper`)
Это самый распространённый тип файловых объектов. Такие файлы используются для работы с текстовыми данными и поддерживают строковые операции.
   with open("example.txt", "w", encoding="utf-8") as file:
file.write("Привет, мир!") # Записываем текст в файл

with open("example.txt", "r", encoding="utf-8") as file:
content = file.read() # Читаем текст из файла
print(content)


🟠Бинарные файлы (`BufferedReader`, `BufferedWriter`)
Эти файлы используются для работы с двоичными данными (изображениями, видео, аудиофайлами и т. д.).
   with open("image.jpg", "rb") as file:
binary_data = file.read() # Читаем файл в бинарном режиме
print(binary_data[:10]) # Выведем первые 10 байтов

with open("copy.jpg", "wb") as file:
file.write(binary_data) # Записываем данные в новый файл


🟠Файлы ввода-вывода в памяти (`io.StringIO`, `io.BytesIO`)
Эти объекты представляют собой файловые буферы, которые хранят данные в оперативной памяти, а не на диске.
   from io import StringIO

file = StringIO()
file.write("Привет, мир!") # Запись данных в буфер
file.seek(0) # Перемещаем указатель в начало
print(file.read()) # Читаем данные из буфера


Пример работы с BytesIO:
   from io import BytesIO

file = BytesIO()
file.write(b"Binary data") # Запись бинарных данных
file.seek(0)
print(file.read()) # Чтение данных


🟠Файловые объекты на основе сокетов, пайпов и других источников
Python позволяет работать с файловыми объектами, полученными из нестандартных источников, например, сокетов или каналов связи (pipes).
   import socket

s = socket.socket()
s.connect(("example.com", 80))
s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
response = s.makefile("r", encoding="utf-8") # Создание файлового объекта
print(response.readline()) # Читаем первую строку HTTP-ответа
s.close()


Ставь 👍 и забирай 📚 Базу знаний
👍7
🤔 Почему индекс списка начинается с нуля?

Индексация с нуля исторически пришла из языков вроде C, где array[0] означает смещение от начала памяти. Это упрощает адресацию и математические операции при обходе массивов, и Python унаследовал этот подход как более логичный и эффективный.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍11
🤔 Что такое хранитель (Memento)?

Это поведенческий паттерн проектирования, который позволяет сохранять и восстанавливать предыдущее состояние объекта без нарушения инкапсуляции. Этот паттерн особенно полезен для реализации операций отмены и повтора, так как он позволяет хранить состояния объектов и возвращать их к этим состояниям по необходимости.

🚩Зачем нужен?

🟠Сохранение состояния:
Позволяет сохранять текущее состояние объекта и восстанавливать его позже.
🟠Инкапсуляция:
Обеспечивает сохранение состояния объекта без нарушения его инкапсуляции. Внутренние детали объекта остаются скрытыми от других объектов.
🟠Отмена и повтор операций:
Поддерживает функциональность отмены и повтора операций, так как позволяет возвращать объект к предыдущим состояниям.
Пример реализации
class Memento:
def __init__(self, state: str):
self._state = state

def get_state(self) -> str:
return self._state

class TextEditor:
def __init__(self):
self._state = ""
self._history = []

def type(self, text: str):
self._save_state()
self._state += text

def _save_state(self):
self._history.append(Memento(self._state))

def undo(self):
if not self._history:
return
memento = self._history.pop()
self._state = memento.get_state()

def get_content(self) -> str:
return self._state

# Клиентский код для использования паттерна Хранитель
def main():
editor = TextEditor()

editor.type("Hello, ")
editor.type("world!")
print(editor.get_content()) # Hello, world!

editor.undo()
print(editor.get_content()) # Hello,

editor.undo()
print(editor.get_content()) #

if __name__ == "__main__":
main()


1⃣`Memento`:
Сохраняет состояние объекта. Он предоставляет методы для получения сохраненного состояния, но не предоставляет методов для изменения состояния, что обеспечивает неизменность.

2⃣`TextEditor`:
Создает и использует объекты Memento для сохранения и восстановления своего состояния. Методы type и undo позволяют редактировать текст и отменять изменения.

3⃣`_save_state`:
Сохраняет текущее состояние редактора в истории перед каждым изменением.

4⃣`undo`:
Восстанавливает предыдущее состояние редактора из истории.

Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 Что такое set?

Множество в Python — это неупорядоченная коллекция уникальных элементов. Оно используется для удаления дубликатов, а также для выполнения операций теории множеств — пересечения, объединения, разности. Это быстрый и удобный тип данных для работы с наборами.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍8
🤔 Почему поиск по ключам в словаре работает быстро?

В Python словари (dict) работают очень быстро, потому что они используют хеш-таблицы. Это позволяет находить значения по ключу в константное время O(1) в большинстве случаев. Давайте разберемся, как это работает.

🟠Как устроен словарь в Python?
Словарь (dict) — это структура данных, которая хранит пары ключ → значение. Например:
data = {"name": "Alice", "age": 25, "city": "New York"}
print(data["age"]) # 25


🟠Как работает хеш-таблица?
Основной принцип:
Хеш-функция (hash()) вычисляет уникальное число (хеш) для ключа.
Используется массив (таблица), где данные хранятся по индексам, связанным с хешем.
Поиск по ключу — это просто вычисление хеша и обращение к нужному индексу.
print(hash("age"))  # Например, вернет 328847234 (будет разным при каждом запуске)


Когда мы пишем
value = data["age"]


🟠Почему поиск занимает O(1)?
Нет линейного поиска: вместо перебора всех элементов Python сразу вычисляет, где находится нужное значение.
Операция доступа занимает фиксированное время: hash() + обращение по индексу.
Даже при большом количестве элементов скорость остается высокой.
Добавим 1 миллион элементов и посмотрим скорость поиска:
import time

data = {i: i * 2 for i in range(1_000_000)}

start = time.time()
print(data[999_999]) # Быстро находит ключ!
end = time.time()

print("Время поиска:", end - start) # Около 0.000001 сек


🟠Что если хеши совпадут? (Коллизии)
Иногда два разных ключа могут иметь одинаковый хеш (редко, но возможно). Тогда Python использует связанный список (chaining) или перехеширование.
print(hash("abc") % 10)  # Например, 5
print(hash("xyz") % 10) # Тоже 5 (редко, но бывает)


Ставь 👍 и забирай 📚 Базу знаний
👍9🤔1
🤔 Что такое сокеты?

Это программный интерфейс (API), через который приложения обмениваются данными по сети.
Сокет — это конечная точка соединения, обеспечивающая передачу данных между двумя узлами (обычно по TCP или UDP).
Используется для:
- Клиент-серверных приложений.
- Чатов, игр, систем в реальном времени.
- Веб-сокетов (WebSocket) для двусторонней связи по HTTP(S).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥8👍4
🤔 Что такое IP адрес и доменное имя?

Это два важных понятия в контексте работы интернета и компьютерных сетей. Они используются для идентификации устройств и ресурсов в сети, а также для упрощения доступа к ним.

🚩IP-адрес (Internet Protocol Address)

Это уникальный числовой идентификатор, присваиваемый каждому устройству, подключенному к сети, использующей протокол IP (Internet Protocol). IP-адреса используются для маршрутизации пакетов данных между устройствами в сети.

🟠IPv4 (Internet Protocol version 4)
Формат: 32-битные числа, записанные в виде четырех десятичных чисел, разделенных точками (например, 192.168.1.1). Пример: 192.168.0.1, 8.8.8.8

🟠IPv6 (Internet Protocol version 6)
Формат: 128-битные числа, записанные в виде восьми групп шестнадцатеричных чисел, разделенных двоеточиями (например, 2001:0db8:85a3:0000:0000:8a2e:0370:7334). Пример: 2001:0db8:85a3:0000:0000:8a2e:0370:7334, ::1 (loopback адрес)

🚩Доменное имя

Это удобочитаемое имя, используемое для идентификации IP-адреса на уровне пользователя. Доменные имена упрощают доступ к ресурсам в интернете, так как их легче запомнить и использовать, чем числовые IP-адреса.

🟠Top-Level Domain (TLD)
Верхний уровень, например, .com, .org, .net.
🟠Second-Level Domain (SLD)
Основная часть доменного имени, например, example в example.com.
🟠Subdomain
Дополнительные уровни, например, www в www.example.com.

🚩Преобразование доменных имен в IP-адреса

Для преобразования доменных имен в IP-адреса используется система доменных имен (DNS, Domain Name System). DNS-серверы выполняют роль "телефонной книги" интернета, переводя доменные имена в соответствующие им IP-адреса.

Ставь 👍 и забирай 📚 Базу знаний
👍8
🤔 Может ли сервер изменить (добавить, удалить) куки?

Да, сервер управляет куками через заголовок Set-Cookie. Он может отправить новые, обновить существующие или установить срок действия, после которого cookie удалится автоматически. Также можно указать флаг удаления, отправив cookie с истёкшим сроком.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥3👍2
🤔 Зачем нужен volume в docker?

В Docker volume (том) — это специальное место для хранения данных контейнера, которое не исчезает при перезапуске или удалении контейнера.

🚩Зачем нужен `volume`?

Данные не теряются при удалении контейнера
Общий доступ: несколько контейнеров могут использовать один и тот же volume
Производительность: тома быстрее, чем хранение внутри контейнера
Разделение кода и данных: удобно для баз данных и логов

🚩Как создать `volume`?

Способ 1: Автоматическое создание при запуске контейнера
docker run -d -v my_volume:/app/data my_container


Способ 2: Создать том отдельно и подключить его
docker volume create my_volume
docker run -d -v my_volume:/app/data my_container


Способ 3: Использовать путь на хосте (bind-mount)
docker run -d -v /home/user/data:/app/data my_container


🚩Как посмотреть список томов?

docker volume ls  # Покажет все тома
docker volume inspect my_volume # Информация о томе


🚩Как удалить `volume`?

Важно: при удалении тома данные удаляются безвозвратно!
docker volume rm my_volume  # Удаление одного тома
docker volume prune # Удаление всех неиспользуемых томов


Ставь 👍 и забирай 📚 Базу знаний
👍6
🤔 Как описать модель проектирования?

Использует шаблон MVC (Model-View-Controller), но адаптирован под MTV (Model-Template-View). Основные компоненты:
- Model — представляет данные и логику базы данных
- Template — отвечает за отображение
- View — обрабатывает логику запроса и ответа


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
💊6👍4🤔2
🤔 Как отсортировать список словарей по определенному полю?

Для сортировки списка словарей по определенному полю в Python удобно использовать функцию sorted() или метод sort(). Оба подхода позволяют указать ключ сортировки с помощью параметра key, где можно передать либо функцию, либо лямбда-выражение, которое извлекает значение из словаря для сортировки.

🚩Почему это важно?

Списки словарей часто используются для хранения структурированных данных. Например, вы можете иметь список сотрудников, где каждый сотрудник представлен в виде словаря с полями, такими как имя, возраст и зарплата. Сортировка по определенному полю позволяет упорядочить данные, чтобы ими было проще пользоваться или отображать.

🚩Как это сделать?

🟠Использование функции `sorted()`
Эта функция возвращает новый отсортированный список.

   employees = [
{"name": "Alice", "age": 30, "salary": 70000},
{"name": "Bob", "age": 25, "salary": 50000},
{"name": "Charlie", "age": 35, "salary": 120000}
]

# Сортировка по возрасту
sorted_employees = sorted(employees, key=lambda x: x["age"])

print(sorted_employees)


Результат

   [{'name': 'Bob', 'age': 25, 'salary': 50000},
{'name': 'Alice', 'age': 30, 'salary': 70000},
{'name': 'Charlie', 'age': 35, 'salary': 120000}]


🟠Использование метода `sort()`
Этот метод изменяет существующий список.

   employees = [
{"name": "Alice", "age": 30, "salary": 70000},
{"name": "Bob", "age": 25, "salary": 50000},
{"name": "Charlie", "age": 35, "salary": 120000}
]

# Сортировка по зарплате
employees.sort(key=lambda x: x["salary"])

print(employees)


Результат

   [{'name': 'Bob', 'age': 25, 'salary': 50000},
{'name': 'Alice', 'age': 30, 'salary': 70000},
{'name': 'Charlie', 'age': 35, 'salary': 120000}]


🟠Сортировка в обратном порядке
Установите параметр reverse=True, чтобы отсортировать в порядке убывания.

   sorted_employees_desc = sorted(employees, key=lambda x: x["age"], reverse=True)
print(sorted_employees_desc)


🟠Использование функции `itemgetter` из модуля `operator`
Это более эффективный способ, чем лямбда-функция, особенно для больших данных.

   from operator import itemgetter

sorted_employees = sorted(employees, key=itemgetter("age"))
print(sorted_employees)


🚩Обработка отсутствующих значений

Если поле может отсутствовать в некоторых словарях, можно использовать параметр key для обработки таких ситуаций.
employees = [
{"name": "Alice", "age": 30},
{"name": "Bob"},
{"name": "Charlie", "age": 35}
]

sorted_employees = sorted(employees, key=lambda x: x.get("age", 0))
print(sorted_employees)


Результат
[{'name': 'Bob'},
{'name': 'Alice', 'age': 30},
{'name': 'Charlie', 'age': 35}]


Ставь 👍 и забирай 📚 Базу знаний
👍4
🤔 Какие есть особенности исключений?

1. Все исключения являются объектами и наследуются от базового класса BaseException (обычно от Exception).
2. Обработка исключений осуществляется через блоки try-except, с возможностью использовать else (если исключения не было) и finally (выполняется всегда).
3. Можно обрабатывать конкретные типы ошибок, в том числе несколько сразу:
4. Можно создавать собственные исключения, унаследовав их от Exception.
5. Исключения можно перекидывать вверх по стеку с помощью raise.
6. В Python принято использовать исключения для управления потоком, особенно при обработке ввода, сетевых операций, доступа к данным и API.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍4
🤔 Почему вычислять большие значения в асинхронной функции плохо?

Асинхронность (asyncio) в Python не выполняет код параллельно, а переключается между задачами во время ожидания (I/O-bound).
Если в async-функции делать тяжёлые вычисления (CPU-bound), это блокирует asyncio, потому что в Python есть GIL (Global Interpreter Lock).

🚩Асинхронность в Python подходит для ввода-вывода (I/O-bound)

Асинхронность позволяет выполнять задачи без блокировки, но только если они ждут чего-то (файлы, сеть, БД).
import asyncio
import aiohttp

async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()

async def main():
urls = ["https://example.com"] * 5
results = await asyncio.gather(*(fetch(url) for url in urls))

asyncio.run(main())


🚩Проблема с `async` и тяжёлыми вычислениями (CPU-bound)

Если в async-функции делать тяжёлые вычисления, Python не сможет переключаться между задачами.
import asyncio

async def heavy_task(n):
print(f"Вычисляю {n}...")
total = sum(i**2 for i in range(n)) # Долгий процесс
return total

async def main():
await asyncio.gather(heavy_task(10**7), heavy_task(10**7))

asyncio.run(main())


🚩Как правильно выполнять вычисления в `async`?

🟠Использовать `asyncio.to_thread()` (делегирование в потоки)
В Python 3.9+ можно выполнять CPU-задачи в отдельных потоках, не блокируя asyncio.
import asyncio

def heavy_computation(n):
return sum(i**2 for i in range(n))

async def main():
result = await asyncio.to_thread(heavy_computation, 10**7)
print(result)

asyncio.run(main())


🟠Использовать `multiprocessing` (запуск на нескольких ядрах)
Так как Python использует GIL, единственный способ выполнять настоящий параллелизм — это multiprocessing.
import asyncio
import multiprocessing

def heavy_computation(n):
return sum(i**2 for i in range(n))

async def main():
loop = asyncio.get_running_loop()
with multiprocessing.Pool() as pool:
result = await loop.run_in_executor(pool, heavy_computation, 10**7)
print(result)

asyncio.run(main())


Ставь 👍 и забирай 📚 Базу знаний
👍5
🤔 Что такое байт-код?

Это промежуточное представление программы, которое создаётся после компиляции Python-кода. Он исполняется виртуальной машиной Python (интерпретатором).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍3
🤔 Что такое паттерн Стратегия (Strategy) ?

Это поведенческий паттерн проектирования, который определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Паттерн "Стратегия" позволяет изменять алгоритмы независимо от клиентов, которые их используют.

🚩Зачем нужен данный паттерн?

🟠Изоляция алгоритмов
Позволяет инкапсулировать различные алгоритмы и использовать их независимо.
🟠Упрощение кода
Устраняет дублирование кода и упрощает классы, которые используют эти алгоритмы.
🟠Гибкость и расширяемость
Легко добавлять новые алгоритмы или изменять существующие без изменения клиентского кода.

🚩Как работает данный паттерн?

🟠Стратегия (Strategy)
Интерфейс, определяющий общий метод, который должны реализовать все алгоритмы.
🟠Конкретные стратегии (ConcreteStrategy)
Реализации различных алгоритмов, которые реализуют интерфейс стратегии.
🟠Контекст (Context)
Класс, использующий стратегию для выполнения задачи.

from abc import ABC, abstractmethod

# Интерфейс стратегии
class Strategy(ABC):
@abstractmethod
def sort(self, data):
pass

# Конкретные стратегии
class BubbleSortStrategy(Strategy):
def sort(self, data):
print("Sorting using Bubble Sort")
for i in range(len(data)):
for j in range(0, len(data)-i-1):
if data[j] > data[j+1]:
data[j], data[j+1] = data[j+1], data[j]

class QuickSortStrategy(Strategy):
def sort(self, data):
print("Sorting using Quick Sort")
self.quick_sort(data, 0, len(data) - 1)

def quick_sort(self, data, low, high):
if low < high:
pi = self.partition(data, low, high)
self.quick_sort(data, low, pi - 1)
self.quick_sort(data, pi + 1, high)

def partition(self, data, low, high):
pivot = data[high]
i = low - 1
for j in range(low, high):
if data[j] <= pivot:
i = i + 1
data[i], data[j] = data[j], data[i]
data[i + 1], data[high] = data[high], data[i + 1]
return i + 1

# Контекст
class SortingContext:
def __init__(self, strategy: Strategy):
self._strategy = strategy

def set_strategy(self, strategy: Strategy):
self._strategy = strategy

def sort(self, data):
self._strategy.sort(data)

# Клиентский код
data = [5, 2, 9, 1, 5, 6]

context = SortingContext(BubbleSortStrategy())
context.sort(data)
print(data) # [1, 2, 5, 5, 6, 9]

context.set_strategy(QuickSortStrategy())
data = [3, 7, 8, 5, 2, 1, 9, 5, 4]
context.sort(data)
print(data) # [1, 2, 3, 4, 5, 5, 7, 8, 9]


🚩Плюсы и минусы

Изоляция алгоритмов
Алгоритмы инкапсулируются в отдельные классы, что упрощает их замену и добавление.
Упрощение кода
Контекст использует стратегии, избегая громоздких условных операторов.
Гибкость и расширяемость
Легко добавлять новые стратегии без изменения существующего кода.
Усложнение структуры кода

Добавление множества классов стратегий может усложнить проект.
Контекст знает о стратегиях
Контекст должен знать о всех возможных стратегиях, чтобы иметь возможность их переключать.

🚩Когда использовать данный паттерн?

Когда есть несколько вариантов алгоритмов для выполнения задачи.
Когда нужно динамически выбирать алгоритм во время выполнения.
Когда необходимо избежать множества условных операторов для выбора алгоритма.

Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 \Что такое конкурентность?

Конкурентность — это способность программы работать с несколькими задачами одновременно (не обязательно параллельно). Например, асинхронность и многопоточность обеспечивают конкурентное поведение за счёт переключения между задачами.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍3
🤔 Как происходит наследование моделей в Django?

В Django можно наследовать модели, используя стандартное наследование классов. Это помогает повторно использовать код, организовывать данные и добавлять общие поля в несколько моделей. Django поддерживает три основных типа наследования:

🟠Абстрактные модели
Абстрактные модели используются, когда нужно создать базовый класс с полями и методами, которые должны быть унаследованы, но сам класс не должен создавать таблицу в базе данных.
Создается базовый класс с abstract = True в Meta.
Дочерние классы наследуют его поля и методы, но не его саму в виде отдельной таблицы.
from django.db import models

class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
abstract = True # Указывает, что это абстрактная модель

class Post(BaseModel):
title = models.CharField(max_length=255)
content = models.TextField()

class Comment(BaseModel):
text = models.TextField()


🟠Многоуровневое (конкретное) наследование
Этот тип наследования создаёт отдельные таблицы для каждой модели. Используется, когда дочерний класс должен представлять отдельную сущность, но при этом иметь доступ к полям родительского класса.
Django создаёт отдельные таблицы в БД для родительской и дочерней модели.
Дочерняя модель автоматически получает OneToOneField на родительскую.
class Person(models.Model):
name = models.CharField(max_length=255)

class Employee(Person): # Отдельная таблица employee
salary = models.DecimalField(max_digits=10, decimal_places=2)


🟠Прокси-модели (Proxy Models)
Используются, когда нужно изменить поведение модели без изменения структуры базы данных.
Прокси-модель наследует поля родительской модели.
В Meta указывается proxy = True.
Можно переопределять методы, добавлять новые, но не менять поля.
class Person(models.Model):
name = models.CharField(max_length=255)

class Manager(Person):
class Meta:
proxy = True # Указываем, что это прокси-модель

def get_uppercase_name(self):
return self.name.upper()


Ставь 👍 и забирай 📚 Базу знаний
👍2