我想知道 Python 的类中到底有没有建立作用域?

2017-11-06 00:58:56 +08:00
 SimbaPeng
以下是 py3 的代码:

class A:
....a = 3
....def test():
........print(a)


A.test()

输出:name 'a' is not defined

这个结果来看类中貌似是没有作用域的。

class A:
....a = 3
....print(a)

输出:3
但是这个例子又能正常打印出本地变量 3,说明类中还是有局部作用域的?
3948 次点击
所在节点    Python
29 条回复
wevsty
2017-11-06 01:11:58 +08:00
test 函数里应该使用 print(self.a)。
Trim21
2017-11-06 01:44:29 +08:00
http://python.jobbole.com/81367/
有是有,但是我没看懂类定义这里到底是怎么算得…
takanasi
2017-11-06 01:59:18 +08:00
从来没想过直接访问类属性,又学到了无用的知识
SimbaPeng
2017-11-06 02:06:34 +08:00
@wevsty 这个我知道,但是如果类中存在作用域的话,按照 LEGB 是可以访问到 a 的
zhy0216
2017-11-06 02:27:48 +08:00
感觉是个 bug ???
zhy0216
2017-11-06 02:41:06 +08:00
这里有答案: https://www.python.org/dev/peps/pep-0227/#specification
Names in class scope are not accessible

不过我还没理解为什么这么设计?

可能是这样 如果允许你这样写, 我们假设还有一个 global var: a,
再运行 del A.a, 那这时候 test 的 a 是不是会指向 global 的 a
那就成了 dynamic scope 了...

可能是你这样写, python 无法再编译后就确定其 scope 必须在运行时才知道
lrxiao
2017-11-06 03:52:57 +08:00
因为
class A:
a = 3
print(3)

相当于
def func_A():
a = 3 # 3 in func_A().__code__.co_consts, a in func_A().__code__.co_names
print(3)

A = builtin.__build_class__(func_A, 'A') # 转移__code__中的 attribute
lrxiao
2017-11-06 03:55:26 +08:00
嘛 缩进全砸了。。这个东西和作用域没啥关系
binux
2017-11-06 05:07:25 +08:00
我的理解是,类中的作用域是声明时的作用域,运行时不存在
greenmoon55
2017-11-06 08:06:55 +08:00
mec
2017-11-06 09:41:33 +08:00
方法没传入 cls 或者 self 啊
ifkite
2017-11-06 09:55:23 +08:00
有作用域。可以改成这样。
class A:
....a = 3
....def test():
........print(a)
....test()

你的代码调方式不对,你的代码调用方式是类的方法的调用,需要函数需要用 staticmethod 或 classmethod 装饰。
类中定义的方法 要么 staticmethod,要么 classmethod,要么定义实例的方法。
SimbaPeng
2017-11-06 10:01:16 +08:00
@ifkite 你这样写一样报 a 变量不存在的 error,在 py3 中不需要用 staticmethod 或 classmethod 装饰也能 A.test()这样调用的
SimbaPeng
2017-11-06 10:03:40 +08:00
@mec py3 中不传 cls,self 一样可以调用,这不是报错原因
zhengxiaowai
2017-11-06 10:41:55 +08:00
Python 中变量可以分为:局部变量、全局变量、自有变量。

Python 是有作用域 全局的 global() 和 本地的 locals() 可以得到。

你用一个类举例子是不正确的。

class A:
....a = 3
....def test():
........print(a)

A.test()

这个栗子肯定是有问题的。。。必然报错,我也无法重现这个错误。错也不是你那个错误,而是

TypeError: unbound method test() must be called with A instance as first argument (got nothing instead)

很明显 test() 是一个未绑定方法,具体什么是未绑定,可以参考 Fluent Python 中的相关章节。(具体哪一个忘记了。。)

这个 Class A 中 a 叫做类属性,然而在 Python 类中要获得属性需要用 __new__ 返回的值,也就是 self.a 来获取。
这就是为什么或有方法第一个参数都是 self 了。

class A:
....a = 3
....print(a)

这个例子能够输出是应为 print() 执行时在构建类时候,也就是在 __new__ 中执行。这时候 a 还是一个普通变量,不是一个属性。

假设你的描述的都是正确的,那么就可以总结来说,第一个和第二个的差别在于执行的时期不同。当类生成以后,a 就变成类属性 需要通过 A.a 或者 self.a 来调用
ipwx
2017-11-06 10:47:58 +08:00
请使用 Python 的范式来写 Python 的程序,不要把 C++/Java 世界的 style 拿到 Python 来用。

Python 访问对象属性的方法只有一种,obj.XXX 。所以成员函数需要传入 self,然后用 self.XXX
lrxiao
2017-11-06 10:57:56 +08:00
讲 unbound 的都是 py2 选手。。
py3 除非你要 instance 访问才需要 @staticmethod
打印 3 是创建时的事情 (发现我上面写的疯狂笔误。。)
SimbaPeng
2017-11-06 11:02:46 +08:00
@zhengxiaowai
首先我这个例子在 py3.6 中调用是肯定不会报你说的那个错误,你可以自己去试试。
另外:
class A:
.... a = 3
....print(locals())

输出:{'__module__': '__main__', '__qualname__': 'A', 'a': 3}
说明在 class 定义时候是有局部作用域产生的.

如果说执行时间不同,那么这样写应该可以找到 a 吧:
class A:
....a = 3
....def test():
........print(a)
....test()

输出: NameError: name 'a' is not defined

一样是找不到 a 变量,我估计是类的作用域和类中的方法不是嵌套关系
lrxiao
2017-11-06 11:08:59 +08:00
这个 a 只是个 local variable 改下 @ifkite 的类

import builtins
import sys


old_bc = builtins.__build_class__

def fake_build_class(cls_func, cls_name):
....print("Build class...")
....print(cls_func.__code__.co_consts)
....print(cls_func.__code__.co_names)
....print(cls_name)
....print("Create class...")
....cls = old_bc(cls_func, cls_name)
....print("End of create class...")
....print(cls.__dict__['a'])

builtins.__build_class__ = fake_build_class

class A:
....a = 3
....def test():
........frame = sys._getframe(1)
........print(frame.f_locals['a'])
....test()

嗯 极其丧病
hcnhcn012
2017-11-06 12:24:26 +08:00
好问题啊。。。我到现在都没搞清楚和模块级作用域的区别

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/403797

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX