首页 > 解决方案 > 如何在不积极修改代码的情况下扩展日志记录?[编写干净的代码]

问题描述

假设我有一个calculate()计算复杂的方法,其中包含许多变量,而我想记录不同阶段的变量值是多少(编辑:不仅用于验证,还用于数据研究目的)。例如。

# These assignment are arbitrary, 
# but my calculate() method is more complex
def calculate(a, b):
  c = 2*a+b
  d = a-b
  if c > d+10:
    g = another_calc(a, c):
  else:
    g = another_calc(a, d):
  return c, d, g

def another_calc(a, c_d):
  e = a+c_d
  f = a*c_d
  g = e+f
  return g

您可能会假设该方法将进行大量修改以进行实验探索。这里没有太多日志记录,我想记录发生的事情,例如我可以编写这样的激进代码

# These assignment are arbitrary, 
# but my calculate() method is more complex
def calculate(a, b):
  info = {"a": a, "b": b}
  c = 2*a+b
  d = a-b
  info["c"], info["d"] = c, d
  if c > d+10:
    info["switch"] = "entered c"
    g, info = another_calc(a, c, info):
  else:
    info["switch"] = "entered d"
    g, info = another_calc(a, d, info):
  return c, d, g, info

def another_calc(a, c_d, info):
  e = a+c_d
  f = a*c_d
  g = e+f
  info["e"], info["f"], info["g"] = e, f, g
  return g, info

这符合我的目的(我得到了 info 对象,然后将其导出为 CSV 以供我进一步研究)但是在原始 clean 方法中添加更多(非功能性)行calculate()、更改签名和返回值是非常难看的。

但是我可以写一个更干净的代码吗? 我在想是否可以使用装饰器来包装这个方法。希望你们能有一些很好的答案。谢谢。

标签: pythondesign-patternscoding-style

解决方案


编写更简洁代码的一种方法(我的观点)是将 info -dictionary 包装在一个类中。

这是我的简单代码示例:

# These assignment are arbitrary, 
# but my calculate() method is more complex
def calculate(a, b, logger):
  logger.log("a", a)
  logger.log("b", b)

  c = 2*a+b
  d = a-b

  logger.log("c", c)
  logger.log("d", d)

  if c > d+10:
    logger.log("switch", "entered c")
    g = another_calc(a, c)
  else:
    logger.log("switch", "entered d")
    g = another_calc(a, d)

  return c, d, g

def another_calc(a, c_d, logger):
  e = a+c_d
  f = a*c_d
  g = e+f
  logger.log("e", e)
  logger.log("f", f)
  logger.log("g", g)
  return g

class Logger(object):

  data = []

  def log(self, key, value):
    self.data.append({key: value})

  def getLog(self):
    return self.data


logger = Logger()
print(calculate(4, 7, logger))
print(logger.getLog())

优点和缺点

我在这里使用单独的记录器类,因为这样我就不需要知道记录器是如何实现的。在示例中,它只是一个简单的字典,但如果需要,您可以更改创建新记录器的实现。

此外,您可以选择如何打印数据或选择输出。也许你可以有一个 Logger 的接口。

我使用字典是因为看起来您只需要键值对。

现在,使用记录器,我们需要更改方法签名。当然,例如,您可以将默认值定义为 None。然后应该一直检查 None 值,但这就是我没有定义默认值的原因。如果您拥有代码并且可以更改该方法的每个引用calculate(),那么这应该不是问题。

还有一件有趣的事情在以后可能很重要。当您调试了输出并且不再需要记录任何内容时,您就可以实现Null object。使用Null object,您可以删除所有日志记录而无需再次更改代码。

我试图思考如何使用装饰器,但现在找到了任何好方法。如果只记录输出,那么装饰器就可以工作。


推荐阅读