Доступ к внешнему классу из внутреннего класса в Python - PullRequest
78 голосов
/ 08 января 2010

У меня такая ситуация ...

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?

Редактировать - Спасибо за ответы. Я пришел к выводу, что мне нужно переоценить, как я спроектировал это для реализации, и придумать более надежный метод.

Ответы [ 9 ]

50 голосов
/ 08 января 2010

Методы вложенного класса не могут напрямую обращаться к атрибутам экземпляра внешнего класса.

Обратите внимание, что это не обязательно тот случай, когда экземпляр внешнего класса существует, даже если вы создали экземпляр внутреннего класса.

На самом деле его часто не рекомендуется использовать вложеннымклассы, поскольку вложение не подразумевает каких-либо особых отношений между внутренним и внешним классами.

43 голосов
/ 12 августа 2011

Вы пытаетесь получить доступ к экземпляру класса 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()
24 голосов
/ 22 августа 2011

возможно, я злюсь, но на самом деле это кажется очень простым - дело в том, чтобы сделать свой внутренний класс внутри метода внешнего класса ...

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 для «класса mooble (объект)»

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 голосов
/ 08 января 2010

Вы имеете в виду использовать наследование, а не вложение классов, как это? То, что вы делаете, не имеет смысла в 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()

... вы получите TypeError при инициализации объекта Inner, потому что Outer.some_method ожидает получить экземпляр Outer в качестве первого аргумента. (В приведенном выше примере вы в основном пытаетесь вызвать some_method как метод класса Outer.)

3 голосов
/ 31 января 2016

Я создал некоторый код 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().

Затем создается другой класс с именем "b", полученный из класса "a" (class b(a):). После этого часть кода запускается b.x1() (вызывая подфункцию "x1" из b, производный подкласс). Эта функция запускает a.run_func(), вызывая функцию "run_func" класса "a", not функцию "run_func" ее родителя, "b" (как и должно быть), потому что функция, которая была определена в классе "a" устанавливается ссылка на функцию класса "a", так как она была его родителем.

Это может вызвать проблемы (например, если функция a.run_func была удалена), и единственным решением без переписывания кода в классе a.x1 будет переопределение подкласса x1 с обновленным кодом для всех классов, полученных из класс "а", который, очевидно, будет сложным и не стоит того.

2 голосов
/ 06 июня 2017

Вы можете легко получить доступ к внешнему классу, используя метакласс: после создания внешнего класса проверьте его атрибуты 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 голосов
/ 29 июля 2011

Другая возможность:

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 голос
/ 20 октября 2015

Расширение на убедительное мышление @ 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: функции являются объектами , а scope является текстовым .

Обычно локальная область действия ссылается на локальные имена (текстуально) текущей функции.

... или текущий класс в нашем случае. Таким образом, объекты, «локальные» для определения класса Outer (Inner и some_static_method), могут ссылаться непосредственно в этом определении.

1 голос
/ 16 мая 2011

я нашел это .

Изменено, чтобы ответить на ваш вопрос, это ответ:

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)

Я уверен, что вы можете написать декоратор для этого или чего-то еще:)
/ edit: своего рода

...