【问题标题】:Does Python have class prototypes (or forward declarations)?Python 有类原型(或前向声明)吗?
【发布时间】:2010-10-06 04:47:29
【问题描述】:

我在一个文件中有一系列 Python 类。一些类引用其他类。

我的代码是这样的:

class A():
    pass

class B():
    c = C()

class C():
    pass

尝试运行它,我得到NameError: name 'C' is not defined。很公平,但有什么办法让它工作,还是我必须手动重新排序我的课程以适应?在 C++ 中,我可以创建一个类原型。 Python 有没有等价物?

(我实际上是在玩 Django 模型,但我尽量不让事情复杂化)。

【问题讨论】:

  • 它在 Kernighan 和 Ritchie 中被称为函数原型,我记得是从那里开始的。
  • 刚刚检查过,我的 K&R 副本中没有“类原型”;)
  • 是的,这有点令人困惑,因为 OOP 中的原型概念和函数式编码完全不相关。 o_O
  • 这个问题在类型标签中出现的比较多(方法的返回类型需要提前声明)。看起来阻力最小的路径是自下而上定义类型,即使这样的安排不是最可读布局。

标签: python class oop prototype


【解决方案1】:

其实,以上都是对 Python 的很好的观察,但都不能解决你的问题。

Django 需要自省。

正确做你想做的事的方法如下:

class Car(models.Model):
    manufacturer = models.ForeignKey('Manufacturer')
    # ...

class Manufacturer(models.Model):
    # ...

注意使用类名作为 string 而不是文字类引用。 Django 提供了这种替代方法来解决 Python 不提供前向声明的问题。

这个问题让我想起了经典的支持问题,您应该始终向任何有问题的客户询问:“您真正想做什么?”

【讨论】:

  • 从问题中可以清楚地看出 OP 试图做什么,它与 Django 相关,并且 OP 试图理解 Python 的面向对象编程模型,因为 Django 的 ORM 可能会令人困惑适合初学者。
  • 这可能是真的,但 OP 最终提出的问题与问题的标题大不相同。三年后我不愿意更改标题,即使它不准确。
【解决方案2】:

在 Python 中,您本身并不创建原型,但您确实需要了解“类属性”和实例级属性之间的区别。在上面显示的示例中,您在 B 类上声明了一个类属性,而不是实例级属性。

这就是你要找的:

class B():
    def __init__(self):
        self.c = C()

【讨论】:

  • 我很想知道为什么 Python 在分配实例属性而不是类级别时可以找到 C 的定义。是不是因为它试图在类定义而不是运行时进行赋值?
  • 是的,因为 c=C() 在类定义中(在模块加载时执行),所以 C 类还不存在。
  • @truppo 是正确的。当您声明类属性时,相应的引用会在加载模块时解析(即解释类)。 init 方法类似于其他语言中的构造函数,因此在其本地范围内的引用在调用之前不必解析
  • 有趣的答案,我现在有更好的搜索词来理解类和实例级属性。我实际上是在玩 Django 模型,所以我不确定类和实例级属性究竟会如何影响它。
  • 这里有很好的建议。我要补充的唯一另一件事是,如果您在尝试定义 ForeignKey 时实际上遇到了这个问题,您可以简单地将类名作为字符串传递,Django 会解决它。跨度>
【解决方案3】:

这将解决您提出的问题(但我认为您确实在寻找 jholloway7 响应的实例属性):

class A:
    pass

class B:
    pass

class C:
    pass

B.c = C()

【讨论】:

  • 对于 Django 模型,这是唯一有效的答案,因为我们试图修改 Class 本身,而不是 Class 的实例。 Django 内省类并使用该元数据来驱动它的 ORM 层。
  • 我收回我的评论——这是一种无需前向 decl 就可以将类成员添加到 Python 类的方法。但它确实实际上解决了 Django 模型声明的问题,因为 Django 处理这些模型的方式内部存在某些东西(?)。
【解决方案4】:

Python 没有原型或 Ruby 风格的开放类。但是如果你真的需要它们,你可以编写一个重载 new 的元类,以便它在当前命名空间中查找该类是否已经存在,如果它确实返回现有类型对象而不是而不是创造一个新的。我在不久前写的 ORM 上做了类似的事情,效果很好。

【讨论】:

  • 答案是否定的,但很奇怪,你能举例说明你的解决方案吗?当 a 依赖于 b 时, b 依赖于 c 并且 c 依赖于 a 。
【解决方案5】:

问这个问题十年后,我遇到了同样的问题。虽然人们建议应该在 init 方法中完成引用,但有时您需要在类实际实例化之前将数据作为“类属性”访问。出于这个原因,我想出了一个使用描述符的简单解决方案。

class A():
    pass

class B():
    class D(object):
        def __init__(self):
            self.c = None
        def __get__(self, instance, owner):
            if not self.c:
                self.c = C()
            return self.c
    c = D()

class C():
    pass

>>> B.c
>>> <__main__.C object at 0x10cc385f8>

【讨论】:

    【解决方案6】:

    关于类与实例属性的所有正确答案。但是,出现错误的原因只是定义类的顺序。当然 C 类还没有定义(因为类级代码在导入时立即执行):

    class A():
        pass
    
    class C():
        pass
    
    class B():
        c = C()
    

    会起作用的。

    【讨论】:

    • 我知道我可以这样做来修复它(这也是我现在所做的)但现在我的代码排序不直观。
    • 好吧,我明白你的意思,但我认为尝试使用尚不存在的东西会更不直观。喜欢做:打印一个; a=5
    • 尝试运行一些语句,例如 print a; a=5 显然没有多大意义,但如果 C 风格的类原型可用,则一个类是自包含的并且完全合理地转发引用。
    • 对,好吧,这听起来像是隐含的地狱。在 python 中,事情是直线执行的,作为“类定义代码”没有区别。你试图做的是 exactly 相同: print a; a=5
    猜你喜欢
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    相关资源
    最近更新 更多