Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26🔥8💊3
Паттерн Наблюдатель (Observer) — это поведенческий паттерн проектирования, который определяет зависимость "один ко многим" между объектами, так что при изменении состояния одного объекта все зависящие от него объекты уведомляются и обновляются автоматически. Этот паттерн используется для реализации механизма подписки, когда объекты могут подписываться на события другого объекта и получать уведомления о любых изменениях.
Позволяет отделить объект, который изменяет свое состояние, от объектов, которые реагируют на эти изменения.
Обеспечивает автоматическое уведомление и обновление зависимых объектов при изменении состояния наблюдаемого объекта.
Легко добавлять или удалять наблюдателей без изменения кода наблюдаемого объекта.
Пример реализации
from abc import ABC, abstractmethod
class Observer(ABC):
@abstractmethod
def update(self, message: str):
pass
class ConcreteObserver(Observer):
def __init__(self, name: str):
self._name = name
def update(self, message: str):
print(f"{self._name} received message: {message}")
class Subject:
def __init__(self):
self._observers = []
def add_observer(self, observer: Observer):
self._observers.append(observer)
def remove_observer(self, observer: Observer):
self._observers.remove(observer)
def notify_observers(self, message: str):
for observer in self._observers:
observer.update(message)
# Клиентский код для использования паттерна Наблюдатель
def main():
subject = Subject()
observer1 = ConcreteObserver("Observer 1")
observer2 = ConcreteObserver("Observer 2")
subject.add_observer(observer1)
subject.add_observer(observer2)
subject.notify_observers("Event 1") # Observer 1 received message: Event 1
# Observer 2 received message: Event 1
subject.remove_observer(observer1)
subject.notify_observers("Event 2") # Observer 2 received message: Event 2
if __name__ == "__main__":
main()
Объявляет метод
update
, который должны реализовать все конкретные наблюдатели.Реализует интерфейс
Observer
и определяет, как наблюдатель должен реагировать на обновления.Содержит список наблюдателей и методы для добавления, удаления и уведомления наблюдателей.
Вызывается при изменении состояния субъекта и уведомляет всех зарегистрированных наблюдателей.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥7💊2❤1
Это поведенческий паттерн проектирования, который позволяет объекту изменять свое поведение в зависимости от его состояния. Это делается путем инкапсуляции состояний в отдельные классы и делегирования задач состояниям, таким образом объект изменяет свой класс поведения при изменении состояния.
Логика, связанная с конкретными состояниями, инкапсулируется в отдельных классах, что способствует лучшему разделению обязанностей и поддержке кода.
Легко добавлять новые состояния и изменять существующие без внесения изменений в основной код объекта.
from abc import ABC, abstractmethod
# Интерфейс состояния
class State(ABC):
@abstractmethod
def insert_coin(self):
pass
@abstractmethod
def eject_coin(self):
pass
@abstractmethod
def dispense(self):
pass
# Конкретные состояния
class NoCoinState(State):
def __init__(self, machine):
self.machine = machine
def insert_coin(self):
print("Coin inserted.")
self.machine.set_state(self.machine.has_coin_state)
def eject_coin(self):
print("No coin to eject.")
def dispense(self):
print("Insert coin first.")
class HasCoinState(State):
def __init__(self, machine):
self.machine = machine
def insert_coin(self):
print("Coin already inserted.")
def eject_coin(self):
print("Coin ejected.")
self.machine.set_state(self.machine.no_coin_state)
def dispense(self):
print("Dispensing product.")
self.machine.set_state(self.machine.no_coin_state)
# Контекст
class VendingMachine:
def __init__(self):
self.no_coin_state = NoCoinState(self)
self.has_coin_state = HasCoinState(self)
self.state = self.no_coin_state
def set_state(self, state: State):
self.state = state
def insert_coin(self):
self.state.insert_coin()
def eject_coin(self):
self.state.eject_coin()
def dispense(self):
self.state.dispense()
# Клиентский код
def main():
machine = VendingMachine()
machine.insert_coin() # Coin inserted.
machine.dispense() # Dispensing product.
machine.eject_coin() # No coin to eject.
machine.insert_coin() # Coin inserted.
machine.eject_coin() # Coin ejected.
machine.dispense() # Insert coin first.
if __name__ == "__main__":
main()
Определяет методы, которые должны реализовать все конкретные состояния.
Реализуют интерфейс
State
и определяют поведение для каждого состояния.Содержит ссылки на все возможные состояния и метод для изменения текущего состояния. Делегирует вызовы методов текущему состоянию.
Вызываются клиентом и делегируются текущему состоянию.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥10
Это технология, позволяющая программам выполнять процедуры или функции на удаленных системах так же, как если бы они выполнялись локально. RPC абстрагирует сетевое взаимодействие, делая удаленные вызовы процедур похожими на локальные вызовы, что упрощает распределенное программирование.
Позволяет разработчикам вызывать удаленные процедуры так, как будто они находятся на локальной машине, что упрощает программирование распределенных систем.
Скрывает сложность сетевого взаимодействия, позволяя сосредоточиться на логике приложения.
Обеспечивает взаимодействие между приложениями, работающими на разных платформах и написанных на разных языках программирования.
Инициирует вызов удаленной процедуры, отправляя запрос на сервер.
Принимает запрос, выполняет запрашиваемую процедуру и возвращает результат клиенту.
Сервер
from xmlrpc.server import SimpleXMLRPCServer
def add(x, y):
return x + y
def main():
server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(add, "add")
server.serve_forever()
if __name__ == "__main__":
main()
Клиентские и серверные заглушки генерируются для сериализации и десериализации данных, что позволяет передавать данные по сети.
Клиент
import xmlrpc.client
def main():
with xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy:
result = proxy.add(5, 3)
print(f"5 + 3 = {result}")
if __name__ == "__main__":
main()
Создает XML-RPC сервер и регистрирует функцию
add
, которая принимает два числа и возвращает их сумму. Сервер ожидает запросы на порту 8000.Подключается к серверу через
ServerProxy
и вызывает удаленную функцию add
с аргументами 5 и 3. Результат вызова выводится на экран.Обеспечивает простой интерфейс для вызова удаленных процедур.
Меньше задержек по сравнению с передачей сообщений благодаря синхронным вызовам.
Поддерживает взаимодействие между системами на разных платформах и языках программирования.
Работа системы зависит от сетевого соединения, что может приводить к задержкам и ошибкам.
Ошибки и проблемы могут быть сложными для диагностики из-за распределенной природы системы.
Необходимо обеспечить безопасность данных, передаваемых по сети, особенно при использовании в открытых сетях.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥4💊1
Это механизм кэширования, который использует стратегию "наименее недавно использованный" для управления хранимыми данными. LRU-кэш хранит фиксированное количество наиболее часто используемых элементов, удаляя наименее недавно использованные элементы, когда необходимо освободить место для новых.
Повышает производительность системы за счет хранения часто запрашиваемых данных в памяти.
Поддерживает ограниченный размер кэша, автоматически удаляя наименее недавно использованные элементы.
Снижает время доступа к данным, которые часто используются, избегая повторных вычислений или запросов к медленным хранилищам.
Python предоставляет удобную реализацию LRU-кэша через декоратор
lru_cache
в модуле functools
. Этот декоратор можно использовать для кэширования результатов вызова функции.from functools import lru_cache
@lru_cache(maxsize=4)
def expensive_computation(n):
print(f"Computing {n}...")
return n * n
def main():
print(expensive_computation(1)) # Computing 1... -> 1
print(expensive_computation(2)) # Computing 2... -> 4
print(expensive_computation(3)) # Computing 3... -> 9
print(expensive_computation(4)) # Computing 4... -> 16
print(expensive_computation(1)) # Cached -> 1
print(expensive_computation(2)) # Cached -> 4
print(expensive_computation(5)) # Computing 5... -> 25 (1 removed from cache)
print(expensive_computation(3)) # Cached -> 9
print(expensive_computation(1)) # Computing 1... -> 1 (2 removed from cache)
if __name__ == "__main__":
main()
Декорирует функцию
expensive_computation
, добавляя механизм кэширования.Определяет максимальный размер кэша. В данном примере, кэш может хранить до 4 элементов.
При вызове функции сначала проверяется, есть ли результат в кэше. Если есть, возвращается кэшированный результат. Если нет, функция вычисляет результат, и он сохраняется в кэш. Когда кэш переполняется, наименее недавно использованный элемент удаляется.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍29🔥5💊1
Это поведенческий паттерн проектирования, который определяет скелет алгоритма в методе, оставляя реализацию некоторых шагов подклассам. Этот паттерн позволяет подклассам переопределять определенные шаги алгоритма, не изменяя его структуру.
Позволяет повторно использовать код, инкапсулируя общие части алгоритма в базовом классе и оставляя вариативные части для реализации в подклассах.
Легко изменять и расширять части алгоритма, переопределяя методы в подклассах.
Базовый класс контролирует структуру алгоритма, предотвращая нежелательные изменения его последовательности.
Пример реализации паттерна Шаблонный метод для процесса приготовления кофе и чая. Общие шаги включают кипячение воды, заваривание напитка и добавление добавок, но конкретные шаги зависят от типа напитка.
from abc import ABC, abstractmethod
class CaffeineBeverage(ABC):
def prepare_recipe(self):
self.boil_water()
self.brew()
self.pour_in_cup()
self.add_condiments()
def boil_water(self):
print("Boiling water")
def pour_in_cup(self):
print("Pouring into cup")
@abstractmethod
def brew(self):
pass
@abstractmethod
def add_condiments(self):
pass
class Tea(CaffeineBeverage):
def brew(self):
print("Steeping the tea")
def add_condiments(self):
print("Adding lemon")
class Coffee(CaffeineBeverage):
def brew(self):
print("Dripping coffee through filter")
def add_condiments(self):
print("Adding sugar and milk")
# Клиентский код для использования паттерна Шаблонный метод
def main():
tea = Tea()
coffee = Coffee()
print("Making tea:")
tea.prepare_recipe()
print("\nMaking coffee:")
coffee.prepare_recipe()
if __name__ == "__main__":
main()
Определяет шаблонный метод
prepare_recipe
, который описывает последовательность шагов алгоритма.Реализованы в абстрактном классе, так как они одинаковы для всех напитков.
Объявлены в абстрактном классе и должны быть реализованы в подклассах.
Реализуют методы
brew
и add_condiments
, определяя конкретные шаги для приготовления чая и кофе.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22
Это современный высокопроизводительный фреймворк для удаленных вызовов процедур (RPC), разработанный Google. Он использует HTTP/2 для транспортировки, Protocol Buffers (protobuf) для сериализации данных и предоставляет возможности, такие как аутентификация, балансировка нагрузки, двухсторонняя потоковая передача и многое другое.
Использует HTTP/2, что обеспечивает низкую задержку и высокую пропускную способность.
Поддерживает множество языков программирования, включая C++, Java, Python, Go и многие другие.
Обеспечивает простой способ определения сервисов с помощью Protocol Buffers.
Поддерживает как однонаправленные, так и двусторонние потоки данных.
Обеспечивает взаимодействие между разными системами и языками программирования.
В gRPC услуги определяются с помощью файлов .proto (Protocol Buffers), где описываются методы и их параметры.
gRPC автоматически генерирует код для клиентов и серверов на основе .proto файлов.
Используются Protocol Buffers для сериализации и десериализации данных, что обеспечивает эффективную передачу по сети.
Создайте файл
service.proto
для определения сервиса и его методов:syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Сгенерируйте код клиента и сервера на языке Python:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. service.proto
Создайте сервер, используя сгенерированный код:
from concurrent import futures
import grpc
import service_pb2
import service_pb2_grpc
class Greeter(service_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return service_pb2.HelloReply(message=f"Hello, {request.name}!")
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
service_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
Создайте клиент для вызова удаленного метода:
import grpc
import service_pb2
import service_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = service_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(service_pb2.HelloRequest(name='World'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
run()
Высокая производительность благодаря HTTP/2 и Protocol Buffers.
Поддержка множества языков программирования.
Простое определение сервисов с помощью Protocol Buffers.
Поддержка стриминга, аутентификации, балансировки нагрузки и других функций.
Возможность взаимодействия между различными системами.
Необходимость изучения Protocol Buffers и особенностей gRPC.
gRPC не поддерживается напрямую в браузерах, хотя есть gRPC-Web для этих целей.
Отладка распределенных систем с использованием gRPC может быть сложной.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍2
Ключем в словаре (dict) в Python может быть любой неизменяемый тип данных, такой как строки, числа, кортежи или булевы значения. Ключи должны быть уникальными, так как они используются для быстрой индексации и поиска значений в словаре. Попытка использования изменяемого объекта, такого как список или словарь, в качестве ключа вызовет ошибку. Ключи словаря должны быть хешируемыми, чтобы поддерживать эффективный поиск.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥5
Относится к ряду сложностей и недостатков, которые могут возникнуть при использовании наследования в объектно-ориентированном программировании. Хотя наследование является мощным механизмом для повторного использования кода и создания иерархий классов, оно также может привести к различным проблемам.
Изменения в базовом классе могут неожиданно повлиять на все производные классы. Это может вызвать ошибки, которые трудно отладить, особенно в больших системах.
Наследование создает тесную связанность между базовым и производными классами. Это затрудняет изменение одного класса без затрагивания других.
Иногда наследование используется для повторного использования кода, но это не всегда лучший подход. Наследование может привести к сложным иерархиям классов, которые трудно понять и сопровождать.
Наследование часто используется для повторного использования кода, но это может привести к унаследованию ненужной реализации, которая не подходит для всех производных классов. Это может затруднить изменение поведения класса.
Изменение базового класса может потребовать изменения всех производных классов, что увеличивает стоимость сопровождения кода.
В языках, поддерживающих множественное наследование (например, C++), это может привести к проблемам, таким как "ромбовидная" проблема, где один и тот же базовый класс наследуется более одного раза через разные пути в иерархии.
Рассмотрим пример, где базовый класс изменяется, что приводит к неожиданным последствиям для производных классов. В этом примере изменение базового класса
Animal
может повлиять на работу производных классов Dog
и Cat
, особенно если они зависят от определенной реализации метода sound
.class Animal:
def sound(self):
return "Some sound"
class Dog(Animal):
def sound(self):
return "Bark"
class Cat(Animal):
def sound(self):
return "Meow"
# Изменение базового класса
class Animal:
def sound(self):
return "Default sound"
animals = [Dog(), Cat()]
for animal in animals:
print(animal.sound())
В многих случаях композиция предпочтительнее наследования. Вместо того чтобы наследовать классы, можно использовать композицию, чтобы включить объекты других классов в качестве полей.
Использование интерфейсов и абстрактных классов позволяет определить контракты для классов, не предоставляя реализации. Это помогает уменьшить связанность и улучшить гибкость.
Этот паттерн позволяет изменять алгоритмы поведения объекта во время выполнения, что может быть более гибким решением, чем наследование.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥1💊1
В асинхронном коде выполнение задач происходит не в линейном порядке, а в зависимости от готовности событий. Здесь линеаризация может привести к потере преимуществ асинхронности.
import asyncio
async def fetch_data():
await asyncio.sleep(1)
return "Data fetched"
async def main():
data = await fetch_data()
print(data)
asyncio.run(main())
Рекурсивные алгоритмы, такие как обход дерева или вычисление факториала, невозможно представить в виде линейного кода без потери логики и структуры.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
В некоторых случаях требуется обработка ошибок и исключений, что нарушает линейную структуру кода.
try:
result = 10 / 0
except ZeroDivisionError:
print("Деление на ноль!")
Когда код содержит сложные вложенные циклы и условия, его линейное представление становится громоздким и трудночитаемым.
for i in range(10):
for j in range(10):
if i == j:
print(f"{i} равно {j}")
Некоторые паттерны проектирования, такие как фабричный метод или стратегия, предполагают использование классов и объектов, что не всегда удобно линеаризовать.
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof"
class Cat(Animal):
def speak(self):
return "Meow"
animals = [Dog(), Cat()]
for animal in animals:
print(animal.speak())
Параллельные вычисления, где задачи выполняются одновременно в разных потоках или процессах, не могут быть линеаризованы.
import threading
def print_numbers():
for i in range(5):
print(i)
thread = threading.Thread(target=print_numbers)
thread.start()
thread.join()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25💊3🤯1
Инкапсуляция предполагает скрытие внутреннего состояния объекта от внешнего мира. Это достигается с помощью модификаторов доступа, таких как private, protected и public. В Python используются соглашения об именовании (например, одиночное подчеркивание
_
для "protected" и двойное подчеркивание __
для "private") для индикации уровня доступа.Инкапсуляция позволяет контролировать доступ к данным и методам объекта. Это предотвращает некорректное использование или изменение состояния объекта, что может привести к ошибкам.
Инкапсуляция способствует созданию четких интерфейсов для взаимодействия с объектами. Внешний код взаимодействует с объектом через его публичные методы, не зная о внутреннем устройстве.
Инкапсуляция помогает разделять функциональность на независимые модули. Это облегчает разработку, тестирование и сопровождение кода.
В этом примере переменная
__balance
скрыта от внешнего доступа. Методы deposit
, withdraw
и get_balance
обеспечивают контролируемый доступ к этой переменной, обеспечивая инкапсуляцию.class BankAccount:
def __init__(self, initial_balance):
self.__balance = initial_balance # Закрытая переменная
def deposit(self, amount):
if amount > 0:
self.__balance += amount
else:
raise ValueError("Amount must be positive")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
raise ValueError("Invalid withdrawal amount")
def get_balance(self):
return self.__balance
# Создание объекта и взаимодействие с ним
account = BankAccount(100)
account.deposit(50)
print(account.get_balance()) # 150
account.withdraw(30)
print(account.get_balance()) # 120
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19👍9😁2💊1
Они нужны для того, чтобы определять поведение объектов в различных ситуациях и обеспечивать взаимодействие с базовыми механизмами языка.
Метод
__init__
используется для инициализации новых объектов. Он позволяет задавать начальные значения атрибутов объекта. class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
Метод
__str__
определяет, как объект будет представлен в виде строки. Это удобно для пользовательского вывода. class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, {self.age} years old"
person = Person("Alice", 30)
print(person) # Alice, 30 years old
Dunder методы позволяют перегружать стандартные операторы. Например,
__add__
позволяет определить поведение оператора +
для объектов класса. class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(5, 7)
v3 = v1 + v2
print(v3) # Vector(7, 10)
Метод
__getitem__
позволяет определить, как объект класса будет вести себя при доступе к его элементам по индексу. class CustomList:
def __init__(self, elements):
self.elements = elements
def __getitem__(self, index):
return self.elements[index]
clist = CustomList([1, 2, 3, 4, 5])
print(clist[2]) # 3
Метод
__enter__
и __exit__
позволяют создавать объекты, которые могут использоваться в контекстах with
. class ManagedFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'r')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
with ManagedFile('example.txt') as f:
content = f.read()
print(content)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17