【问题标题】:How to refer to the class from within it (like a recursive function)如何从其中引用类(如递归函数)
【发布时间】:2018-02-03 19:24:44
【问题描述】:

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

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

f(10)

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

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

【问题讨论】:

  • 不清楚你想要什么或意味着什么。 class A: 不创建对象,所以没有 A 可以参考。查看提供的解决方案,了解您可能的意思...
  • @Andrew class A: 创建一个 class 对象,因为 python 中的类是第一类

标签: python class


【解决方案1】:

如果我正确理解您的问题,您应该能够通过将类型注释放在引号中来引用 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

【讨论】:

  • 您所描述的内容仅对制作类型注释有用 - 很可能不是 OP所询问的内容......
【解决方案2】:

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

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

【讨论】:

  • 为什么?在这方面,是什么让类定义体与函数体不同?鉴于函数和类都是一流的对象,它们主体的差异对我来说并不明显。编辑:正如下面的另一个答案指出的那样,函数体在定义本身被执行时执行,而类体作为类定义执行的一部分被执行。
  • 如何使用类装饰器和元类来实现这一点?举个例子就好了。
  • @ProQ:这里是one example。这是another。如果您搜索它们,我相信还有很多其他人。
【解决方案3】:

由于评估它们的时间,您无法使用您描述的特定语法。给出的示例函数起作用的原因是在函数体内对 f(i-1) 的调用是因为 f 的名称解析直到函数被执行才执行实际调用。此时 f 存在于执行范围内,因为该函数已被评估。在类示例的情况下,在类定义仍在评估期间查找对类名的引用。因此,它还不存在于本地范围内。

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

class MetaA(type):

    def __init__(cls):
        some_func(cls)

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

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

【讨论】:

  • 这是解释为什么它不能完成的唯一答案(函数体不仅仅在定义时执行,而类体是)。感谢您的澄清。
【解决方案4】:

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

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

@classmethod
    def my_class_method(cls):
       return None

然后通过使用调用:

x = __class__.my_class_method()

【讨论】:

    【解决方案5】:

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

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

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

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

    如果您想访问 metaclass 类对象(很可能不是您想要的),再次,只需这样做:

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

    【讨论】:

    • 只是澄清一下:在你的最后一个例子中,引用“A”被称为“类对象”,而不是“元类”,这是一个完全不同的概念。在您的示例中,A 的元类将是 types.ClassType(如果使用新型类或 python3,则为 type
    • 虽然这是大多数实际问题的解决方案,但从技术上讲,它不是类主体,而是方法主体,这在导入时很重要。
    【解决方案6】:

    请记住,在 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

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

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

    【讨论】:

      【解决方案7】:

      似乎这里的大多数答案都已过时。来自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
      

      【讨论】:

        【解决方案8】:

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

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

        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)可能会更好。

        【讨论】:

          【解决方案9】:

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

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

          class A:
              # do something
              pass
          
          some_func(A)
          

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

          【讨论】:

            【解决方案10】:

            如果你只想做点小技巧

            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

            【讨论】:

              【解决方案11】:

              如果目标是以类作为参数调用函数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
              

              【讨论】:

                【解决方案12】:

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

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

                class A(object):
                    pass
                some_func(A)
                

                【讨论】:

                  【解决方案13】:

                  如果它实际上在范围内,则可以在其主体内引用类的名称(如在方法定义内)......如果它是在顶层定义的,它将是什么。 (在其他情况下可能不会,因为 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 怪事。 :-)

                  【讨论】:

                    【解决方案14】:

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

                    【讨论】:

                    • -1 他非常明确地询问如何在类的主体中调用 A,而不是从它的方法中调用。
                    猜你喜欢
                    • 2020-10-11
                    • 2020-01-09
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-06
                    • 2016-08-17
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多