下面是一个有两种不同实现的类ProgressPrinter
的略长示例,具体取决于它所使用的文件描述符是连接到控制台还是其他什么。用户可以通过调用ProgressPrinter.create()
创建实例,该函数有一个方便的默认参数:
import sys
class ProgressPrinter:
def __init__(self, file):
self._file = file
def print_progress(self, message):
raise NotImplementedError
@classmethod
def create(cls, file=sys.stderr):
if file.isatty():
return TTYProgressPrinter(file)
else:
return NoTTYProgressPrinter(file)
class TTYProgressPrinter(ProgressPrinter):
def print_progress(self, message):
# Image some fancy stuff with ANSI escape codes.
pass
class NoTTYProgressPrinter(ProgressPrinter):
def print_progress(self, message):
print(message, file=self._file)
重写它的最佳方式是什么,这样用户就可以直接调用ProgressPrinter([file])
,而不是调用ProgressPrinter.create([file])
,从而获得相同的结果
我尝试过重写__new__()
,甚至写了一个元类,但都没有成功(很多TypeError: object() takes no parameters
和TypeError: __init__() missing 1 required positional argument: 'file'
,甚至还有一个RecursionError: maximum recursion depth exceeded while calling a Python object
)
更新:删除示例中的冗余代码。
更新:使实现实际上成为基类的子类
只是克制一下。您真正想要的是用户对您的代码感到满意。当我看到
ProgressPrinter.create([file])
时,我可以假设一个工厂将创建与ProgressPrinter
相关但可能属于不同类的对象。您希望它们是子类,这很好但是,当我看到
ProgressPrinter([file])
时,我希望对象正好来自类,而不是子类的成员。这意味着这段代码将更难阅读和理解,这是您肯定不想要的。我知道Python允许它,我甚至知道如何做到这一点。但这是不自然的,因为程序员不需要这样做规则是遵循既定的模式。您可以按照@Barmar的建议使用委托给子对象(非子类),也可以坚持工厂模式。但请不要使用反模式
如果您真的想这样做,诀窍是在new中构建并配置一个适当子类的对象。但是必须使用
Object.__new__
来构建子类的对象。如果只使用标准创建,则会递归到ProgressPrinter.__new__
,导致堆栈溢出最小的改变是用这个
__new__
方法替换__init__
方法:您还可以删除create方法并将其代码直接复制到
__new__
但这只是向您展示Python语言的可能性,我仍然强烈建议您不要在生产代码中使用它,并坚持使用更好的工厂模式。未来的维护人员肯定会责怪您使用这种反模式
通过委托而不是子类化,在
__init__
方法中选择实现类来完成下面是一个让您起步的工作示例
现在我们可以这样做:
此时,您一定已经注意到,
IceCream
只是一个伪装的函数,以复杂的方式调用如果需要为参数返回正确的值(而不是其他内部状态),我建议您在本例中使用工厂函数(而不是方法)
相关问题 更多 >
编程相关推荐