【问题标题】:How to access outer class from an inner class?如何从内部类访问外部类?
【发布时间】:2011-01-02 17:34:54
【问题描述】:

我有这样的情况......

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.Outer.some_method()    # <-- this is the line in question

如何从Inner 类访问Outer 类的方法?

【问题讨论】:

  • 你为什么要这样做?简单的同伴关系有什么问题?你是不是想“隐藏”什么东西?
  • 这种情况的一个例子可能是有一个带有子类的类,这些子类需要像往常一样访问外部类,但随后需要创建另一个从第一个类。在这种情况下,第二个类的子类将尝试使用self.&lt;original_parent_name&gt; 访问父类并获取原始类,而不是它们作为子类的新类乙>。 我希望阅读本文的人能够想象这个困难的场景并看到这样的问题的重点。
  • 复制到stackoverflow.com/questions/2278426,它有一个很好的答案。

标签: python scope nested inner-classes


【解决方案1】:

也许我很生气,但这似乎确实很容易 - 事情是让你的内部类在外部类的方法中......

def do_sthg(self):
    ...

def messAround(self):

    outerClassSelf = self

    class Mooble():
        def do_sthg_different(self):
            ...
            outerClassSelf.do_sthg()

另外...“self”只是按照惯例使用,所以你可以这样做:

def do_sthg(self):
    ...

def messAround(outerClassSelf):

    class Mooble():
        def do_sthg_different(self):
            ...
            outerClassSelf.do_sthg()

可能有人反对说你不能从外部类之外创建这个内部类......但这不是真的:

class Bumblebee():

    def do_sthg(self):
        print "sthg"
    
    def giveMeAnInnerClass(outerClassSelf):

        class Mooble():
            def do_sthg_different(self):
                print "something diff\n"
                outerClassSelf.do_sthg()
        return Mooble
    

然后,在几英里外的某个地方:

blob = Bumblebee().giveMeAnInnerClass()()
blob.do_sthg_different()    

甚至将船推开一点并扩展这个内部类(注意要让super() 工作,您必须将Mooble 的类签名更改为class Mooble(object))。

class InnerBumblebeeWithAddedBounce(Bumblebee().giveMeAnInnerClass()):
    def bounce(self):
        print "bounce"
    
    def do_sthg_different(self):
        super(InnerBumblebeeWithAddedBounce, self).do_sthg_different()
        print "and more different"
    

ibwab = InnerBumblebeeWithAddedBounce()    
ibwab.bounce()
ibwab.do_sthg_different()

稍后

mrh1997 提出了一个有趣的观点,即使用这种技术传递的内部类的非公共继承。但似乎解决方案非常简单:

class Fatty():
    def do_sthg(self):
        pass
    
    class InnerFatty(object):
        pass
            
    def giveMeAnInnerFattyClass(self):
        class ExtendedInnerFatty(Fatty.InnerFatty):
            pass
        return ExtendedInnerFatty
                
fatty1 = Fatty()
fatty2 = Fatty()

innerFattyClass1 = fatty1.giveMeAnInnerFattyClass()
innerFattyClass2 = fatty2.giveMeAnInnerFattyClass()

print (issubclass(innerFattyClass1, Fatty.InnerFatty))
print (issubclass(innerFattyClass2, Fatty.InnerFatty))

