如何从内部类访问外部类?

问题描述 投票:89回答:9

我有这样的情况...

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

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

python scope nested inner-classes
9个回答
63
投票

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

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

实际上,通常建议不要使用嵌套类,因为嵌套并不暗示内部类和外部类之间的任何特定关系。


53
投票

您正在尝试从内部类实例访问外部类的实例。因此,只需使用工厂方法构建内部实例并将外部实例传递给它即可。

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()

25
投票

也许我很生气,但这似乎确实很容易-事情是使您的内部类成为外部类的方法...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

加...“自我”仅按惯例使用,因此您可以这样做:

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()    

[甚至将船伸出一点并扩展此内部类(NB要使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 ))

4
投票

您的意思是使用继承,而不是像这样嵌套类?您所做的事情在Python中没有任何意义。

您可以通过仅在内部类的方法中引用Outer来访问Outer.some_method的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的类方法来调用。)


3
投票

我已经创建了一些Python代码以使用其内部类中的外部类,这是基于另一个answer针对该问题的一个好主意。我认为它简短,简单且易于理解。

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}

主代码,“生产准备就绪”(无注释等)。请记住将尖括号(例如<x>)中的每个值全部替换为所需的值。

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”,not父类“ b”的函数“ run_func”(应如此),因为该函数在类中定义设置“ a”以引用类“ a”的功能,因为它是其父级。

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


2
投票

另一种可能性:

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 ()

2
投票

您可以使用元类轻松访问外部类:在创建外部类之后,检查任何类的属性dict(或应用所需的任何逻辑-我的例子很简单)并设置相应的值:

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!

使用这种方法,您可以轻松地相互绑定并引用两个类。


2
投票

我找到了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?


1
投票

扩展@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)定义的“本地”对象。

© www.soinside.com 2019 - 2024. All rights reserved.