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


101

我有这样的情况

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

如何OuterInner类中访问类的方法?


你为什么做这个?简单的同伴关系有什么问题?您是否要“隐藏”某物?
S.Lott 2010年

1
这种情况的一个示例可能是具有一个带有子类的类,该子类需要照常访问外部类,但随后需要创建从第一类派生的另一个类(顶层)。在这种情况下,第二个类的子类将尝试使用self.<original_parent_name>获取原始类,而不是新类(它们是的子类)来访问父我希望阅读此书的人可以形象地看到这种困难的情况,并了解类似问题的要点。
爱德华

1
复制到stackoverflow.com/questions/2278426,它有一个不错的答案。
xmedeko

Answers:


67

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

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

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


1
嗯,Python比Java / C ++活泼...请参阅下面的答案。如果我们平时很忙,我真的不能告诉你我的“方法中的嵌套类”是否算作内部类。但是,在这一点上,我必须调用鸭子类型:如果它能够完成内部类可能做的所有事情……从Python的角度来看,可能是时候变得无聊了
麦克·啮齿动物

21
当然,内部类确实暗示着与外部类的关系,通常与内部类或组织名称空间的隐含使用范围有关。
Acumenus 2014年

59

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

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

这正是正确的答案-应该选择正确的答案,因为它为OP的问题提供了解决方案-非常感谢!
丹尼尔

28

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

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

1
这对我有用。这个结构到底叫什么?有工厂功能吗?关闭?
nudefanatic 2011年

我不知道这是什么线索...但是我可能想建议其他海报者之所以没有看到它的原因,是因为人们可能并不完全理解Python中的大多数东西都是非神圣的,包括“自我”。 “(任意名称)和类-它们是“一流的对象”,这似乎意味着您可以以非常离谱的方式进行操作
麦克·啮齿动物

做得好。受此解决方案思想和思考的启发,我扩展了部分答案,以在下面创建另一个答案并提供更多解释。
爱德华

1
@mikerodent因为对您的评论(关于我的回答),我现在已经编辑了我的回答,以删除“社区Wiki”的描述。像您一样,我对“社区Wiki”的答案一无所知,因此,您可以纠正错误。
爱德华

@mikerodent同样,没有违法行为,但是我不认为“社区Wiki”答案具有“社区Wiki”标签,它们必须只是一种答案。如果您碰巧感兴趣,则可能会在Stack Overflow上提供一个帮助页面,其中包含“社区Wiki”答案的描述。
爱德华

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


1
之所以可能没有任何意义,是因为我故意变黑。在Django中将自定义方法添加到QuerySet中需要一些样板代码,而我试图衍生出一种巧妙的方式来使用python做到这一点,这使我可以对样板代码进行模板化,并简单地在模型代码中编写相关部分。
T. Stone 2010年

抱歉-我不了解Django,因此无法建议一种模板化样板代码的方法,但是您可能在尝试嵌套类时遇到了错误的问题。内部班级没有从外部班级获得任何东西。将其嵌套在Outer内部的所有操作都迫使您通过Outer.Inner而不是简单的Inner访问它。
zenbot 2010年

3

您可以使用元类轻松访问外部类:创建外部类后,检查任何类的属性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!

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


3

基于这个问题的另一个答案,我创建了一些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}

主代码为“生产就绪”(无注释等)。切记将尖括号(例如<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()运行代码。

然后,从类“ a”()派生另一个名为“ b”的类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答案”标签一无所知,现在已将其删除!因此,也感谢您指出这一点。您可能需要相应地编辑答案,以免引起将来读者的困惑!
麦克啮齿动物

3

我发现了这个

调整了适合您的问题:

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)

我确定您可以以某种方式为此目的编写装饰器

相关:python内部类的目的是什么?


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

1

扩展@tsnorri的有说服力的思想,即外部方法可能是静态方法

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功能,即功能是对象作用域是文本

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

...或本例中的当前班级。因此Innersome_static_method可以直接在该定义内引用“外部”类(和)定义的“局部”对象。


1

几年迟到了....但是扩大@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

再次,一个很好的答案,迈克的道具!


-2

这太简单了:

输入:

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类func1


2
嗯,注意这个问题,单词访问外部类。通过一个快乐的,相当有趣的巧合,您的外部类和内部类都有一个方法func1。同样有趣的是,您创建了一个A内部构造函数B,这绝对不是A那个特定对象的外部类对象B
mike啮齿动物
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.