【问题标题】:Python forward-declaration of functions inside classes类中函数的 Python 前向声明
【发布时间】:2011-03-06 20:27:39
【问题描述】:

我第一次接触 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__ 有什么我不明白的地方?

我不认为这与forward declaration 相同,因此我希望这个问题不是重复的。

顺便说一句,我的目标是实现 DSL,但这主要是让自己学习 Python 的借口。

【问题讨论】:

    标签: python class-design forward-declaration


    【解决方案1】:

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

    对于第二个问题,y.a 不起作用,因为您没有说self.a = foo('stirng')a = foo('stirng') 只在__ init __ 的范围内创建变量a。

    理想的__ init __ 函数将变量分配给实例self

    【讨论】:

    • 这是一个输入速度非常快的字符串。
    • 感谢您的第二个回答。我一定在某个地方错过了。这也回答了一些关于范围的问题。关于第一个问题:我认为 foo() 只会在 B 被实例化之后被调用。我错了。谢谢!
    【解决方案2】:

    您对__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
    

    【讨论】:

    • 代码没有运行:NameError: global name 'foo' is not defined。但我明白你的意思。
    • @lorenzog:是的,这就是重点:如果您实际尝试初始化类,则会发生与上述相同的事情,因为那时您正在查找尚未定义的名称。跨度>
    【解决方案3】:
    class A:
        def __init__(self):
            a = foo("baa")
    
    class B(A):
        b = foo("boo")
    

    a 是一个实例属性 - 每个 A 都有自己的一个变量,并且 foo("baa") 仅在您创建 A 对象时进行评估。

    b 是一个类属性 - 每个 B 对象共享 b 的相同副本,并且在定义类时评估 foo("boo")。

    一般来说,您可以引用尚不存在的函数和类只要在您评估引用之前定义它们(即在您实际尝试之前)调用函数)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多