python中的自引用类定义

时间:2009-06-19 22:14:40

标签: python

有没有办法从类声明中引用类名?一个例子如下:

class Plan(SiloBase):
    cost = DataField(int)
    start = DataField(System.DateTime)
    name = DataField(str)
    items = DataCollection(int)
    subPlan = ReferenceField(Plan)

我有一个读取此信息的元类并进行一些设置,基类实现了一些常见的保存。我希望能够创建这样的递归定义,但到目前为止,在我的实验中,我无法获得我想要的效果,通常会遇到“计划未定义”错误。我理解发生了什么,类的名称不在类的范围内。

6 个答案:

答案 0 :(得分:23)

试试这个:

class Plan(SiloBase):
    cost = DataField(int)
    start = DataField(System.DateTime)
    name = DataField(str)
    items = DataCollection(int)

Plan.subPlan = ReferenceField(Plan)

或者像这样使用__new__

class Plan(SiloBase):

    def __new__(cls, *args, **kwargs):
        cls.cost = DataField(int)
        cls.start = DataField(System.DateTime)
        cls.name = DataField(str)
        cls.items = DataCollection(int)
        cls.subPlan = ReferenceField(cls)
        return object.__new__(cls, *args, **kwargs)

答案 1 :(得分:8)

  

我有一个读取此信息的元类并进行一些设置

大多数使用元类的框架都提供了解决此问题的方法。例如,Django

subplan = ForeignKey('self')

Google App Engine

subplan = SelfReferenceProperty()

稍后使用__new__添加附加属性等解决方案的问题在于,大多数ORM元类都希望在创建类时存在类属性。

答案 2 :(得分:2)

  

我明白发生了什么,   类的名称不在范围内   在课堂内。

不完全是。在定义内容(例如范围)时,类的名称尚未定义

答案 3 :(得分:0)

不,你不能这样做。想想如果你这样做会发生什么:

 OtherPlan = Plan
 other_plan = OtherPlan()

other_plan的实例化时,该类的名称是什么?

无论如何,这种事情最好在__new__方法中完成,该方法需要cls参数来引用该类。

答案 4 :(得分:0)

Sine Python 3.7和PEP 563有一种方法可以实现。

添加导入

from __future__ import annotations

,以下代码将起作用

from __future__ import annotations
from typing import List

class Refer(object):
    def __init__(self, x: Plan):
        self.x: Plan = x

class Plan(object):
    def __init__(self):
        pass
    subPlan: Refer(Plan())

答案 5 :(得分:0)

使用python3中的以下代码可以做到这一点。 我们使用cached_property只评估一次Player.enemyPlayer,然后返回缓存的结果。因为我们的值来自一个函数,所以在第一次加载该类时不会对其进行求值。

class cached_property(object):
    # this caches the result of the function call for fn with no inputs
    # use this as a decorator on function methods that you want converted
    # into cached properties
    def __init__(self, fn):
        self._fn = fn

    def __set_name__(self, owner, name):
        # only works in python >= 3.6
        self.name = name
        self._cache_key = "_" + self.name

    def __get__(self, instance, cls=None):
        if self._cache_key in vars(self):
            return vars(self)[self._cache_key]
        else:
            result = self._fn()
            setattr(self, self._cache_key, result)
            return result


class Player:
    @cached_property
    def enemyPlayer():
        return Player