鱼C论坛

 找回密码
 立即注册
查看: 221|回复: 11

[已解决]y变量是什么时候被赋的值呢?

[复制链接]
发表于 2024-7-27 23:30:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
class E:
    def __init__(self, func):
        self.func = func
    def __del__(self):
        self.func(self)


def outter():
    x = 0
    def inner(y=None):
        nonlocal x
        print(f'在函数内部显示x变量:{x}')
        print(f'在函数内部显示y变量:{y}')
        if y:
            x = y
        else:
            return x
    return inner

f = outter()
e = E(f)

运行结果:
在函数内部显示x变量:0
在函数内部显示y变量:<__main__.E object at 0x0000018D1450E8B0>

请问各位老师,在上面这段代码中,为什么是这样的结果呢?是什么时候给y值赋值了呢?我个人以为,在E这个类实例化的时候,是首先调用__init___函数的进行对象初始化的,但在__init___中并没有给y赋值啊?请问为什么y值了呢?谢谢大家。
最佳答案
2024-7-28 13:08:55
wbzxz 发表于 2024-07-28 13:08
销毁e引发的? 我在上面代码中,只有 e = E(f)这一条 ...


你说得对,我之前的解释有点不够清楚,让我们来逐步澄清一下这一点。

在你的代码中,`e = E(f)` 创建了一个 `E` 类的实例,并将 `inner` 函数绑定到这个实例上。此时,`y` 的值并没有被设置,因为我们并没有显式地调用 `inner` 函数。

关键的地方在于,当 `e` 这个对象被销毁的时候,Python 的垃圾回收机制会调用 `__del__` 方法,这个过程并不需要你手动执行 `del e`。当程序执行结束或没有引用指向 `e` 时,垃圾回收会自动进行,触发 `__del__` 方法。

### 这里是发生的事情:

1. **创建实例**:当你运行 `e = E(f)` 时,`E` constructor (`__init__`) 同时也接受了 `f` 这个函数,并保存在 `self.func` 中,但此时 `inner` 函数并没有执行,因此 `y` 仍然为 `None`。

2. **对象销毁**:在某个时刻(例如,当程序结束,或者没有其他变量引用 `e` 时),Python 的垃圾回收机制开始工作并找到了 `e` 对象,然后调用 `__del__` 方法。在这个时候,`self.func(self)` 被执行,而 `self.func` 是 `inner` 函数,这个函数的参数 `y` 就被赋值为 `self`(指向 `e` 对象)。

3. **打印输出**:在 `inner` 函数内部,你第一次打印 `y` 的时候,`y` 被传入了 `self`,这是因为 `inner` 现在得到了 `e` 的引用,所以你看到的 `y` 是 `e` 的对象。

所以,总的来说,`y` 的赋值是在 `inner` 函数被 `__del__` 调用的时候而不是在你显式运行的代码中。

感谢你的耐心,我希望这个解释更加明确了!如果还有疑问,请继续问我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-7-27 23:30:54 | 显示全部楼层
这是一个很好的问题!我们来分析一下这段代码,揭开 y 变量的神秘面纱。

首先,我们有一个外部函数 `outter()`,在这个函数内部定义了一个内部函数 `inner(y=None)`。这里的 `y` 设定了一个默认值为 `None`,所以当 `inner` 函数被调用但没有传入参数时,`y` 就会保持为 None。

然后,当你执行 `f = outter()` 时,`outter()` 被调用,返回的是 `inner` 函数的引用,赋值给变量 `f`。此时,`x` 被初始化为 `0`,但 `y` 并没有被赋予任何值,它依然是 `None`。

接下来你创建了一个对象 `e = E(f)`,这时 `E` 类的 `__init__` 方法接收了 `inner` 函数(也就是 `f`),并将其保存在 `self.func` 中。`__init__` 方法执行完后,`E` 类的实例就已创建完成,但此时 `inner` 函数并没有被执行,因此 `y` 依然是 `None`。

最终,`__del__` 方法是在对象 `e` 被销毁时自动调用的。你并没有直接调用 `inner` 函数,所以它并没有机会去接受一个参数,这也是为什么在打印结果中 `y` 仍然是 `None`。