【讨论】:

  • 这对我有用。这个结构到底叫什么?工厂功能?关闭?
  • 我不知道它叫什么......但我可能会建议其他海报没有看到这个的原因是因为它可能没有完全理解 Python 中的大多数东西是非神圣的,包括“自我”(任意名称)和类 - 它们是“第一类对象”,这似乎意味着您可以以非常离谱的方式操纵它们
  • 伟大的工作。受到这个解决方案想法和一些思考的启发,我扩展了这个答案的部分内容,在下面创建了另一个答案,并提供了更多解释。
  • @mikerodent 因为对于您的评论(关于我的回答),我现在已经编辑了我的回答以删除“社区 wiki”描述。和你一样,我对“社区维基”的答案一无所知,所以你纠正了你的错误是件好事。
  • 请记住,每次调用 Bumblebee().giveMeAnInnerClass() 时,此解决方案都会为内部类创建一个新类型对象。这在检查继承时尤其是一个问题: ` class A(Bumblebee().giveMeAnInnerClass()): pass issubclass(A, Bumblebee().giveMeAnInnerClass() => False
【解决方案2】:

我们可以做的是将Outer类的self变量作为类参数传入Inner Class,并在Outer init下初始化Inner Class,将Outer self传入Inner

class Outer:
    def __init__(self):
        self.somevalue=91
        self.Inner=self.Inner(self)
    def SomeMethod(self):
        print('This is Something from Outer Class')

    class Inner:
        def __init__(self,Outer)
            self.SomeMethod=Outer.SomeMethod
            self.somevalue=Outer.somevalue
    
        def SomeAnotherMethod(self):
            print(self.somevalue)
            self.SomeMethod()        

>>>f=Outer()
>>>f.Inner.SomeAnotherMethod() 
91
This is Something from Outer Class

现在运行这个函数后它可以工作了

【讨论】:

    【解决方案3】:

    你可以创建一个类来装饰内部类。在这种情况下@inner。

    因为这是一个装饰器:Outer.A = inner(Outer.A)。一旦您的代码需要 Outer.A,它将执行 inner.__get__ 方法,该方法返回原始类 (A),并在其上设置了新属性:A.owner = Outer。

    类 A 中的类方法,在本例中为 def add(cls, y=3),可以在 return cls.owner.x + y + 1 处使用新属性 owner

    setattr(owner, name, self.inner) 行破坏了描述符,因为 owner.name =&gt; Outer.A =&gt; A 不再是类内部的实例。

    希望这会有所帮助。

        class inner:
        
            def __init__(self, inner):
                self.inner = inner
        
            def __get__(self, instance, owner):
                print('__get__ method executed, only once... ')
                name = self.inner.__name__
                setattr(self.inner, 'owner', owner) 
                setattr(owner, name, self.inner) # breaks descriptor
                return self.inner #returns Inner
        
        class Outer:
            x = 1
        
            @inner
            class A:
        
                @classmethod
                def add(cls, y=3):
                    return cls.owner.x + y + 1
        
        print(Outer.A.add(0)) # First time executes inner.__get__ method
        print(Outer.A.add(0)) # Second time not necessary.
    
        >> __get__ method executed, only once... 
        >> 2
        >> 2
    
    

    【讨论】:

      【解决方案4】:

      太简单了:

      输入:

      class A:
          def __init__(self):
              pass
      
          def func1(self):
              print('class A func1')
      
          class B:
              def __init__(self):
                  a1 = A()
                  a1.func1()
      
              def func1(self):
                  print('class B func1')
      
      b = A.B()
      b.func1()
      

      输出

      A 类 func1

      B类功能1

      【讨论】:

      • 嗯,注意问题,以及访问外部类。巧合的是,您的外部类和内部类都有一个方法func1。而且,同样有趣的是,您在B 的构造函数中创建了一个A,它绝对不是特定B 的外部类A 对象。
      【解决方案5】:

      晚了几年......但为了扩展 @mike rodent 的精彩回答,我在下面提供了我自己的示例,展示了他的解决方案是多么灵活,以及为什么它应该(或应该是)被接受的答案。

      Python 3.7

      class Parent():
      
          def __init__(self, name):
              self.name = name
              self.children = []
      
          class Inner(object):
              pass
      
          def Child(self, name):
              parent = self
              class Child(Parent.Inner):
                  def __init__(self, name):
                      self.name = name
                      self.parent = parent
                      parent.children.append(self)
              return Child(name)
      
      
      
      parent = Parent('Bar')
      
      child1 = parent.Child('Foo')
      child2 = parent.Child('World')
      
      print(
          # Getting its first childs name
          child1.name, # From itself
          parent.children[0].name, # From its parent
          # Also works with the second child
          child2.name,
          parent.children[1].name,
          # Go nuts if you want
          child2.parent.children[0].name,
          child1.parent.children[1].name
      )
      
      print(
          # Getting the parents name
          parent.name, # From itself
          child1.parent.name, # From its children
          child2.parent.name,
          # Go nuts again if you want
          parent.children[0].parent.name,
          parent.children[1].parent.name,
          # Or insane
          child2.parent.children[0].parent.children[1].parent.name,
          child1.parent.children[1].parent.children[0].parent.name
      )
      
      
      # Second parent? No problem
      parent2 = Parent('John')
      child3 = parent2.Child('Doe')
      child4 = parent2.Child('Appleseed')
      
      print(
          child3.name, parent2.children[0].name,
          child4.name, parent2.children[1].name,
          parent2.name # ....
      )
      

      输出:

      Foo Foo World World Foo World
      Bar Bar Bar Bar Bar Bar Bar
      Doe Doe Appleseed Appleseed John
      

      再一次,一个精彩的答案,麦克的道具!

      【讨论】:

      • 我担心你会发生什么pickle 不过,你最终会出现无休止的递归吗? (我不在 Python 控制台,否则我会自己测试它!)
      • 这个解决方案非常独特,应该进行更多评估。拥有与功能 Child 相同的类 Child 是重点。
      【解决方案6】:

      我找到了this

      针对您的问题进行了调整:

      class Outer(object):
          def some_method(self):
              # do something
      
          class _Inner(object):
              def __init__(self, outer):
                  outer.some_method()
          def Inner(self):
              return _Inner(self)
      

      我相信你可以为这个或其他东西写一个装饰器

      相关:What is the purpose of python's inner classes?

      【讨论】:

        【解决方案7】:

        根据另一个 answer 的一个好主意,我已经创建了一些 Python 代码来使用其 内部类 中的 外部类。我认为它简短、简单且易于理解。

        class higher_level__unknown_irrelevant_name__class:
            def __init__(self, ...args...):
                ...other code...
                # Important lines to access sub-classes.
                subclasses = self._subclass_container()
                self.some_subclass = subclasses["some_subclass"]
                del subclasses # Free up variable for other use.
        
            def sub_function(self, ...args...):
                ...other code...
        
            def _subclass_container(self):
                _parent_class = self # Create access to parent class.
                class some_subclass:
                    def __init__(self):
                        self._parent_class = _parent_class # Easy access from self.
                        # Optional line, clears variable space, but SHOULD NOT BE USED
                        # IF THERE ARE MULTIPLE SUBCLASSES as would stop their parent access.
                        #  del _parent_class
                class subclass_2:
                    def __init__(self):
                        self._parent_class = _parent_class
                # Return reference(s) to the subclass(es).
                return {"some_subclass": some_subclass, "subclass_2": subclass_2}
        

        主要代码,“生产就绪”(没有 cmets 等)。请记住将尖括号中的所有值(例如&lt;x&gt;)替换为所需的值。

        class <higher_level_class>:
            def __init__(self):
                subclasses = self._subclass_container()
                self.<sub_class> = subclasses[<sub_class, type string>]
                del subclasses
        
            def _subclass_container(self):
                _parent_class = self
                class <sub_class>:
                    def __init__(self):
                        self._parent_class = _parent_class
                return {<sub_class, type string>: <sub_class>}
        

        说明此方法的工作原理(基本步骤):

        1. 创建一个名为_subclass_container 的函数作为包装器来访问变量self,这是对更高级别类的引用(来自函数内部运行的代码)。

          1. 创建一个名为_parent_class的变量,这是对该函数的变量self的引用,_subclass_container的子类可以访问(避免与子类中的其他self变量发生名称冲突)。

          2. 将子类/子类作为字典/列表返回,以便调用_subclass_container 函数的代码可以访问其中的子类。

        2. 在更高级别类中的__init__ 函数中(或其他任何需要的地方),将函数_subclass_container 返回的子类接收到变量subclasses 中。

        3. 将存储在subclasses 变量中的子类分配给更高级别类的属性。

        让场景更简单的一些技巧:

        使将子类分配给更高级别类的代码更易于复制并用于从更高级别类派生的类中使用 __init__ 功能已更改:

        在主代码的第 12 行之前插入:

        def _subclass_init(self):
        

        然后将第 5-6 行(主代码)插入到此函数中,并将第 4-7 行替换为以下代码:

        self._subclass_init(self)
        

        当有许多/未知数量的子类时,可以将子类分配给更高级别的类。

        将第 6 行替换为以下代码:

        for subclass_name in list(subclasses.keys()):
            setattr(self, subclass_name, subclasses[subclass_name])
        

        此解决方案在哪里有用以及在哪里应该不可能获得更高级别的类名的示例场景:

        创建了一个名为“a”(class a:) 的类。它有需要访问它的子类(父类)。一个子类称为“x1”。在这个子类中,运行代码a.run_func()

        然后创建另一个名为“b”的类,派生自“a”类 (class b(a):)。之后,一些代码运行b.x1()(调用b的子函数“x1”,一个派生的子类)。这个函数运行a.run_func(),调用类“a”的函数“run_func”,不是它的父类“b”的函数“run_func”(应该如此),因为这个函数是在“a”类中定义的函数被设置为引用“a”类的函数,因为那是它的父类。

        这会导致问题(例如,如果函数 a.run_func 已被删除),唯一的解决方案是不重写类 a.x1 中的代码,将重新定义子类 x1,并为所有派生的类更新代码“a”类显然是困难的,不值得。

        【讨论】:

        • 感谢您对我的回答的好评。我对“社区 wiki 回答”标签一无所知,现在已将其删除!所以也感谢您指出这一点。您可能需要相应地编辑您的答案,以免给未来的读者造成混淆!
        【解决方案8】:

        您可以使用元类轻松访问外部类:在创建外部类后检查它的任何类的属性字典(或应用您需要的任何逻辑 - 我的只是简单的示例)并设置相应的值:

        import six
        import inspect
        
        
        # helper method from `peewee` project to add metaclass
        _METACLASS_ = '_metaclass_helper_'
        def with_metaclass(meta, base=object):
            return meta(_METACLASS_, (base,), {})
        
        
        class OuterMeta(type):
            def __new__(mcs, name, parents, dct):
                cls = super(OuterMeta, mcs).__new__(mcs, name, parents, dct)
                for klass in dct.values():
                    if inspect.isclass(klass):
                        print("Setting outer of '%s' to '%s'" % (klass, cls))
                        klass.outer = cls
        
                return cls
        
        
        # @six.add_metaclass(OuterMeta) -- this is alternative to `with_metaclass`
        class Outer(with_metaclass(OuterMeta)):
            def foo(self):
                return "I'm outer class!"
        
            class Inner(object):
                outer = None  # <-- by default it's None
        
                def bar(self):
                    return "I'm inner class"
        
        
        print(Outer.Inner.outer)
        >>> <class '__main__.Outer'>
        assert isinstance(Outer.Inner.outer(), Outer)
        
        print(Outer().foo())
        >>> I'm outer class!
        print(Outer.Inner.outer().foo())
        >>> I'm outer class!
        print(Outer.Inner().outer().foo())
        >>> I'm outer class!
        print(Outer.Inner().bar())
        >>> I'm inner class!
        

        使用这种方法,您可以轻松地在彼​​此之间绑定和引用两个类。

        【讨论】:

          【解决方案9】:

          扩展@tsnorri 的中肯思考,外部方法可能是static method

          class Outer(object):
          
              @staticmethod
              def some_static_method(self):
                  # do something
          
              class Inner(object):
                  def __init__(self):
                      self.some_static_method()    # <-- this will work later
          
              Inner.some_static_method = some_static_method
          

          现在有问题的行在实际调用时应该可以工作了。

          上面代码的最后一行给了 Inner 类一个静态方法,它是 Outer 静态方法的克隆。


          这利用了两个 Python 功能,functions are objectsscope is textual

          通常,本地范围引用(以文本形式)当前函数的本地名称。

          ...或当前的 class 在我们的例子中。因此,外部类(Innersome_static_method)定义的“本地”对象可以直接在该定义中引用。

          【讨论】:

            【解决方案10】:

            您正试图从内部类实例访问外部类实例。所以只需使用工厂方法构建 Inner 实例并将 Outer 实例传递给它。

            class Outer(object):
            
                def createInner(self):
                    return Outer.Inner(self)
            
                class Inner(object):
                    def __init__(self, outer_instance):
                        self.outer_instance = outer_instance
                        self.outer_instance.somemethod()
            
                    def inner_method(self):
                        self.outer_instance.anothermethod()
            

            【讨论】:

            • 太棒了!如果你改变外部类的任何东西,内部类会相应地显示该值!
            【解决方案11】:

            另一种可能性:

            class _Outer (object):
                # Define your static methods here, e.g.
                @staticmethod
                def subclassRef ():
                    return Outer
            
            class Outer (_Outer):
                class Inner (object):
                    def outer (self):
                        return _Outer
            
                    def doSomething (self):
                        outer = self.outer ()
                        # Call your static mehthods.
                        cls = outer.subclassRef ()
                        return cls ()
            

            【讨论】:

              【解决方案12】:

              嵌套类的方法不能直接访问外部类的实例属性。

              请注意,即使您创建了内部类的实例,也不一定存在外部类的实例。

              事实上,通常建议不要使用嵌套类,因为嵌套并不意味着内部和外部类之间有任何特定的关系。

              【讨论】:

              • 嗯,Python 比 Java/C++ 更活泼……请参阅下面的答案。如果我们通常是分裂头发,我真的无法告诉你我的“方法中的嵌套类”是否算作内部类。不过,在这一点上,我必须调用鸭子类型:如果它完成了内部类可能做的所有事情......从 Python 的角度来看,可能是时候厌倦分裂的头发了
              • 内部类当然暗示着与外部类的关系,通常与内部类的隐含使用范围或组织命名空间有关。
              • 如果您像@Kitlbas 在另一个答案中建议的那样设置它们,它们可以。这样,如果您在外部类上更改变量的值并从内部类访问它,您将获得所述变量的更新值。
              【解决方案13】:

              你的意思是使用继承,而不是像这样嵌套类吗?你所做的在 Python 中毫无意义。

              您可以通过仅在内部类的方法中引用Outer.some_method 来访问Outer 的some_method,但它不会像您期望的那样工作。例如,如果您尝试这样做:

              class Outer(object):
              
                  def some_method(self):
                      # do something
              
                  class Inner(object):
                      def __init__(self):
                          Outer.some_method()
              

              ...初始化Inner 对象时会出现TypeError,因为Outer.some_method 期望接收Outer 实例作为其第一个参数。 (在上面的示例中,您基本上是在尝试将some_method 作为Outer 的类方法调用。)

              【讨论】:

              • 它可能没有意义的原因是因为我是故意的。在 Django 中将自定义方法添加到 QuerySet 需要一些样板代码,我试图使用 python 导出一种巧妙的方法来实现它,它允许我对样板代码进行模板化,并在我的模型代码中简单地编写相关部分。跨度>
              • 抱歉——我不了解 Django,因此无法建议模板化样板代码的方法,但您可能会在尝试嵌套类时发现错误的树。您的 Inner 课程不会从您的 Outer 课程中获得任何东西。将它嵌套在 Outer 中的所有操作都是强制您通过 Outer.Inner 访问它,而不仅仅是普通的 Inner。
              猜你喜欢
              • 2011-02-13
              • 2021-03-13
              • 2021-12-31
              • 1970-01-01
              • 1970-01-01
              • 2014-02-11
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多