Python中类内函数的前向声明

3 投票
3 回答
3850 浏览
提问于 2025-04-16 13:07

我第一次接触Python,现在遇到了一些问题:

class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

def foo(string):
    return string

在这个时候,我加载了上面的文件(叫做 classes),然后出现了这样的情况:

$ python
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from classes import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "classes.py", line 5, in <module>
    class B(A):
  File "classes.py", line 6, in B
    b = foo("boo")
NameError: name 'foo' is not defined

注意错误出现在类B中,那里直接调用了 foo,而不是从 __init__ 中调用。还有,我还没有实例化类 B

第一个问题:

  • 为什么会返回错误?我并没有实例化一个类。

接下来。这个“问题”通过把 foo() 的定义往上移动几行来解决:

def foo(string):
    return string

class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

现在我可以这样做:

>>> x = B()
>>> x.b
'boo'

但我不能这样做:

>>> y = A()
>>> y.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'a'

进一步的问题:

  • 我对 __init__ 的理解有什么不对的地方?

我觉得这和前向声明不太一样,所以我希望这个问题不是重复的。

顺便说一下,我的目标是实现一个DSL,但这主要是为了让我自己学习Python。

3 个回答

1

你可能没有理解的是,__init__ 这个方法只有在你创建类 A 的实例时才会被调用。因为在你的代码中并没有实际创建这个类的实例,所以它的初始化方法就没有被调用,自然也就不会遇到前向声明的问题。

一般来说,前向声明在 Python 中并不是个大问题,因为引用只有在你调用相关函数时才会被评估。这里的问题是,类的定义是在定义时就被执行的。具体来说,当 Python 解释器遇到 class 语句时,会发生以下几件事:

  • 它会定义一个新的命名空间(就像一个局部变量的字典)来存放所有的代码。
  • 它会执行类定义中的代码。
  • 它会把存储在局部命名空间中的内容放入类的 __dict__ 中。
  • 它会关闭这个新的命名空间,并将新类添加到模块的命名空间中。

当然,你不需要了解这些细节就能定义一个类;你只需要记住,类定义中的任何内容在类第一次定义时都会被执行!(这通常不是个问题,因为类级别的变量并不常见,通常也不需要在定义时动态设置。)

这个问题在函数定义中也会出现:当你执行

def foo(x=bar()): pass

时,bar 只会在 foo 被定义时被调用一次,之后就不会再调用了。


试着运行以下代码,看看是否能帮助你理解:

class A():
    def __init__(self):
        self.a = foo()

a = A()

def foo():
    pass
1
class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

a 是一个实例属性 - 每个 A 类的对象都有自己的 a 变量,而 foo("baa") 只有在你创建 A 对象的时候才会被计算。

b 是一个类属性 - 每个 B 类的对象共享同一个 b 的副本,而 foo("boo") 在类定义的时候就会被计算。

一般来说,你可以引用那些还不存在的函数和类,只要在你实际调用这个函数之前,它们被定义好了。

6

首先,在类 B 中,函数 foo() 是在被声明之前就被调用了。而类 A 没有这个问题,因为 foo() 只有在类被实例化后才会被调用,也就是在函数 foo 定义之后。

关于你的第二个问题,y.a 是不行的,因为你没有写 self.a = foo('stirng')a = foo('stirng') 只是创建了一个变量 a,这个变量只在 __ init __ 的范围内有效。

一个理想的 __ init __ 函数会把变量分配给实例 self

撰写回答