skyteam超级吕布 发表于 2024-7-21 10:36:56

闭包最后一条返回值的语句

def outer():
    x = 0
    def inner(y=None):
      nonlocal x
      if y:
            x = y
      else:
            return x
    return inner
这是一个闭包函数,请问为什么要加上最后的 return inner ?我太久没看有点忘了

FishC 发表于 2024-7-21 10:40:13

哈哈,没问题,让我这个宅鱼来帮你捞起那些遗忘在脑海深处的计算机知识吧!

首先,闭包是什么呢?闭包是一个函数,它可以记住并访问它所在作用域(比如这里的 `outer` 函数)中的变量,即使这个作用域已经结束了。

你的 `outer` 函数定义了一个内部函数 `inner`,`inner` 可以访问到 `outer` 作用域里的变量 `x`。这就是传说中的“闭包”!

关于你提到的 `return inner`,这是整个闭包机制的核心所在!我们逐步剥开代码的“洋葱皮”,看看其中的奥秘。

def outer():
    x = 0
    def inner(y=None):
      nonlocal x
      if y:
            x = y
      else:
            return x
    return inner

当你调用 `outer()` 的时候,会发生什么呢?啥也没有发生,因为它只是定义了 `inner` 并返回它而已!

my_closure = outer()

这个时候,`my_closure` 就是那个被`outer`返回的 `inner` 函数。重点来了:由于 `inner` 是 `outer` 的一部分,它可以访问 `outer` 的变量 `x` ,即使 `outer` 早就执行完了!

然后你可以像这样使用这个闭包:

print(my_closure())# 0
my_closure(10)
print(my_closure())# 10

看吧,`inner` 函数一直记得那个 `outer` 里面的 `x`,更神奇的是,它还能改变它,所以 `x` 会从 0 变成 10。

所以,加上 `return inner` 是为了将 `inner` 函数作为对象返回给调用者,这样你才能够使用这个闭包,并利用它“记住” `outer` 中的变量。

没有这个 `return inner`,你就没办法拿到 `inner`,也就没办法使用这个闭包了,那整个设计就变得毫无意义啦!

希望这个解答能像鱼油一样,滋润你的编程魂~

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

skyteam超级吕布 发表于 2024-7-21 10:50:38

FishC 发表于 2024-7-21 10:40
哈哈,没问题,让我这个宅鱼来帮你捞起那些遗忘在脑海深处的计算机知识吧!

首先,闭包是什么呢?闭包是 ...

那为啥 return inner 的后面不用加括号和参数啊

FishC 发表于 2024-7-21 10:52:54

skyteam超级吕布 发表于 2024-07-21 10:50
那为啥 return inner 的后面不用加括号和参数啊

哈哈,这个问题问得好,不加括号和参数,简直就像买了鱼却不带酱油一样奇怪是吧?别急,我来给你剥开这个知识点的“洋葱皮”!

在 Python 中,加上括号 `()` 就意味着要立即调用函数,而不加括号则是引用函数对象。我们来看一看具体情况:

1. **不加括号:返回函数对象**

def outer():
    x = 0
    def inner(y=None):
      nonlocal x
      if y:
            x = y
      else:
            return x
    return inner# 注意这里没有括号!

当 `outer` 执行时,它返回的是 `inner` 函数本身,而不是立即调用 `inner`。这样你得到的是一个可以随时调用的闭包。

my_closure = outer()   # my_closure 是 inner 函数对象
print(my_closure)      # <function outer.<locals>.inner at 0x...>

2. **加括号:立即调用函数**

def outer():
    x = 0
    def inner(y=None):
      nonlocal x
      if y:
            x = y
      else:
            return x
    return inner()# 注意这里有括号!

当 `outer` 执行时,它会立刻调用 `inner` 函数并返回 `inner` 的结果,而不是 `inner` 函数本身:

result = outer()# 这里 result 是 inner 的返回值,而不是一个函数对象
print(result)   # 如果你没有传 y,那么 result 是 0;传了 y,就根据 y 的值不同而不同。

所以,`return inner` 是将 **函数对象** 传递给调用者,比如:

my_closure = outer()

这样你拿到的是 `inner` 函数,然后你可以选择什么时候调用,并传递任意参数,就像这样:

print(my_closure())# 0
my_closure(10)
print(my_closure())# 10

而 `return inner()` 会立即执行 `inner` 并返回其结果,再也无法通过 `my_closure` 去改变 `x` 了。所以:

result = outer()# result 是 inner 的即时返回值,比如 0

简而言之,如果你希望获得一个函数对象以便日后调用,请 **不加括号**;如果你希望获得一个函数的返回值,请 **加括号**。

希望这个解释能够帮你理清这其中的奥秘!像吃鱼一样,享受每一步的过程,编码也是如此~

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

skyteam超级吕布 发表于 2024-7-21 10:58:15

FishC 发表于 2024-7-21 10:52
哈哈,这个问题问得好,不加括号和参数,简直就像买了鱼却不带酱油一样奇怪是吧?别急,我来给你剥开这 ...

将函数对象传递给调用者,这个调用者是谁啊?
调用者接收了这个函数对象后会怎么使用这个函数对象?

FishC 发表于 2024-7-21 10:58:31

skyteam超级吕布 发表于 2024-07-21 10:58
将函数对象传递给调用者,这个调用者是谁啊?
调用者 ...

哈哈,这个问题问得太好了,简直就像鱼油对鱼尾巴的追问一样执着!来,我们细细捞一捞这些函数对象的故事。

### 调用者是谁?

在我们的例子里,调用者就是你写的代码里的那个部分,即你调用 `outer()` 的地方。例如:

my_closure = outer()

在这行代码中,`outer()` 被调用了,它返回的是 `inner` 函数对象,这个对象被赋值给了 `my_closure`。所以说,调用者就是整个执行过程中接收到函数对象的变量 `my_closure`。

### 调用者如何使用函数对象?

一旦调用者拿到这个函数对象后,它可以像正常的函数一样随时调用它。这也是闭包的威力所在!它能“记住”之前作用域中的变量,并在多次调用时保持状态。

继续我们之前的例子:

def outer():
    x = 0
    def inner(y=None):
      nonlocal x
      if y:
            x = y
      else:
            return x
    return inner

# outer() 返回 inner 函数对象,并赋值给 my_closure
my_closure = outer()

# 调用 inner 函数对象(即 my_closure)
print(my_closure())# 输出: 0,初始值为0

# 再次调用 my_closure 并传入参数
my_closure(10)

# 调用 my_closure 查看更改后的值
print(my_closure())# 输出: 10,x 被更改为 10

这里发生了什么呢?让我们剖析一下:

1. **首先调用 `outer()`**:`outer` 函数定义了一个内部函数 `inner` 并返回它的引用(函数对象),因此 `my_closure` 引用的是 `inner` 函数。

2. **`my_closure()` 调用**:当你第一次调用 `my_closure()` 时,相当于在调用 `inner()` 函数,`nonlocal x` 保证了 `x` 指向的是 `outer` 作用域中的 `x`。此时 `y` 为空,所以返回 `x` 的当前值 0。

3. **`my_closure(10)` 调用**:这次传递了参数 `y=10`,于是 `x` 被更新为 10。

4. **再次调用 `my_closure()`**:返回更新后的 `x` 值,即 10。

### 实际应用案例

闭包的这种特性在很多实际开发中非常有用,比如:

- **回调函数**:你需要在未来某个时刻调用一个函数并希望它记住当前的状态。
- **工厂函数**:生成带有某些特定属性或行为的函数。
- **实例替代**:有时候你不需要一个完整的类,只需要一个简单的闭包来保存状态。

举个简单的例子:

def make_multiplier(multiplier):
    def multiplier_function(value):
      return value * multiplier
    return multiplier_function

times3 = make_multiplier(3)
times5 = make_multiplier(5)

print(times3(10))# 输出: 30
print(times5(10))# 输出: 50

在这个例子中,`make_multiplier` 函数生成并返回一个 `multiplier_function` 闭包函数。不同的 `multiplier` 值生成不同的函数对象,且这些函数对象记住了生成它们时的 `multiplier` 值。

希望这样解释清楚了!就像在水里游泳一样,编程的乐趣也是无穷的~ Keep coding and enjoy!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

skyteam超级吕布 发表于 2024-7-21 11:15:02

FishC 发表于 2024-7-21 10:58
哈哈,这个问题问得太好了,简直就像鱼油对鱼尾巴的追问一样执着!来,我们细细捞一捞这些函数对象的故 ...

ok这回懂了
页: [1]
查看完整版本: 闭包最后一条返回值的语句