Оставайтесь НАДЕЖНЫМИ и СУХИМИ с помощью сопрограмм и функций как методов в python

#python #python-3.x #dry #solid-principles

#python #python-3.x #сухой #solid-принципы

Вопрос:

У меня есть этот пример кода:

 from time import sleep
import asyncio

class bird:

    def __init__(self, sleeptime=1):
        self.var = sleeptime

    def wait_meep(self):
        sleep(self.var)
        print("Meep")

    def do_sth(self):
        print("Dop Dop Do do ...")


class bird_async:

    def __init__(self, sleeptime=1):
        self.var = sleeptime

    async def wait_meep(self):
        await asyncio.sleep(self.var)
        print("Meep")

    def do_sth(self):
        print("Dop Dop Do do ...")
  

Как вы можете видеть, оба клиента в основном идентичны и должны содержать одно и то же имя (чтобы все знали, чего ожидать). Теперь я хочу быть СУХИМ и писать bird_async(bird) . Потому что каждое расширение в bird также должно использоваться в bird_async . Это возможно, но мой коллега говорит, что это ненадежно, потому что я перезаписал wait_meep . Теперь я ищу разные soultions и нашел абстрактные классы.
Чего я не знаю, так это того, будет ли создание абстрактного класса birdBase(ABC) также надежным. Я бы также перезаписал их, потому что сначала это была функция, а затем подпрограмма, или я здесь ошибаюсь?

Что может быть НАДЕЖНЫМ и сухим решением для объединения этих обоих классов без переименования методов?

Ответ №1:

Сухое решение — это своего рода подкласс, как вы уже делали.

Я думаю, что «твердого» решения очень трудно достичь в ваших условиях. Факт в том, что у вас есть две функции wait_meep , которые имеют фактически разную сигнатуру и семантику. А именно, первый блокирует интервал ожидания, который может быть произвольно длинным. Второй OTOH является асинхронным, то есть требует специальной семантики вызова и выполняется одновременно.

Несколько сопоставимым случаем является Queue класс из стандартной библиотеки. Там у вас есть get и get_nowait методы, которые делают то же самое, но по-разному. Вторым примером могут быть методы __iter__ и __aiter__ .

Поэтому я думаю, что единственным «правильным» решением было бы переименование одного из методов. Что имело бы побочный эффект, заключающийся в том, что вы могли бы записать все это как один класс, то есть уменьшить количество движущихся частей.

Ответ №2:

Проблема

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

Решение

Рассмотрите следующее, чтобы сделать ваш код более надежным и СУХИМ:

 from time import sleep
import asyncio

class Bird:
    DEFAULT_WAIT_MEEP_TEXT = "Meep"
    DEFAULT_DO_STH_TEXT = "Dop Dop Do do ..."

    def __init__(self, sleeptime=1):
        self.sleeptime = sleeptime

    def wait_meep(self, text=DEFAULT_WAIT_MEEP_TEXT):
        sleep(self.sleeptime)
        print(text)
        
    def do_sth(self, text=DEFAULT_DO_STH_TEXT):
        print(text)


class BirdAsync(Bird):
    async def wait_meep_async(self, text=DEFAULT_WAIT_MEEP_TEXT):
        await asyncio.sleep(self.var)
        print(text)
  

Примечания

Идея здесь состоит в том, чтобы создать конкретный метод для выполнения async (DRY) и гарантировать, что жестко заданные значения по умолчанию вместо этого передаются в аргументах (принцип Open-Closed). Конечно, все это можно сделать в исходном классе Bird без использования класса BirdAsync

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

1. Предлагаемая реализация не вызывает sleep(..) в BirdAsync ‘s wait_meep_async(..) асинхронно — она должна была быть вызвана await asyncio.sleep(..) .

2. @Yoel извините, исправлено.