gpt4 book ai didi

用多态替换 Python 条件

转载 作者:太空狗 更新时间:2023-10-30 02:30:53 25 4
gpt4 key购买 nike

我最近阅读了一篇文章/代码片段,其中展示了一个用多态替换条件的示例。这是代码:

之前:

def log_msg(log_type):
msg = 'Operation successful'
if log_type == 'file':
log_file.write(msg)
elif log_type == 'database':
cursor.execute('INSERT INTO log_table (MSG) VALUES ('?')', msg)

之后:

class FileLogger(object):
def log(self, msg):
log_file.write(msg)

class DbLogger(object):
def log(self, msg):
cursor.execute('INSERT INTO log_table (MSG) VALUES ('?')', msg)

def log_msg(obj):
msg = 'Operation successful'
obj.log(msg)

Here是我从哪里得到的。

现在我的问题是,第二种方法在哪些方面比第一种更好?据我所知,如果我想使用第二种方法,每次我想记录一些东西时我都必须做这样的事情:

if log_type == 'file':
log_msg(FileLogger())
elif: log_type == 'database':
log_msg(DbLogger())

我是不是漏掉了重点还是有什么很明显的地方?

最佳答案

Replace Conditional with Polymorphism当您看到相同 条件散布在整个代码中时,重构是最有效的。当您需要添加一种新的行为类型时,您必须找到并更改每个条件以适应新的选项。相反,我们将条件逻辑集中在一个地方 - 创建多态对象的代码 - 让 OO 的语义为我们处理其余部分。


这是您的日志记录示例的一个更令人震惊的稻草人形式。

if log_type == "file":
log_file.write("DEBUG: beginning script")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('DEBUG', 'beginning script')")

try:
file = open("/path/to/file")
lines = file.readlines()

if log_type == "file":
log_file.write("INFO: read {} lines".format(len(lines)))
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('INFO', 'read {} lines')".format(len(lines)))
except:
if log_type == "file":
log_file.write("ERROR: failed to read file")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('ERROR', 'failed to read file')")

raise
finally:
if log_type == "file":
log_file.write("INFO: closing file")
elif log_type == "database":
cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('INFO', 'closing file')")
file.close()

您可以看到检查日志类型的条件逻辑执行了三次,每次都略有不同。如果我们需要添加一种新的日志记录类型,比如通过电子邮件记录错误,我们必须遍历整个脚本并在每个日志语句中添加另一个 elif,这很容易出错并且很麻烦.

也很难一眼看出脚本实际在做什么,因为它淹没在实际进行日志记录的细节中。


所以这是用多态替换条件的一个很好的候选。以下是重构后的记录器类:

class AbstractLogger:
def debug(self, msg):
self.log("DEBUG", msg)

def info(self, msg):
self.log("INFO", msg)

def error(self, msg):
self.log("ERROR", msg)

def log(self, level, msg):
raise NotImplementedError()

class FileLogger(AbstractLogger):
def __init__(self, file):
self.file = file

def log(self, level, msg):
self.file.write("{}: {}".format(level, msg))

class DatabaseLogger(AbstractLogger):
def __init__(self, cursor):
self.cursor = cursor

def log(self, level, msg):
self.cursor.execute("INSERT INTO log_table (Level, Msg) VALUES ('{}', '{}')".format(level, msg))

我使用继承来避免在 FileLogger 和 DatabaseLogger 类之间重复太多代码。

这是脚本:

# create the logger once at the start
if log_type == "file":
logger = FileLogger(log_file)
elif log_type == "database":
logger = DatabaseLogger(cursor)

logger.debug("beginning script")

try:
file = open("/path/to/file")
lines = file.readlines()

logger.info("read {} lines".format(len(lines)))
except:
logger.error("failed to read file")
raise
finally:
logger.info("closing file")
file.close()

现在添加一种新类型的日志记录要容易得多:只需编写一个 EmailLogger 并修改创建它的单个条件。代码也更简洁:记录器类将它们如何工作的所有细节隐藏在一组简单的方法后面,这些方法具有面向日志记录的名称。

关于用多态替换 Python 条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24517069/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com