python:如何从其中引用类(如递归函数)

时间:2010-01-09 23:44:13

标签: python class

对于递归函数,我们可以这样做:

def f(i):
  if i<0: return
  print i
  f(i-1)

f(10)

但有没有办法做以下事情?

class A:
  # do something
  some_func(A)
  # ...

14 个答案:

答案 0 :(得分:18)

在Python中,你不能在类体中引用类,尽管在像Ruby这样的语言中你可以这样做。

在Python中,您可以使用类装饰器,但是在初始化类后将调用它。另一种方法可能是使用元类,但这取决于你想要实现的目标。

答案 1 :(得分:8)

由于评估时间的原因,您无法使用您所描述的特定语法。给出的示例函数的作用是在函数体中调用 f(i-1)是因为在函数为函数之前不执行 f 的名称解析实际上叫。此时 f 存在于执行范围内,因为已经评估了该函数。对于类示例,在仍在评估类定义的过程中会查找对类名的引用。因此,它在本地范围内尚不存在。

或者,可以使用类似的元类来完成所需的行为:

class MetaA(type):

    def __init__(cls):
        some_func(cls)

class A(object):
    __metaclass__=MetaA
  # do something
  # ...

使用此方法,您可以在评估类时对类对象执行任意操作。

答案 2 :(得分:2)

如果你想引用同一个对象,只需使用'self':

class A:
    def some_func(self):
        another_func(self)

如果要创建同一个类的新对象,只需执行以下操作:

class A:
    def some_func(self):
        foo = A()

如果您想要访问元类类对象(很可能不是您想要的),请再次执行:

class A:
    def some_func(self):
        another_func(A) # note that it reads A, not A()

答案 3 :(得分:1)

在类范围内没有办法做到这一点,除非首先将A定义为其他东西(然后some_func(A)会做出与你期望的完全不同的事情)

除非您正在进行某种堆栈检查以向该类添加位,否则您想要这样做似乎很奇怪。为什么不呢:

class A:
    # do something
    pass

some_func(A)

也就是说,在A之后运行some_func。或者,如果你想以某种方式修改类A,你可以使用类装饰器(它的语法在2.6中添加)或元类。你能澄清你的用例吗?

答案 4 :(得分:1)

不。它在函数中起作用,因为函数内容是在调用时执行的。但是类的内容是在define-time执行的,此时该类还不存在。

这通常不是问题,因为您可以在定义后将其他成员入侵到类中,因此您可以将类定义拆分为多个部分:

class A(object):
    spam= 1

some_func(A)

A.eggs= 2

def _A_scramble(self):
    self.spam=self.eggs= 0
A.scramble= _A_scramble

然而,想要在自己的定义中调用类上的函数是非常不寻常的。目前还不清楚你要做什么,但是你可能会更好地使用decorators(或相对较新的class decorators)。

答案 5 :(得分:1)

如果你想做一点hacky的事情

class A(object):
    ...
some_func(A)

如果你想做更复杂的事情,你可以使用元类。元类负责在完全创建类对象之前对其进行操作。模板将是:

class AType(type):
    def __new__(meta, name, bases, dct):
        cls = super(AType, meta).__new__(meta, name, bases, dct)
        some_func(cls)
        return cls

class A(object):
    __metaclass__ = AType
    ...

type是默认的元类。元类的实例是类,因此__new__返回(在这种情况下)A的修改实例。

有关元类的详细信息,请参阅http://docs.python.org/reference/datamodel.html#customizing-class-creation

答案 6 :(得分:1)

请记住,在Python中,类型提示仅用于自动代码完成,因此它可帮助IDE推断类型并在运行时警告用户。在运行时,类型提示几乎从未使用过(在某些情况下除外),因此您可以执行以下操作:

from typing import Any, Optional, NewType

LinkListType = NewType("LinkList", object)


class LinkList:
    value: Any
    _next: LinkListType

    def set_next(self, ll: LinkListType):
        self._next = ll


if __name__ == '__main__':
    r = LinkList()
    r.value = 1
    r.set_next(ll=LinkList())
    print(r.value)

您可以看到IDE成功地推断出它的类型为LinkList

PyCharm type inference in action

注意:由于next可以是None,因此在类型中暗示这一点会更好,我只是不想混淆OP。

class LinkList:
    value: Any
    next: Optional[LinkListType]

答案 7 :(得分:1)

也许您可以尝试致电__class__。 现在,我正在编写一个从同一类中调用类方法的代码。 到目前为止,一切正常。

我正在使用类似以下的方法创建类方法:

@classmethod
    def my_class_method(cls):
       return None

然后使用以下电话进行呼叫:

x = __class__.my_class_method()

答案 8 :(得分:0)

你想要达到什么目的?可以使用元类来访问类以调整其定义,但不建议这样做。

您的代码示例可以简单地编写为:

class A(object):
    pass
some_func(A)

答案 9 :(得分:0)

如果它实际上在范围内,可以引用其体内类的名称(如内部方法定义)...如果它在顶层定义,它将是什么。 (在其他情况下可能不会,因为Python的范围怪癖!)。

为了说明范围问题,请尝试实例化Foo:

class Foo(object):
    class Bar(object):
        def __init__(self):
            self.baz = Bar.baz
        baz = 15
    def __init__(self):
        self.bar = Foo.Bar()

(它会抱怨没有定义全球名称'Bar'。)


另外,有些东西告诉我你可能想要查看类方法:docs on the classmethod function(用作装饰器),a relevant SO question编辑:好的,所以这个建议可能根本不合适......只是我在阅读你的问题时首先想到的是像替代构造函数等等。如果更简单的东西适合你的需要,避开@classmethod怪异。 : - )

答案 10 :(得分:0)

如果目标是使用类作为参数来调用函数some_func,则答案是将some_func声明为类装饰器。请注意,在初始化类之后,将调用类装饰器。它将作为参数修饰的类传递给它。

def some_func(cls):
    # Do something
    print(f"The answer is {cls.x}")
    return cls # Don't forget to return the class

@some_func
class A:
    x = 1

如果要将其他参数传递给some_func,则必须从装饰器返回一个函数:

def some_other_func(prefix, suffix):
    def inner(cls):
        print(f"{prefix} {cls.__name__} {suffix}")
        return cls
    return inner

@some_other_func("Hello", " and goodbye!")
class B:
    x = 2

可以构造类装饰器,这导致它们以相反的顺序被调用:

@some_func
@some_other_func("Hello", "and goodbye!")
class C:
    x = 42

其结果是:

# Hello C and goodbye!
# The answer is 42

答案 11 :(得分:0)

如果我正确理解了您的问题,则可以通过将类型注释放在引号中来引用类A中的类A。这称为前向参考。

class A:
  # do something
  def some_func(self, a: 'A')
  # ...

请参阅下面的参考文件

  1. https://github.com/python/mypy/issues/3661
  2. https://www.youtube.com/watch?v=AJsrxBkV3kc

答案 12 :(得分:0)

这里的大部分答案似乎都过时了。来自python3.7:

from __future__ import annotations

示例:

$ cat rec.py
from __future__ import annotations
class MyList:
    def __init__(self,e):
        self.data = [e]
    def add(self, e):
        self.data.append(e)
        return self
    def score(self, other:MyList):
        return len([e
            for e in self.data
            if e in other.data])
print(MyList(8).add(3).add(4).score(MyList(4).add(9).add(3)))
$ python3.7 rec.py
2

答案 13 :(得分:-1)

类中的大多数代码都在方法定义中,在这种情况下,您只需使用名称A