cindyber 发表于 2025-1-11 18:14:43

第五十九讲问答题第六题求助

6. 请看下面代码,如果调用 test() 函数,那么打印的值分别是什么呢?
>>> x = 1

>>> def test():

...   x = 2

...   print(x)

...

...   class C:

...         x = 3

...         print(x)

...

...         def m1(self):

...             print(x)

...             print(self.x)

...

...         def m2(self):

...             x = 4

...             print(x)

...             self.x = 5

...

...         def m3(self):

...             print(x)

...             print(self.x)

...

...   c = C()

...   c.m1()

...   c.m2()

...   c.m3()

答案是>>> test()

2

3

2

3

4

2

5
不懂第二个数字到第四个数字为什么是这三个
解析是在类中定义类属性(x = 3),并不会被类方法 m1() 直接通过变量名访问到,所以,在 m1() 中,print(x) 打印的 x 值是 2,print(self.x) 才打印 3(它逻辑上是先去找对象是否有 x 属性,如果没有设置,才去找类属性);但是看不懂,以及怎么和LEGB联系起来

FishC 发表于 2025-1-11 18:14:55

在 Python 中,变量的查找遵循 LEGB 规则:Local、Enclosing、Global、Built-in。这意味着在不同的作用域中,Python 以这个顺序查找变量。

来分析 `test()` 函数执行时每个步骤的变量查找过程:

1. **`print(x)`(函数 `test` 内)**

   - `test` 函数的 `local` 作用域中有 `x=2`,因此输出 `2`。

2. **`print(x)`(类 `C` 中)**

   - `C` 类定义时,`x=3` 是类作用域中的变量。
   - 当类 `C` 被加载时,`print(x)` 查找到的是类作用域中的 `x=3`,因此输出 `3`。

3. **`m1` 方法中的 `print(x)`**

   - 方法 `m1` 中的 `x` 查找遵循 LEGB 规则:
   - Local: `m1` 方法内没有 `x`。
   - Enclosing: `C` 类内没有 `x`,但是 `test` 函数的 `local` 作用域有 `x=2`。
   - 因此,`print(x)` 输出 `2`。

4. **`m1` 方法中的 `print(self.x)`**

   - `self.x` 是访问对象的属性。
   - 因为 `c = C()`,`c` 没有 `x` 的实例变量,所以查找类属性,类 `C` 拥有 `x=3`。
   - 因此,`print(self.x)` 输出 `3`。

5. **`m2` 方法中的 `print(x)`**

   - `m2` 方法内 `x=4` 是局部变量。
   - 因此,`print(x)` 输出 `4`。

6. **`m3` 方法中的 `print(x)`**

   - `m3` 方法与 `m1` 方法类似:
   - Local: `m3` 方法内没有 `x`。
   - Enclosing: `test` 函数的 `local` 作用域中有 `x=2`。
   - 因此,`print(x)` 输出 `2`。

7. **`m3` 方法中的 `print(self.x)`**

   - 在 `m2` 方法中执行 `self.x = 5`,这为对象 `c` 增加了一个实例变量 `x`。
   - 因此,`print(self.x)` 输出 `5`。

希望这个解释能帮助你更好地理解代码运行的过程以及 LEGB 规则是如何应用的!如果有进一步问题,欢迎继续提问!

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

某一个“天” 发表于 2025-1-11 19:02:57

LEGB规则是Python中变量查找的顺序规则,用于确定在何处查找变量名。LEGB规则的各个字母代表不同的作用域层次:

;Local(L)‌:局部作用域,指函数内部定义的变量。局部作用域是最近的查找层次,只有在函数内部才能访问到这些变量‌

Enclosing(E)‌:嵌套作用域,指外部嵌套函数中的变量。如果局部作用域中没有找到变量,解释器会继续在外部嵌套函数的局部作用域中查找‌

Global(G)‌:全局作用域,指模块层级定义的变量。如果前两个作用域中没有找到变量,解释器会在模块层级的作用域中查找‌

Builtin(B)‌:内置作用域,指Python内置的名称空间。如果前三个作用域中没有找到变量,解释器会在Python的内置名称空间中查找‌

第二个数:C类定义时,x=3是类作用域中的变量。
   - 当类 C被加载时,print(x) 查找到的是类作用域中的 x=3,因此输出 3。
   也就是一被加载就输出了
