【问题标题】:Structuring function calls in Python在 Python 中构造函数调用
【发布时间】:2018-11-07 21:17:44
【问题描述】:

我不知道如何构造函数调用。我知道“干净的代码规则”说函数应该只做一件事,并且你应该让它们尽可能小。所以我愿意。

但我不确定如何调用这些函数。我不知道如何命名这些概念,但我正在努力在“在函数内部调用函数”或“链接/管道函数”之间做出选择。在下面的示例中,我有一个非常基本的示例,但我希望它使我所谈论的概念更加清晰。

“Chaining/Piping”使代码更容易进行单元测试,您不需要模拟函数。但是“调用内部函数”使代码更短,可能更具可读性。

你如何选择一种方式或另一种方式?

顺便说一句,如果有人能指出一个好的来源,我很难寻找这些概念之间的差异——那就太好了。

# Calling inside a function

def sum_of_squares(numbers):
    return sum([square(num) for num in numbers])

def square(num):
    return num*num

numbers = [2,3,4]
square_sum = sum_of_squares(numbers))

print(square_sum) # prints 29

-----

# Chaining/Piping

def sum_of_squares(squares):
    return sum(squares)

def square(num):
    return num*num

numbers = [2,3,4]
squares = [ square(num) for num in numbers ]
square_sum = sum_of_squares(squares))

print(square_sum) # prints 29

【问题讨论】:

  • 一个函数不需要只做一件事,它可以做多个简单的事情,更重要的是它不会(通常)将过程调用(例如写入文件)与有用的返回值。
  • 第二种方式客观上是不好的,因为sum_of_squares只是一个名字不好的sum。这与单元测试完全无关。
  • 对于您的示例,单个函数就可以了,或者使用顶部的函数(底部的函数只是 sum_of_squares = sum 无意义
  • def sum_of_squares(numbers): return sum(num*num for num in numbers) 会很好——当然对于不那么琐碎的例子,分解代码是有意义的
  • 感谢您的回答,我对不同类型的(虚构的?)概念更感兴趣。也许这个例子太基础了,我应该在代码审查交流中发布我的代码。

标签: python unit-testing functional-programming


【解决方案1】:

基本上这两种类型的声明都是好的和可用的。 您可以根据要实现的目标和上下文选择其中一种。 最重要的方面之一是代码的透明度可读性。 您总是应该在两种类型的声明之间找到权衡

如果您在一个(或几个)函数中编写代码,则代码的主要流将非常透明和清晰,但是如果有人(或只是您自己)需要阅读和解释您的代码,您将拥有总是跳回到函数声明,然后如果你在该函数中有另一个函数调用,然后再进一步等等,这可能非常不方便,而且你很可能会在一段时间后丢失该行。

另一方面,如果你“链接”了很多函数调用,就像你做的第二种方式一样,那么前面的部分,代码的主流会增长,一段时间后变得不那么透明。我的主要经验法则是始终将真正足以理解您的代码的内容放在“前面”,并始终选择一些简单的不言自明的函数命名约定以便以后能够很好地阅读您的代码。

另一方面,如果你想使用相同的代码序列,你应该在一个函数中进行。在这种情况下,您显然会更喜欢第一种方式。

希望对你有帮助。

【讨论】:

    【解决方案2】:

    首先你应该准备好用于链接/管道的函数,然后你可以让另一个函数包装所有函数以实现为调用者提供更高级别抽象的特定任务。

    【讨论】:

      【解决方案3】:

      在我看来,干净代码的概念意味着保持代码的一致性和对其他人的可读性。以下是我认为可以使代码清晰易读的内容。

      任何没有编写过代码或没有先验知识的人 关于代码做了什么应该能看懂到底是什么 结果将在代码运行时给出,而您不必 向他们解释你的代码是做什么的。

      所以,对我来说,平方函数是不必要的。 number * number 是不言自明的。

      所以我的平方和将是

      numbers = [2, 3, 4]
      square_sum = sum(number * number for number in numbers)
      

      或者如果你需要一个平方函数

      numbers = [2, 3, 4]
      sqaureFunction = lambda number: number * number
      square_sum = sum(map(sqaureFunction, numbers))
      

      在像这样的小程序中,这无关紧要,您不应该花时间在干净的代码上,因为这个程序并没有做太多的事情。所以,我将举一个更大的例子来解释这一点......

      想象一下,您正在编写一个 HTML 解析器,使用漂亮的汤从我的用例网站中提取信息。这是您的算法在抽象中的样子。

      • 从网站下载 HTML。
      • 把它交给像 bs4 或 lxml 这样的 HTML 解析器。
      • 浏览 HTML 汤中的每个标签并提取信息。
      • 存储它。

      现在是详细的部分,

      第 1 部分 HTML 解析:提取

      • 首先,从获取要提取的网站的 URL 开始
      • 收集所需的信息标题并发布数据以获取实际信息
      • 使用必要的标头和有效负载向网站发出请求,返回 HTML
      • 将 HTML 写入文件,以便您可以重复使用它,而无需在每次测试时发出多个请求。

      HTML 解析的第 2 部分:解析器

      • 从文件中加载 HTML
      • 在 HTML 中导航并获取所需数据
      • 将信息存储在数据库或文件中。

      最后是代码:

      我只是在这里编写提取部分的代码。请参阅 ma​​keHttpRequest 函数的文档了解干净代码的概念

      def makeGetRequest(url, headers = []):
          # You may want to preprocess. Add default headers, etc ... which are specific to the site.
          response = requests.get(url, headers=headers, params=payload)
          return response
      
      def makeHttpRequest(url, method = "GET", headers = [], payload = {}):
          """
          This function makes an HTTP request.
      
          You may wanna do several requests before getting the actual information.
          But every time you're going to call make request because it does one thing i.e. Making a request. 
          There can be 101 things that can happen inside this function. 
          But on the outlook, it is just making a single request which can considered as 1 task.
          """
          # You may wanna do POST, PUT, PATCH. But this is just for giving general idea
          if method == "GET":
              response = makeGetRequest(url, headers=headers)
              return response.text
          return None
      
      def extractor(storageFilename):
          """
          This function will make HTTP request and get HTML data.
          After getting HTML data it will store it in a file.
          """
          (url, headers, payload) = loadRequestInformation()
          html = makeHttpRequest(url, headers=headers, payload=payload)
          with open(storageFilename) as htmlFileHandle:
              htmlFileHandle.write(html)
      

      【讨论】:

      • 我喜欢你在这里用一个更大的例子来解释这些概念的方式。但是,您应该注意使用可变值作为默认参数 - 这是您应该避免的 common gotcha
      猜你喜欢
      • 2012-01-10
      • 1970-01-01
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 2011-12-07
      • 2019-03-14
      • 1970-01-01
      相关资源
      最近更新 更多