classe externa acesso de classe interna em python
-
19-09-2019 - |
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.
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):
-
Crie uma função chamada
_subclass_container
para agir como um wrapper para acessar oself
variável, uma referência para a classe de nível superior (a partir do código em execução dentro da função).-
Criar uma variável chamada
_parent_class
que é uma referência para oself
variável desta função, que as subclasses de_subclass_container
acesso lata (evita nomear conflitos com outras variáveis ??self
em subclasses). -
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.
-
-
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 nosubclasses
variável. -
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