第三个数:m1方法中的 print(x)
   - 方法 `m1` 中的 `x` 查找遵循 LEGB 规则:
   - Local: `m1` 方法内没有 `x`。
   - Enclosing: `C` 类内没有 `x`,但是 `test` 函数的 `local` 作用域有 `x=2`。
   - 因此,`print(x)` 输出 `2`。
   print(x) 打印的 x 值是 2,print(self.x) 才打印 3。因为x只是一个变量,不是属性
第四个数:
    加了self就是属性
- `self.x` 是访问对象的属性。
   - 因为 `c = C()`,`c` 没有 `x` 的实例变量,所以查找类属性,类 `C` 拥有 `x=3`。
   - 因此,`print(self.x)` 输出 `3`。
   (它逻辑上是先去找对象是否有 x 属性,如果没有设置,才去找类属性)

cindyber 发表于 2025-1-11 22:03:40

某一个“天” 发表于 2025-1-11 19:02
LEGB规则是Python中变量查找的顺序规则,用于确定在何处查找变量名。LEGB规则的各个字母代表不同的作用域层 ...

第三个数:m1方法中的 print(x)
   - 方法 `m1` 中的 `x` 查找遵循 LEGB 规则:
   - Local: `m1` 方法内没有 `x`。
   - Enclosing: `C` 类内没有 `x`,但是 `test` 函数的 `local` 作用域有 `x=2`。
   - 因此,`print(x)` 输出 `2`。

这里Enclosing说C类没有x,是说class C下的定义是属性x,不是变量x么,
当类 C被加载时,print(x) 查找到的是类作用域中的 x=3,因此输出 3。
那为什么这里还是可以被Print呢?

cindyber 发表于 2025-1-11 22:04:18

FishC 发表于 2025-1-11 18:14
在 Python 中,变量的查找遵循 LEGB 规则:Local、Enclosing、Global、Built-in。这意味着在不同的作用域中 ...

第三个数:m1方法中的 print(x)
   - 方法 `m1` 中的 `x` 查找遵循 LEGB 规则:
   - Local: `m1` 方法内没有 `x`。
   - Enclosing: `C` 类内没有 `x`,但是 `test` 函数的 `local` 作用域有 `x=2`。
   - 因此,`print(x)` 输出 `2`。


这里Enclosing说C类没有x,是说class C下的定义是属性x,不是变量x么,
当类 C被加载时,print(x) 查找到的是类作用域中的 x=3,因此输出 3。

那为什么这里还是可以被Print呢?

FishC 发表于 2025-1-11 22:04:26

cindyber 发表于 2025-01-11 22:04
FishC 发表于 2025-1-11 18:14
在 Python 中,变量的 ...

在 Python 中,类定义时的类变量(如 `x=3`)可以被视作类属性,而不是函数内的局部变量。这是因为类的定义本身提供一个新的命名空间。

### 关于 `C` 类中 `print(x)` 输出 `3` 的原因:

- 当类 `C` 被定义时,`x=3` 就在类的命名空间内被绑定。因此在 `C` 的类体内,可以直接访问 `x`,输出为 `3`。

### 关于 `m1` 方法中 `print(x)` 输出 `2` 的原因:

- 对于 `m1` 方法,变量 `x` 的查找遵循 LEGB 规则:
- **Local**: `m1` 内部没有定义 `x`。
- **Enclosing**: `C` 类中定义的 `x` 作为类属性,而不是方法的局部变量,所以这里不被视作 Enclosing 作用域适用。
- **Global**: `test` 函数中的局部变量 `x=2` 由于是 enclosing function 的变量,所以被应用到 `m1`。
- 因此,`print(x)` 输出 `2`,是来自 `test()` 的函数局部变量。

在类的定义体内(比如直接在类中,不是在方法内)可以直接访问和打印类属性,就像在 `C` 类的定义时直接 `print(x)` 会输出 `3`。而在方法内部,并没有直接被访问到(比如 `m1`),LEGB 的规则更为细致,方法内的 `self.x` 用来访问实例属性或类属性。

希望这些解释能够澄清你的疑惑!如果有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 第五十九讲问答题第六题求助