我来自C#背景,为了实现策略模式,我们将始终使用接口,例如:ILoggger。现在,据我了解,在Python等鸭类语言中,我们可以避免这种基类/合同。
我的问题是,这是通过利用鸭子输入来实现策略模式的最佳方法吗?并且,这种鸭子式的输入方式是否使我的代码的下一个用户清楚这是“扩展点”?另外,我认为最好使用类型提示来帮助下一个查看您的代码的人以了解策略的类型,但是在没有基类/合同的情况下使用鸭子类型时,您使用哪种类型?已经具体的课程之一?
以下是一些代码:
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
有人可以建议解决此问题的最标准/ Pythonic /可读方式是什么?
我不是在寻找“两行代码中的答案”。
答案 0 :(得分:2)
如果您真的想一直学习 classes 并强制您使用基类,请创建一个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
然后使用它们:
# import typing # for other type things
class DoIt:
def __init__(self, logger: BaseLogger):
# enforce usage of BaseLogger implementation
if isinstance(logger, BaseLogger):
self.logger = logger
else:
raise ValueError("logger needs to inherit from " + BaseLogger.__name__)
def log(self, message):
# use the assigned logger
self.logger.log(message)
# provide different logger classes
d1 = DoIt(ConsoleLogger())
d2 = DoIt(FileLogger())
for k in range(5):
d1.log(str(k))
d2.log(str(k))
with open(d2.logger.fn) as f:
print(f.read())
try:
d3 = DoIt( NotALogger())
except Exception as e:
print(e)
输出:
0
1
2
3
4
file: 0
file: 1
file: 2
file: 3
file: 4
logger needs to inherit from BaseLogger
作为旁注:python已经具有相当复杂的日志记录功能。如果这只是您查询的唯一目的,请查看Logging。
答案 1 :(得分:1)
在Python中,由于运行时的安全性和优雅的终止,通常不需要具有编译时类型强制的全面策略模式。
如果您希望自定义现有代码的某些部分,通常的做法是:
__call__
的对象);
if
/ else
中进行测试,或者在某个注册表中查找(无论是模块全局还是类本地或宾语);
enum
,但在简单情况下,这样做的好处被认为是太多缺点(调试时不可读,需要样板),因为与C#相比,Python在灵活性方面更具灵活性-与可扩展性量表。答案 2 :(得分:1)
据我所知,在Python中实现策略模式的最常见方法是传递一个函数(或可调用函数)。函数是Python中的一流对象,因此,如果所有消费者需要的只是一个函数,则您不需要提供更多的功能。当然,您可以根据需要添加注释。假设您只想记录字符串:
class ComponentThatNeedsLogger:
def __init__(self, log_func: Callable[[str], None]):
self._log_func = log_func
这允许您即时创建一个简单的记录器:
ComponentThatNeedsLogger(
log_func=print
)
但是您还可以利用类的所有功能来创建复杂的记录器并仅传递相关方法。
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
)