python中的策略模式

2024-04-25 11:37:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我来自C的背景,为了实现一个策略模式,我们总是使用一个接口,例如:ilogger。据我所知,在像Python这样的duck类型的语言中,我们可以避免这个基类/契约。在

我的问题是,这是利用duck类型实现策略模式的最佳方法吗?而且,这种duck-typing方法是否能让我代码的下一个用户明白这是一个“扩展点”?另外,我认为最好使用类型提示来帮助下一个查看代码的人了解策略的类型,但是对于没有基类/契约的duck类型,您使用哪种类型?一个已经很具体的课程?在

下面是一些代码:

class FileSystemLogger():
    def log(self, msg):
        pass

class ElasticSearchLogger():
    def log(self, msg):
        pass

# if i wanted to use type hints, which type should logger be here?
class ComponentThatNeedsLogger():
    def __init__(self, logger):
        self._logger = logger

# should it be this?
class ComponentThatNeedsLogger():
    def __init__(self, logger : FileSystemLogger):
        self._logger = logger

有人能告诉我什么是最标准的/Python式的/可读的处理方法吗?在

我不是在寻找“这里是答案在2行代码”的答案。在


Tags: 方法代码selflog类型def模式msg
3条回答

在Python中,由于运行时安全和优雅的终止,通常不需要具有编译时类型强制的全面策略模式。在

如果希望自定义现有代码的某些部分,通常的做法是:

  • 无论是通过子类化,包括mix-in,还是仅仅赋值来替换适当的方法(方法和其他方法一样是活动对象的一个属性,可以同样地重新赋值;替换的方法甚至可以不是函数,而是带有__call__的对象);
    • 请注意,在Python中,通过将代码的定义放在函数中,可以动态地创建包含代码(包括函数和类)的对象。然后在封闭函数执行时计算定义,并且可以使用可访问变量(也称为闭包)作为特殊参数;或者
  • 在某个时间点接受回调(或在适当的时候调用其方法的对象,有效地充当一组回调);或
  • 接受一个字符串参数,该参数是来自某个集合的常量,然后代码将在if/else中进行测试,或者在某个注册表中查找(无论是对模块是全局的,还是对类或对象的局部的);
    • 从3.4开始就有enum,但是对于简单的情况,它被认为是太多的缺点而没有好处(在调试时是不可读的,需要样板文件),因为Python在灵活性和可伸缩性方面比C更具灵活性。在

如果您真的想一直使用并强制使用基类,请创建一个ABC: abstract base class / method及其一些实现:

属性:用于查找目的的Alex Vasses answer here

from abc import ABC, abstractmethod

class BaseLogger(ABC):
    """ Base class specifying one abstractmethod log - tbd by subclasses."""
    @abstractmethod
    def log(self, message):
        pass

class ConsoleLogger(BaseLogger):
    """ Console logger implementation."""
    def log(self, message):
        print(message)

class FileLogger(BaseLogger):
    """ Appending FileLogger (date based file names) implementation."""
    def __init__(self):
        import datetime 
        self.fn = datetime.datetime.now().strftime("%Y_%m_%d.log")

    def log(self,message):
        with open(self.fn,"a") as f:
            f.write(f"file: {message}\n")

class NotALogger():
    """ Not a logger implementation."""
    pass

然后使用它们:

^{pr2}$

输出:

0
1
2
3
4 
file: 0
file: 1
file: 2
file: 3
file: 4

logger needs to inherit from BaseLogger

顺便说一句:python已经具有相当复杂的日志记录能力。如果这是你调查的唯一目的,请调查Logging。在

据我所知,在Python中实现策略模式最常见的方法是传递函数(或可调用函数)。函数是Python中的第一类对象,因此,如果消费者需要的只是一个函数,那么您不需要提供更多的函数。当然,如果你想的话,你可以给它加注释。假设您只想记录字符串:

class ComponentThatNeedsLogger:
    def __init__(self, log_func: Callable[[str], None]):
        self._log_func = log_func

这允许您动态创建一个简单的记录器:

^{pr2}$

但是您也可以利用类的所有功能来创建一个复杂的记录器并只传递相关的方法。在

class ComplexLogger:
    def __init__(self, lots_of_dependencies):
        # initialize the logger here

    def log(self, msg: str): None
        # call private methods and the dependencies as you need.

    def _private_method(self, whatever):
        # as many as you need.

ComponentThatNeedsLogger(
    log_func= ComplexLogger(lots_of_dependencies).log
)

相关问题 更多 >