python:如何从类内部引用类(如递归函数)

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

1
2
3
4
5
6
def f(i):
  if i<0: return
  print i
  f(i-1)

f(10)

然而,有办法做下面的事情吗?

1
2
3
4
class A:
  # do something
  some_func(A)
  # ...


在Python中,您不能在类体中引用类,但在Ruby之类的语言中,您可以这样做。

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


您无法使用所描述的特定语法,这是由于它们被计算的时间不同造成的。给出的示例函数能够工作的原因是,在函数体中调用f(i-1)是因为直到实际调用函数时才执行f的名称解析。此时f存在于执行范围内,因为函数已经被求值了。在类示例的情况下,在仍然计算类定义的过程中查找对类名的引用。因此,它还不存在于本地范围中。

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

1
2
3
4
5
6
7
8
9
class MetaA(type):

    def __init__(cls):
        some_func(cls)

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

使用这种方法,您可以在计算类时对类对象执行任意操作。


如果你想引用相同的对象,只需使用"self":

1
2
3
class A:
    def some_func(self):
        another_func(self)

如果你想创建一个相同类的新对象,就这样做:

1
2
3
class A:
    def some_func(self):
        foo = A()

如果您想访问元类类对象(很可能不是您想要的),同样,只需执行以下操作:

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


如果你只是想做一件不寻常的事,那就去做

1
2
3
class A(object):
    ...
some_func(A)

如果您想做更复杂的事情,可以使用元类。元类负责在类对象完全创建之前操作它。模板应该是:

1
2
3
4
5
6
7
8
9
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#定制类创建。


不。它在函数中工作,因为函数内容是在调用时执行的。但是类内容是在定义时间执行的,此时类还不存在。

这通常不是一个问题,因为你可以在定义了类之后再入侵更多的成员,所以你可以把一个类定义分成多个部分:

1
2
3
4
5
6
7
8
9
10
class A(object):
    spam= 1

some_func(A)

A.eggs= 2

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

然而,在类的定义中间调用函数是非常不寻常的。不清楚您想要做什么,但是很可能您最好使用decorator(或者相对较新的类decorator)。


在类范围内没有办法做到这一点,除非首先将a定义为其他东西(然后some_func(a)将执行与预期完全不同的操作)

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

1
2
3
4
5
class A:
    # do something
    pass

some_func(A)

也就是说,在创建完成后,在A上运行some_func。另外,如果想以某种方式修改类a,可以使用类修饰器(在2.6中添加了它的语法)或元类。你能阐明你的用例吗?


如果目标是用类作为参数调用函数some_func,那么一种方法是将some_func声明为类装饰器。注意,类装饰器是在类初始化之后调用的。它将传递作为参数修饰的类。

1
2
3
4
5
6
7
8
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,你必须从装饰器返回一个函数:

1
2
3
4
5
6
7
8
9
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

类修饰符可以被组合,这导致调用它们的顺序与声明它们的顺序相反:

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

其结果是:

1
2
# Hello C and goodbye!
# The answer is 42

如果类实际上在作用域中,那么可以在类的主体中引用类的名称(就像在方法定义中一样)。如果是在顶层定义的。(在其他情况下可能不会,因为Python的作用域的怪癖!)

要说明作用域的问题,请尝试实例化Foo:

1
2
3
4
5
6
7
class Foo(object):
    class Bar(object):
        def __init__(self):
            self.baz = Bar.baz
        baz = 15
    def __init__(self):
        self.bar = Foo.Bar()

(它会抱怨没有定义全局名称"Bar"。)

另外,有件事告诉我,您可能想研究类方法:关于classmethod函数的docs(用作装饰器),这是一个相关的SO问题。编辑:好的,所以这个建议可能根本不合适……我在阅读你的问题时首先想到的是替代构造函数等。如果有一些更简单的东西适合你的需要,那就避开奇怪的东西。:-)


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

你的代码样本可以简单地写成:

1
2
3
class A(object):
    pass
some_func(A)

类中的大多数代码都在方法定义中,在这种情况下,您可以简单地使用名称A