总结一下:`y` 只有在 `inner` 被调用时才会被赋值。如果不调用 `inner` 并且没有传入参数,它的默认值一直是 `None`,而你现在的代码并没有显式地调用 `inner`。所以, `y` 没有值!

希望这个解释能帮你更好地理解这个问题,也希望我的幽默能让你的编程之路更加轻松!如果还有其他问题,尽管问!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-7-28 13:06:37 | 显示全部楼层
FishC 发表于 2024-7-27 23:30
这是一个很好的问题!我们来分析一下这段代码,揭开 y 变量的神秘面纱。

首先,我们有一个外部函数 `out ...

不对啊,按照您所说的,只有执行了 “del e ”这个语句的时候,才会给y赋值(对象本身)。
但是为什么代码中,没有执行del语句,也已经完成了赋值呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-28 13:06:48 | 显示全部楼层
wbzxz 发表于 2024-07-28 13:06
不对啊,按照您所说的,只有执行了 “del e ”这个语 ...


好的,看来我们有必要深入一步,来澄清这个问题。

首先,让我们明确一下 `__del__` 方法的作用。这个方法是在对象被垃圾回收机制回收时自动调用的,而不是在你显式调用 `del` 的时候。因此,`y` 变量的赋值并不是因为你手动删除对象 `e`。

实际上,`y` 只有在你调用 `inner` 函数的时候才会受影响。这样的话,整个过程是这样的:

1. 当 `outter()` 被调用时,内部定义的 `inner` 函数被返回,并且 `x` 初始化为 `0`。此时,`y` 仍然是 `None`,因为你没有调用 `inner`。
  
2. 然后你创建了一个 `E` 类的实例 `e`,把这个 `inner` 函数传递进去。此处 `__init__` 方法运行,但 `y` 仍然没有被赋值。

3. 现在,关键点来了!当 `E` 类的实例被创建后,**并没有触发对象的销毁**。所以在这个例子中,`__del__` 方法不会立刻执行。

4. 实际上,`__del__` 自动在对象的生命周期结束时调用。在这个过程中,它会执行 `self.func(self)`,而此时 `self.func` 指向的是 `inner` 函数,参数为 `self`(这里即是 `e`)。

5. 当 `inner(self)` 被调用时,它进入了 `inner` 函数内部,此时 **`y` 被赋值为 `self`**,也就是 `e` 的引用。

因此,如果你查看控制台输出,你会看到 `在函数内部显示y变量:<__main__.E object at ...>`,说明 `y` 现在确实拥有一个对象的引用。

希望现在一切都更清楚了!`y` 的赋值发生在 `__del__` 方法调用时,也就是你没有直接调用,而是通过销毁 `e` 对象引发的。如果还有更多的问题或需要进一步探讨,请随时问哦!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-7-28 13:08:43 | 显示全部楼层
FishC 发表于 2024-7-28 13:06
好的,看来我们有必要深入一步,来澄清这个问题。

首先,让我们明确一下 `__del__` 方法的作用。这 ...

销毁e引发的? 我在上面代码中,只有 e = E(f)这一条语句,没有销毁对象e啊。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-28 13:08:55 | 显示全部楼层    本楼为最佳答案   
wbzxz 发表于 2024-07-28 13:08
销毁e引发的? 我在上面代码中,只有 e = E(f)这一条 ...


你说得对,我之前的解释有点不够清楚,让我们来逐步澄清一下这一点。

在你的代码中,`e = E(f)` 创建了一个 `E` 类的实例,并将 `inner` 函数绑定到这个实例上。此时,`y` 的值并没有被设置,因为我们并没有显式地调用 `inner` 函数。

关键的地方在于,当 `e` 这个对象被销毁的时候,Python 的垃圾回收机制会调用 `__del__` 方法,这个过程并不需要你手动执行 `del e`。当程序执行结束或没有引用指向 `e` 时,垃圾回收会自动进行,触发 `__del__` 方法。

### 这里是发生的事情:

1. **创建实例**:当你运行 `e = E(f)` 时,`E` constructor (`__init__`) 同时也接受了 `f` 这个函数,并保存在 `self.func` 中,但此时 `inner` 函数并没有执行,因此 `y` 仍然为 `None`。

2. **对象销毁**:在某个时刻(例如,当程序结束,或者没有其他变量引用 `e` 时),Python 的垃圾回收机制开始工作并找到了 `e` 对象,然后调用 `__del__` 方法。在这个时候,`self.func(self)` 被执行,而 `self.func` 是 `inner` 函数,这个函数的参数 `y` 就被赋值为 `self`(指向 `e` 对象)。

3. **打印输出**:在 `inner` 函数内部,你第一次打印 `y` 的时候,`y` 被传入了 `self`,这是因为 `inner` 现在得到了 `e` 的引用,所以你看到的 `y` 是 `e` 的对象。

所以,总的来说,`y` 的赋值是在 `inner` 函数被 `__del__` 调用的时候而不是在你显式运行的代码中。

感谢你的耐心,我希望这个解释更加明确了!如果还有疑问,请继续问我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-7-28 14:25:03 | 显示全部楼层
class E:
    def __init__(self, func):
        self.func = func
    def __del__(self):
        self.func(self)


def outter():
    x = 0
    def inner(y=None):
        nonlocal x
        print(f'在函数内部显示x变量:{x}')
        print(f'在函数内部显示y变量:{y}')
        if y:
            x = y
        else:
            return x
    return inner

f = outter()
e = E(f)
g = f()

谢谢您,好像快懂啦,那您能给解释一下上面的运行代码为什么是列出的运行结果吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-28 14:25:14 | 显示全部楼层
wbzxz 发表于 2024-07-28 14:25
class E:
    def __init__(self, func):
        s ...


当然可以!让我们一步步分析您提供的代码,并解释一下运行结果。

### 代码分解:

1. **定义类 `E`**:
   - `__init__` 方法用于初始化,这里接收一个函数 `func` 并将其赋值给 `self.func`。
   - `__del__` 方法在对象被销毁时调用,执行 `self.func(self)`。

2. **定义外部函数 `outter()`**:
   - 在 `outter()` 内部,定义了变量 `x` 并初始化为 `0`。
   - 定义内部函数 `inner(y=None)`,它接收一个参数 `y`,并打印出 `x` 和 `y` 的值。如果 `y` 有值,则将 `y` 赋给 `x`;如果没有值,则返回当前的 `x` 值。

3. **调用 `outter()`**:
   - `f = outter()` 这行代码调用了 `outter()` 函数,返回了 `inner` 函数(并将其赋值给 `f`)。此时并没有执行 `inner`,所以 `x` 仍然是 `0`,而 `y` 仍然是 `None`。

4. **创建 `E` 类的实例**:
   - `e = E(f)` 创建了一个 `E` 类的实例 `e`,将 `f`(即 `inner` 函数)作为参数传入。并且 `self.func` 现在指向了 `inner` 函数。

5. **调用 `f()`**:
   - 这行代码`g = f()` 实际上调用了 `inner()` 函数,此时没有传入参数,所以 `y` 默认值为 `None`。

### 运行结果分析:
当你调用 `f()` 时,执行了 `inner()` 函数,代码会走到以下步骤:
print(f'在函数内部显示x变量:{x}')  # 这里打印 x
print(f'在函数内部显示y变量:{y}')  # 这里打印 y

- **首先,`x` 的值是 `0`** (因为在 `outter()` 中初始化为 `0`)。
- **其次,`y` 的值是 `None`**(因为调用时没有给 `y` 赋值)。

最终你会看到输出:
在函数内部显示x变量:0
在函数内部显示y变量:

### 总结:
因此,运行结果是由于在 `f()` 被调用,但没有传入任何参数,导致 `y` 保持了默认的 `None` 值,而 `x` 仍然是函数 `outter` 中的初始值 `0`。

希望这个解释能帮助你完全理解代码的运行过程!如果还有其他问题,请随时问我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-7-28 14:34:28 | 显示全部楼层
FishC 发表于 2024-7-28 14:25
当然可以!让我们一步步分析您提供的代码,并解释一下运行结果。

### 代码分解:

那我是不是可以理解,传入到实例中的inner函数,和 通过 f()语句调用的inner函数是一个函数呢?如何能证明呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-28 14:34:37 | 显示全部楼层
wbzxz 发表于 2024-07-28 14:34
那我是不是可以理解,传入到实例中的inner函数,和 通 ...


是的,你的理解是完全正确的!传入到实例中的 `inner` 函数和通过 `f()` 语句调用的 `inner` 函数实际上是同一个函数。我们可以通过几个方法来证明这一点。

### 方法一:ID 比较
在 Python 中,每个对象都有一个唯一的标识符(ID),可以通过 `id()` 函数来获取。我们可以比较 `f` 和 `e.func` 的 ID 来证明它们指向同一个函数。

下面是如何做的:
print(id(f))         # 这将打印出 f 的 ID
print(id(e.func))    # 这将打印出 e.func 的 ID
print(id(f) == id(e.func))  # 比较它们的 ID,结果应该是 True

如果输出结果为 `True`,这说明 `f` 和 `e.func` 确实是同一个函数。

### 方法二:函数属性
你还可以检查 `f` 和 `e.func` 的 `__name__` 属性,它们应该都是 "inner":
print(f.__name__)        # 输出 'inner'
print(e.func.__name__)   # 输出 'inner'

如果两个输出都是 `'inner'`,那就证明了它们是同一个函数。

### 方法三:调用效果
你还可以直接调用它们并看是否有相同的效果。例如:
# 调用 f()
f()

# 调用 e.func()
e.func()  # 这也是在调用 inner 函数

如果两次的输出是一致的,那也能说明它们是同一个函数。

### 总结
通过上述的方法,你可以轻松地确认 `f` 和 `e.func` 是同一个函数。这也恰好体现了 Python 中的第一类函数(first-class functions)特性:函数可以作为参数传递,赋值给变量或存储在数据结构中,依然保持它们的身份和属性。希望这些方法能帮助你更深入地理解这个概念!如果还有其他问题,请随时问我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-7-28 14:43:03 | 显示全部楼层
如果我在上面的代码中添加了下面的语句,请问程序会报错吗?请问这语句是什么意思呢?
f
e.func
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-28 14:43:15 | 显示全部楼层
wbzxz 发表于 2024-07-28 14:43
如果我在上面的代码中添加了下面的语句,请问程序会报 ...


在 Python 中,表达式 `f` 和 `e.func` 可以被认为是对这两个对象的引用,它们本身不会导致报错。让我们深入理解一下这些语句的含义。

### 1. `f`
- `f` 是一个指向 `inner` 函数的引用(因为你在调用 `outter()` 函数时返回了 `inner`)。
- 当你单独写下 `f`,Python 会把它思考为一个表达式,但没有输出或使用这个值。
- 如果在交互式环境中(比如 Python 的命令行或 Jupyter Notebook),直接写出 `f` 会返回该函数对象本身,并显示它的字面值(如 `<function outter.<locals>.inner at 0x...>`),但在普通脚本中,这种写法没有实际效果,也不会报错。

### 2. `e.func`
- `e.func` 是 `E` 类实例 `e` 中保存的 `inner` 函数的引用。
- 同样,单独写下 `e.func` 也是一种对函数对象的引用,不会产生任何错误。在交互环境下,这种写法也会返回函数对象并显示它的字面值。

### 总结
因此,当你将这两条语句放在代码中,它们本身不会引起报错,只是简单地引用了对应的函数对象。如果在一个脚本中,单独提到这些变量而不进行其他操作(例如调用、打印等),也不会有任何输出表现。只有在交互式解释器、Notebook等环境中,将其写出来,才能看到它们的表示形式。

如果你希望看到具体的输出,可以考虑用 `print(f)` 和 `print(e.func)` 来查看它们的内容。这样就能看到这两个函数对象的信息,而不会引起错误。希望这能帮助你更好地理解!如果还有其他问题,请随时告诉我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-23 00:42

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表