Python内部函数它们有什么好处?

发布于:2021-01-19 16:43:18

0

282

0

Python 内部函数

让我们看看编写内部函数的三个常见原因。

注意:在Python中,函数是“一等公民”。这意味着它们与任何其他对象(整数、字符串、列表、模块等)相当。您可以动态地创建或销毁它们、将它们传递给其他函数、将它们作为值返回等等。

本教程使用Python 3.4.1版。

封装

您使用内部函数来保护它们不受函数外部发生的任何事情的影响,这意味着它们被隐藏在全局范围之外。

这里有一个简单的例子突出了这个概念:

def outer(num1):
   def inner_increment(num1):  # Hidden from outer code
       return num1 + 1
   num2 = inner_increment(num1)
   print(num1, num2)

inner_increment(10)
# outer(10)

尝试调用inner_increment()

Traceback (most recent call last):
 File "inner.py", line 7, ininner_increment()
NameError: name 'inner_increment' is not defined

现在注释掉inner_increment()调用并取消外部函数调用的注释,outer(10),作为参数传入:

10 11

注意:请记住这只是一个示例。尽管这段代码确实达到了预期的效果,但是最好使用前导下划线将inner_increment()变成顶级的“私有”函数:_inner_increment()。

下面的递归示例是嵌套函数的一个稍微好一点的用例:

def factorial(number):

   # Error handling
   if not isinstance(number, int):
       raise TypeError("Sorry. 'number' must be an integer.")
   if not number >= 0:
       raise ValueError("Sorry. 'number' must be zero or positive.")

   def inner_factorial(number):
       if number <= 1:
           return 1
       return number*inner_factorial(number-1)
   return inner_factorial(number)

# Call the outer function.
print(factorial(4))

也测试一下。使用此设计模式的一个主要优点是,通过在外部函数中执行所有参数检查,可以安全地跳过内部函数中的错误检查。


保持干燥

也许您有一个巨大的函数,它在许多地方执行相同的代码块。例如,您可能编写了一个处理文件的函数,并且希望接受打开的文件对象或文件名:

def process(file_name):
   def do_stuff(file_process):
       for line in file_process:
           print(line)
   if isinstance(file_name, str):
       with open(file_name, 'r') as f:
           do_stuff(f)
   else:
       do_stuff(file_name)

注意:同样,通常只将do_stuff()设为私有顶级函数,但如果您希望将其作为内部函数隐藏,则可以。一个实际的例子怎么样?假设您想知道纽约市WiFi热点的数量。是的,这个城市有原始数据告诉我们。访问站点并下载CSV:

def process(file_name):

   def do_stuff(file_process):
       wifi_locations = {}

       for line in file_process:
           values = line.split(',')
           # Build the dict and increment values.
           wifi_locations[values[1]] = wifi_locations.get(values[1], 0) + 1

       max_key = 0
       for name, key in wifi_locations.items():
           all_locations = sum(wifi_locations.values())
           if key > max_key:
               max_key = key
               business = name

       print(f'There are {all_locations} WiFi hotspots in NYC, '
             f'and {business} has the most with {max_key}.')

   if isinstance(file_name, str):
       with open(file_name, 'r') as f:
           do_stuff(f)
   else:
       do_stuff(file_name)

运行函数:

>>> process('NAME_OF_THE.csv')
There are 1251 WiFi hotspots in NYC, and Starbucks has the most with 212.

闭包和工厂函数

现在我们来讨论使用内部函数的最重要原因。到目前为止,我们看到的所有内部函数示例都是普通函数,只是碰巧嵌套在另一个函数中。换句话说,我们可以用另一种方式定义这些函数(如前所述)。没有具体的理由说明为什么需要嵌套它们。

但是当涉及到闭包时,情况并非如此:必须使用嵌套函数。

什么是闭包?

闭包只会使内部函数在调用时记住其环境的状态。初学者通常认为闭包是内部函数,但它实际上是由内部函数引起的。闭包“关闭”堆栈上的局部变量,在堆栈创建完成后,这个问题仍然存在。

一个例子

这里有一个例子:

def generate_power(number):
   """
   Examples of use:

   >>> raise_two = generate_power(2)
   >>> raise_three = generate_power(3)
   >>> print(raise_two(7))
   128
   >>> print(raise_three(5))
   243
   """

   # Define the inner function ...
   def nth_power(power):
       return number ** power
   # ... that is returned by the factory function.

   return nth_power

示例中发生了什么

让我们看看这个例子中发生了什么:

  1. generate_power()是工厂函数,仅表示每次调用它都会创建一个新函数,然后返回新创建的函数。因此,raise_two()和raise_three()是新创建的功能。

  2. 这个新的内部函数有什么作用?它只接受一个参数power,然后返回number**power。

  3. 内部函数从哪里获得价值number?这就是闭包起作用的地方:从外部函数(工厂函数)nth_power()获取值power。让我们逐步完成此过程:

  • 调用外部函数:generate_power(2)。

  • Build nth_power(),它接受一个参数power。

  • 拍摄的状态快照nth_power(),其中包括number=2。

  • 将该快照传递到中generate_power()。

  • 返回nth_power()。

换句话说,闭包“初始化”其中的数字栏nth_power(),然后将其返回。现在,无论何时调用该新返回的函数,它都将始终看到其自己的私有快照,其中包括number=2。

结论

闭包和工厂函数的使用是内部函数的最常见和最强大的用法。在大多数情况下,当您看到修饰的函数时,修饰器是一个工厂函数,它将一个函数作为参数并返回一个新函数,该新函数在闭包内部包括旧函数。停止。深吸一口气。喝杯咖啡。再读一遍。

换句话说,装饰器只是用于实现generate_power()示例中概述的过程的语法糖。

我给您留下最后一个示例:

def generate_power(exponent):
   def decorator(f):
       def inner(*args):
           result = f(*args)
           return exponent**result
       return inner
   return decorator


@generate_power(2)
def raise_two(n):
   return n

print(raise_two(7))


@generate_power(3)
def raise_three(n):
   return n

print(raise_two(5))

如果您的代码编辑器允许,并排查看generate_power(exponent)generate_power(number)以说明所讨论的概念。(例如,Sublime Text具有列视图。)

如果尚未对这两个函数进行编码,请打开“代码编辑器”并开始编码。对于新程序员来说,编码是一项实践活动:就像骑自行车一样,你只需要自己动手,自己动手。所以回到手头的任务!

键入代码后,您现在可以清楚地看到,它的相似之处在于产生相同的结果,但也存在差异。对于那些从未使用过decorators的人来说,如果你冒险沿着这条路走下去,注意这些差异将是理解它们的第一步。