Pergunta

Eu tenho uma situação assim ...

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

Como posso acessar o método da classe Outer da classe Inner?

Editar - Obrigado pelas respostas. Estou concluindo que eu preciso re-avaliar como eu tinha projetado isso para ser implementado e chegar a um método mais robusto.

Foi útil?

Solução

Os métodos de uma classe aninhada não pode acessar diretamente a instância atributos da classe externa.

Note que não é necessariamente o caso que uma instância da classe externa existe mesmo quando você tiver criado uma instância da classe interna.

Na verdade, muitas vezes é recomendado contra o uso de classes aninhadas, uma vez que o assentamento não implica qualquer relação particular entre as classes internas e externas.

Outras dicas

Você está tentando acessar instância de classe do exterior, de instância de classe interior. Então, basta usar fábrica método para construir instância interna e passar instância exterior a ele.

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

talvez eu sou louco, mas isso parece muito fácil, na verdade - a coisa é tornar a sua classe interna dentro de um método da classe externa ...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

Além disso ... "eu" é utilizado apenas por convenção, para que você poderia fazer isso:

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

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

Pode-se objetar que você não pode, então, criar esta classe interna de fora da classe exterior ... mas isso não é verdade:

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

então, em algum lugar milhas de distância:

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

até mesmo empurrar o barco para fora um pouco e estender esta classe interna (NB para obter super () para trabalhar você tem que mudar a assinatura classe de Mooble para "Mooble classe (objeto)"

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

depois

mrh1997 levantou um ponto interessante sobre a herança não-comum de classes internas entregues usando esta técnica. Mas parece que a solução é bastante simples:

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

Você pretende usar a herança, em vez de aulas de nidificação como este? O que você está fazendo não faz um monte de sentido em Python.

Você pode acessar some_method do Outer por apenas referenciando Outer.some_method dentro de métodos da classe interna, mas não vai funcionar como você espera que ele vai. Por exemplo, se você tentar fazer isso:

class Outer(object):

    def some_method(self):
        # do something

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

... você vai ter um TypeError quando inicializar um objeto Inner, porque espera Outer.some_method para receber uma instância Outer como seu primeiro argumento. (No exemplo acima, você está basicamente tentando some_method chamada como um método de classe de Outer.)

Eu criei algum código Python para usar um classe externa de sua classe interna , com base em uma boa idéia de outra resposta para esta pergunta. Eu acho que é curto, simples e fácil de entender.

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}

O código principal, "produção pronta" (sem comentários, etc.). Lembrar para substituir a totalidade de cada valor no ângulo entre parênteses (por exemplo <x>) com o valor desejado.

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

Explicação de como este método funciona (as etapas básicas):

  1. Crie uma função chamada _subclass_container para agir como um wrapper para acessar o self variável, uma referência para a classe de nível superior (a partir do código em execução dentro da função).

    1. Criar uma variável chamada _parent_class que é uma referência para o self variável desta função, que as subclasses de _subclass_container acesso lata (evita nomear conflitos com outras variáveis ??self em subclasses).

    2. devolver o sub-classe / sub-classes como um dicionário / lista para código chamar a função _subclass_container pode acessar os sub-classes dentro.

  2. Na função __init__ dentro da classe de nível superior (ou onde mais necessário), receber as sub-classes retornados da _subclass_container função no subclasses variável.

  3. Atribuir sub-classes armazenados na variável subclasses aos atributos da classe de nível superior.

Algumas dicas para tornar mais fácil cenários:

Fazer o código para atribuir as subclasses para a classe de nível superior mais fácil copiar e ser usado em classes derivadas da classe de nível superior que têm a sua __init__ Função alterado:

Inserir antes da linha 12 no código principal:

    def _subclass_init(self):

Em seguida, inserir esta função linhas 5-6 (do código principal) e substituir linhas 4-7 com o seguinte código:

        self._subclass_init(self)

Fazer subclasse atribuindo à classe nível mais elevado possível quando há muitos / unknown quantidades de subclasses.

Substituir linha 6 com o seguinte código:

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

Exemplo cenário de onde esta solução seria útil e onde o nome da classe de nível superior deve ser impossível de obter:

A classe, chamado "a" (class a:) é criado. Ele tem subclasses que precisam acessá-lo (o pai). Uma subclasse é chamado de "x1". Nesta subclasse, o a.run_func() código é executado.

Em seguida, uma outra classe, chamado "b" é criado, derivada da classe "a" (class b(a):). Depois disso, alguns código é executado b.x1() (chamando a sub-função "x 1" de b, uma sub-classe de derivados). Esta função é executado a.run_func(), chamando a função "run_func" de classe "a", não a função "run_func" de seu pai, "b" (como deveria), porque a função que foi definido no classe "a" está definido para se referir à função de classe "a", como era seu pai.

Isto causaria problemas (por exemplo, se a função a.run_func foi eliminado) ea única solução sem reescrever o código em a.x1 classe seria redefinir o x1 sub-classe com código atualizado para todas as classes derivadas da classe "A", que seria obviamente, ser difícil e não vale a pena.

Outra possibilidade:

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

Você pode facilmente acesso a classe externa utilizando metaclass: após a criação da classe externa verificá-lo de atributo dict para as classes (ou aplicar qualquer lógica que você precisa - o meu é apenas exemplo trivial) e conjunto de valores correspondentes:

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!

Usando essa abordagem, você pode facilmente ligar e referem-se duas classes entre si.

Expandindo pensamento convincente de @ tsnorri, que o método exterior pode ser um href="https://docs.python.org/2/library/functions.html#staticmethod" rel="nofollow"> método estático :

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

Agora, a linha em questão deve trabalhar no momento em que é realmente chamado.

A última linha no código acima dá a classe Inner um método estático que é um clone do método estático exterior.


Isso leva vantagem de duas características Python, que funções são objetos , e escopo é textual .

Normalmente, as referências de âmbito local os nomes locais do (textualmente) função atual.

... ou corrente class no nosso caso. Então objetos "local" para a definição da classe Outer (Inner e some_static_method) podem ser referidos diretamente a essa definição.

i encontrados este .

Tweaked de acordo com a sua pergunta, é a resposta:

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)

Eu tenho certeza que você pode de alguma forma escrever um decorador para este ou algo :)
/ Edit: meio

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top