programacion - poo python 3
¿Clase derivada de Python y atributos de clase base? (2)
Si hago una clase derivada, ¿tendrá automáticamente todos los atributos de la clase base?
Atributos de clase, sí. Atributos de instancia, no (simplemente porque no existen cuando se crea la clase), a menos que no haya __init__
en la clase derivada, en cuyo caso se llamará la base en su lugar, y establecerá los atributos de instancia.
Hace BaseClass. init () necesitas argumentos?
Depende de la clase y su firma __init__
. Si está llamando explícitamente a Base.__init__
en la clase derivada, al menos deberá pasar self
como primer argumento. Si usted tiene
class Base(object):
def __init__(self):
# something
entonces es bastante obvio que ningún otro argumento es aceptado por __init__
. Si tuvieras
class Base(object):
def __init__(self, argument):
# something
entonces tienes que pasar un argument
al llamar a base __init__
. No hay ciencia espacial aquí.
Si tiene argumentos para su clase base init (), si la clase derivada también los utiliza, ¿necesita establecer explícitamente los argumentos en el init () de la clasificación derivada o establecerlos en BaseClass? init () en su lugar?
Nuevamente, si la clase derivada no tiene __init__
, se usará una base en su lugar.
class Base(object):
def __init__(self, foo):
print ''Base''
class Derived(Base):
pass
Derived() # TypeError
Derived(42) # prints Base
En otro caso, necesitas cuidarlo de alguna manera. Si usa *args, **kwargs
y simplemente pasa argumentos no modificados a la clase base, o copia la firma de la clase base, o suministra argumentos de otros lugares, depende de lo que esté tratando de lograr.
Parece que no hay buena documentación en línea sobre esto: si hago una clase derivada, ¿tendrá automáticamente todos los atributos de la clase base? Pero, ¿para qué BaseClass.__init()
? ¿También debe hacerlo con otros métodos de clase base? ¿ BaseClass.__init__()
necesita argumentos? Si tiene argumentos para su clase base __init__()
, ¿también los utiliza la clase derivada, necesita establecer explícitamente los argumentos en el __init__()
la __init__()
derivada, o establecerlos en BaseClass.__init__()
lugar?
Si implementa __init__
en una clase derivada de BaseClass, se sobrescribirá el método __init__
heredado y por BaseClass.__init__
, nunca se llamará a BaseClass.__init__
. Si necesita llamar al método __init__
para BaseClass (como suele ser el caso), entonces depende de usted hacerlo, y se hace explícitamente llamando a BaseClass.__init__
, normalmente desde el método __init__
recién implementado.
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
self.b = 20
bar = Bar()
bar.do_something()
Esto causará el siguiente error:
AttributeError: ''Bar'' object has no attribute ''a''
Por lo tanto, el método do_something
se ha heredado como se esperaba, pero ese método requiere que se haya establecido el atributo a, que nunca se debe a que __init__
también se sobrescribió. Foo.__init__
esto llamando explícitamente a Foo.__init__
desde dentro de Bar.__init__
.
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self)
self.b = 20
bar = Bar()
bar.do_something()
que imprime 10
como se esperaba. Foo.__init__
en este caso espera un solo argumento que es una instancia de Foo
(que por convención se llama self
).
Normalmente, cuando llama a un método en una instancia de una clase, la instancia de la clase se pasa automáticamente como el primer argumento. Los métodos en una instancia de una clase se denominan métodos enlazados . bar.do_something
es un ejemplo de un método enlazado (y bar.do_something
que se llama sin ningún argumento). Foo.__init__
es un método Foo.__init__
porque no está vinculado a una instancia particular de Foo
, por lo que el primer argumento, una instancia de Foo
, debe pasarse explícitamente.
En nuestro caso, nos pasamos a Foo.__init__
, que es la instancia de Bar
que se pasó al método __init__
en Bar
. Como Bar
hereda de Foo
, las instancias de Bar
también son instancias de Foo
, por lo que self
permite pasar self
to Foo.__init__
.
Es probable que la clase de la que está heredando requiera o acepte más argumentos que solo una instancia de la clase. Estos se tratan como lo haría con cualquier método que esté llamando desde __init__
:
class Foo(object):
def __init__(self, a=10):
self.a = a
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self, 20)
bar = Bar()
bar.do_something()
que imprimiría 20
.
Si está intentando implementar una interfaz que expone completamente todos los argumentos de inicialización de la clase base a través de su clase heredada, deberá hacerlo explícitamente. Esto normalmente se hace con los argumentos * args y ** kwargs (los nombres son por convención), que son marcadores de posición para el resto de los argumentos que no tienen un nombre explícito. El siguiente ejemplo hace uso de todo lo que he discutido:
class Foo(object):
def __init__(self, a, b=10):
self.num = a * b
def do_something(self):
print self.num
class Bar(Foo):
def __init__(self, c=20, *args, **kwargs):
Foo.__init__(self, *args, **kwargs)
self.c = c
def do_something(self):
Foo.do_something(self)
print self.c
bar = Bar(40, a=15)
bar.do_something()
En este caso, el argumento c
se establece en 40, ya que es el primer argumento de Bar.__init__
. El segundo argumento se incorpora a las variables args
y kwargs
(el * y ** es una sintaxis específica que dice expandir la lista / tupla o diccionario en argumentos separados cuando se pasa a una función / método), y se pasa a Foo.__init__
.
Este ejemplo también señala que cualquier método sobrescrito debe llamarse explícitamente si eso es lo que se requiere (como do_something
es do_something
en este caso).
Un último punto, a menudo verá que se super(ChildClass, self).method()
(donde ChildClass
es una clase secundaria arbitraria) que se usa en lugar de una llamada al método BaseClass
explícitamente. La discusión de super
es otra pregunta, pero basta con decir que, en estos casos, normalmente se usa para hacer exactamente lo que se hace llamando a BaseClass.method(self)
. Brevemente, los super
delegados de la llamada al método a la siguiente clase en el orden de resolución del método: el MRO (que en herencia única es la clase principal). Consulte la documentación en super para más información.