Categorías
Python

Consejos Git avanzada para Python Desarrolladores

 

Tabla de Contenidos

  • una visión general de super () Función
  • super Python () en la herencia simple
  • ¿Qué puedo super () hacer por usted?
  • Un super () del buceo de profundidad
  • super () en múltiples InheritanceMultiple Herencia Resolución OverviewMethod OrderMultiple Herencia Alternativas
  • herencia múltiple general Resolución
  • método de pedido
  • herencia múltiple Alternativas
  • Un super (herencia múltiple) Crónica
  • Descripción general
  • Método orden de resolución de
  • herencia múltiple Alternativas

Mira ahora Este tutorial tiene un vídeo relacionado curso creado por el equipo del real Python. Mira que junto con el tutorial escrito para profundizar su comprensión: Supercharge sus clases con Python super ()

Mientras que Python no es puramente un lenguaje orientado a objetos, es lo suficientemente flexible y lo suficientemente potente como para que pueda construir sus aplicaciones utilizando el paradigma orientado a objetos. Una de las formas en que Python logra esto es mediante el apoyo herencia , que lo hace con super ().

En este tutorial, aprenderá acerca de lo siguiente:

  • El concepto de herencia en Python
  • La herencia múltiple en Python
  • ¿Cómo funciona la función super ()
  • Cómo función del super () en una sola funciona la herencia
  • Cómo la función super () en la herencia múltiple funciona Bono

gratuito: 5 pensamientos sobre Python Maestría, un curso gratuito para los desarrolladores de Python que muestra la hoja de ruta y la mentalidad que necesita para tomar su Python habilidades al siguiente nivel.

Una visión general de la función de Python super ()

Si usted tiene experiencia con lenguajes orientados a objetos, es posible que ya esté familiarizado con el funcionamiento de super ().

Si no es así, no temas! Mientras que la documentación oficial es bastante técnico, en un super alto nivel () le da acceso a los métodos de una superclase de la subclase que herede de ella.

super () vuelve solo un objeto temporal de la superclase que luego le permite llamar a los métodos de esa superclase.

por qué le gustaría hacer algo de esto? Mientras que las posibilidades están limitadas por su imaginación, un caso de uso común es la construcción de clases que amplían la funcionalidad de clases construidas con anterioridad.

Llamar a los métodos previamente construida, con () le ahorra de tener que volver a escribir esos métodos en la subclase, y le permite intercambiar superclases con cambios mínimos en el código.

super () en la herencia simple

Si no está familiarizado con los conceptos de programación orientada a objetos, herencia podría ser un término desconocido. La herencia es un concepto en la programación orientada a objetos en los que se deriva una clase (o hereda ) los atributos y comportamientos de otra clase sin necesidad de aplicar de nuevo.

Al menos para mí, es más fácil de entender estos conceptos cuando se mira en el código, así que vamos a clases de escritura que describen algunas formas:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width

def area(self):
return self.length * self.width

def perimeter(self):
return 2 * self.length + 2 * self.width

class Square:
def __init__(self, length):
self.length = length

def area(self):
return self.length * self.length

def perimeter(self):
return 4 * self.length

Aquí, hay dos clases similares: rectángulo y de la plaza.

Puede usarlas como a continuación:

>>> square = Square(4)
>>> square.area()
16
>>> rectangle = Rectangle(2,4)
>>> rectangle.area()
8

En este caso, dos formas que tienes que están relacionados entre sí: un cuadrado es un tipo especial de rectángulo. El código, sin embargo, no refleja esa relación y por lo tanto tiene código que se repite en esencia.

Mediante el uso de la herencia, se puede reducir la cantidad de código que escriba mientras que simultáneamente refleja la relación del mundo real entre rectángulos y cuadrados:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width

def area(self):
return self.length * self.width

def perimeter(self):
return 2 * self.length + 2 * self.width

# Here we declare that the Square class inherits from the Rectangle class
class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)

Aquí, usted ha utilizado super () para llamar al __init __ () de la clase Rectangle , lo que permite utilizar en la clase Square sin repetir código. A continuación, los restos funcionalidad del núcleo después de hacer cambios:

>>> square = Square(4)
>>> square.area()
16

En este ejemplo, rectángulo es la superclase, y de la plaza es la subclase.

Debido a que el cuadrado y del rectángulo .__ init __ () métodos son tan similares, simplemente pueden llamar de la superclase .__ init __ () método (Rectángulo .__ init __ ()) de la de la plaza mediante el uso de super (). Esto establece el .length y atributos .Width a pesar de que sólo tenía que suministrar un único parámetro de longitud al constructor Square.

Al ejecutar esto, a pesar de que su clase de la plaza no explícitamente ponerla en práctica, la llamada a .area () utilizará el método .area () de la superclase e imprimir 16. La clase Square heredó .area ( ) de la clase Rectángulo.

Nota: Para aprender más sobre la herencia y conceptos orientados a objetos en Python, asegúrese de revisar Programación (POO) orientada a objetos en Python 3.

¿Qué puedo super () hacer por usted?

Entonces, ¿qué puede super () hacer por usted en la herencia simple?

Al igual que en otros lenguajes orientados a objetos, que le permite llamar a los métodos de la superclase en la subclase. El caso de uso principal de este es extender la funcionalidad del método heredado.

En el siguiente ejemplo, creará un cubo clase que hereda de la plaza y amplía la funcionalidad de .area () (heredado de la clase rectangular a través de Square) para calcular el área superficial y el volumen de una instancia de Cube:

class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)

class Cube(Square):
def surface_area(self):
face_area = super().area()
return face_area * 6

def volume(self):
face_area = super().area()
return face_area * self.length

ahora que usted ha construido las clases, vamos a ver la superficie y el volumen de un cubo con una longitud lateral de 3:

>>> cube = Cube(3)
>>> cube.surface_area()
54
>>> cube.volume()
27

Precaución : Tenga en cuenta que en nuestro ejemplo anterior, super () por sí sola no hará que el llamadas a métodos para usted: usted tiene que llamar al método en el propio objeto proxy.

Aquí han aplicado dos métodos para la clase Cubo: .surface_area () y .volume (). Ambos de estos cálculos se basan en el cálculo del área de una sola cara, por lo que en lugar de reimplementar el cálculo del área, se utiliza super () para extender el cálculo del área.

Observe también que la definición de clase Cube no tiene un .__ init __ (). Debido a que hereda del cubo de la plaza y .__ init __ () en realidad no hace nada diferente para el cubo de lo que ya lo hace para el cuadrado, puede omitir la definición de ella, y la .__ init __ () de la superclase (Square) se llamará automáticamente.

super () devuelve un objeto delegado a una clase padre, por lo que se llama al método que desea directamente en ella:. Super () área ().

Esto no sólo nos salva de tener que volver a escribir los cálculos de área, sino que también nos permite cambiar la lógica interna .area () en un solo lugar. Esto es especialmente útil cuando se tiene un número de subclases que heredan de una superclase.

Un super () del buceo de profundidad

Antes de dirigirse a la herencia múltiple, vamos a tomar un pequeño desvío en la mecánica de super ().

Mientras que los ejemplos anteriores (y abajo) súper call () sin ningún parámetro, super () puede también tener dos parámetros: la primera es la subclase, y el segundo parámetro es un objeto que es una instancia de esa subclase.

En primer lugar, vamos a ver dos ejemplos que muestran lo que la manipulación de la primera variable puede hacer, utilizando las clases ya que se muestran:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width

def area(self):
return self.length * self.width

def perimeter(self):
return 2 * self.length + 2 * self.width

class Square(Rectangle):
def __init__(self, length):
super(Square, self).__init__(length, length)

En Python 3, el super llamada (Square, uno mismo) es equivalente a la súper sin parámetros () llamada. El primer parámetro se refiere a la subclase Square, mientras que el segundo parámetro se refiere a un objeto Square, que, en este caso, es auto. Puede llamar a super () con otras clases, así:

class Cube(Square):
def surface_area(self):
face_area = super(Square, self).area()
return face_area * 6

def volume(self):
face_area = super(Square, self).area()
return face_area * self.length

En este ejemplo, se están estableciendo la plaza como el argumento subclase a super (), en lugar de cubo. Esto hace super () para iniciar la búsqueda de un método de adaptación (en este caso, .area ()) en un nivel por encima de la plaza en la jerarquía de ejemplo, en este caso rectangular.

En este ejemplo concreto, el comportamiento no cambia. Pero imaginemos que Square también implementó una función .area () que quería asegurarse de que no utilizó Cubo. Llamar a super () de esta forma permite que hagas eso.

Precaución: A pesar de que están haciendo un montón de tocar el violín con los parámetros a super () con el fin de explorar cómo funciona bajo el capó, me advierten en contra de hacer esto de forma regular. Se recomienda

El sin parámetros llamada a super () y suficiente para la mayoría casos de uso, y la necesidad de cambiar la jerarquía de búsqueda regularmente podría ser indicativo de un problema de diseño más grande.

¿Qué pasa con el segundo parámetro? Recuerde, esto es un objeto que es una instancia de la clase utilizada como primer parámetro. Por ejemplo, isinstance (cubo, cuadrado) debe devolver True.

Mediante la inclusión de un objeto instanciado, super () devuelve un obligado método : un método que se enlaza con el objeto, lo que da el método de contexto del objeto tal como los atributos de instancia. Si no se incluye este parámetro, el método devuelve es sólo una función, no asociado con el contexto de un objeto.

Para obtener más información acerca de los métodos encuadernados, métodos no unidas, y las funciones, lea la documentación de Python en su sistema de descriptores.

Nota: Técnicamente, super () no devuelve un método. Devuelve un objeto proxy . Este es un objeto que delega las llamadas a los métodos de la clase correcta sin hacer un objeto adicional con el fin de hacerlo.

super () en la herencia múltiple

Ahora que ha trabajado a través de una visión general y algunos ejemplos de super () y la herencia simple, se le presentó a una visión general y algunos ejemplos que demuestran cómo funciona la herencia múltiple y cómo súper () permite que la funcionalidad.

herencia múltiple general

Hay otro caso en el que el uso super () realmente brilla, y éste no es tan común como el escenario de herencia simple. Además de la herencia simple, Python soporta la herencia múltiple, en el que una subclase puede heredar de múltiples superclases que no necesariamente heredan de uno al otro (también conocido como hermano clases ).

Soy una persona muy visual, y me encuentro con diagramas son muy útiles para entender conceptos como éste. La imagen siguiente muestra un escenario simple herencia múltiple, donde uno hereda de la clase de dos (hermano) superclases no relacionadas:

Para ilustrar mejor la herencia múltiple en acción, he aquí algo de código para que usted pueda probar, mostrando cómo se puede construir una pirámide recta (una pirámide de base cuadrada) de un triángulo y un cuadrado:

class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height

def area(self):
return 0.5 * self.base * self.height

class RightPyramid(Triangle, Square):
def __init__(self, base, slant_height):
self.base = base
self.slant_height = slant_height

def area(self):
base_area = super().area()
perimeter = super().perimeter()
return 0.5 * perimeter * self.slant_height + base_area

Nota: El término altura inclinada pueden ser desconocidos, especialmente si ha pasado un tiempo desde que ha tomado una clase de geometría o trabajado en cualquier pirámides.

La altura inclinada es la altura desde el centro de la base de un objeto (como una pirámide) hasta su cara al pico de ese objeto. Puede leer más sobre alturas inclinadas en WolframMathWorld.

En este ejemplo se declara una clase de triángulo y una clase que hereda de RightPyramid tanto triangular y cuadrada.

Verás otro método .area () que utiliza super () al igual que en la herencia simple, con el objetivo de que llegar a la .perimeter () y .area () Los métodos definidos todo el camino hasta la clase Rectángulo.

Nota: Usted puede notar que el código anterior no está utilizando propiedades heredadas de la clase Triángulo todavía. Ejemplos posteriores tendrán plenamente las ventajas de la herencia tanto de triángulo y el cuadrado.

El problema, sin embargo, es que ambas superclases (triángulo y cuadrado) definen un .area (). Tome un segundo y pensar en lo que podría suceder cuando se llama .area () en RightPyramid, y luego tratar de llamar a él como a continuación:

>> pyramid = RightPyramid(2, 4)
>> pyramid.area()
Traceback (most recent call last):
File "shapes.py", line 63, in
print(pyramid.area())
File "shapes.py", line 47, in area
base_area = super().area()
File "shapes.py", line 38, in area
return 0.5 * self.base * self.height
AttributeError: 'RightPyramid' object has no attribute 'height'

lo ha adivinado que Python intentará llamar Triangle.area ()? Esto se debe a algo que se llama el método de resolución para .

Nota: Cómo hizo notamos que Triangle.area () fue llamado y no, como esperábamos, Square.area ()? Si nos fijamos en la última línea del rastreo (antes de la AttributeError), verá una referencia a una línea específica de código:

return 0.5 * self.base * self.height

Usted puede reconocer esto desde la clase de geometría como la fórmula para el área de un triángulo. De lo contrario, si usted es como yo, es posible que se haya desplazado hasta las definiciones de clase de triángulo y rectángulo y visto este mismo código en Triangle.area (). Resolución

método de pedido

El orden de resolución de métodos (o MRO ) le dice a Python cómo buscar métodos heredados. Esto es muy útil cuando se está utilizando super () porque el MRO te dice exactamente donde Python buscará un método que está llamando con super () y en qué orden.

Cada clase tiene un .__ atributo mro__ que nos permite inspeccionar la orden, así que vamos a hacer lo siguiente:

>>> RightPyramid.__mro__
(, ,
, ,
)

Esto nos dice que los métodos se buscará por primera vez en Rightpyramid, a continuación, en Triangle, a continuación, en la plaza, a continuación, Rectángulo, y luego Si no se encuentra nada, en el objeto, a partir del cual se originan todas las clases.

El problema aquí es que el intérprete es la búsqueda de .area () en triángulo antes cuadrado y del rectángulo, y al encontrar .area () en Triangle, Python lo invoca en lugar de la que usted desea. Debido Triangle.area () espera que haya un .height y un atributo .base, Python lanza una AttributeError.

Afortunadamente, usted tiene cierto control sobre cómo se construye el MRO. Con sólo cambiar la firma de la clase RightPyramid, puede buscar en el orden que desee, y los métodos resolverá correctamente:

class RightPyramid(Square, Triangle):
def __init__(self, base, slant_height):
self.base = base
self.slant_height = slant_height
super().__init__(self.base)

def area(self):
base_area = super().area()
perimeter = super().perimeter()
return 0.5 * perimeter * self.slant_height + base_area

en cuenta que RightPyramid inicializa parcialmente con la .__ init __ () de la clase Square. Esto permite .area () para usar la .length en el objeto, como está diseñado.

Ahora, se puede construir una pirámide, inspeccione el MRO, y calcular la superficie:

>>> pyramid = RightPyramid(2, 4)
>>> RightPyramid.__mro__
(, ,
, ,
)
>>> pyramid.area()
20.0

Usted ve que el MRO es ahora lo que se espera, y se puede inspeccionar el área de la pirámide, así, gracias a .area () y .perimeter ().

todavía hay un problema aquí, sin embargo. En aras de la simplicidad, he hecho algunas cosas mal en este ejemplo: la primera, y podría decirse que es más importante, era que tenía dos clases separadas con el mismo nombre de método y firma.

Esto causa problemas con la resolución de métodos, ya que la primera instancia de .area () que se encuentra en la lista de MRO será llamado.

Cuando se está usando super () con la herencia múltiple, que es imprescindible para diseñar sus clases a cooperar . Parte de esto es asegurarse de que sus métodos son únicos para que se resuelven en el MRO, asegurándose de que la firma de métodos son únicos, ya sea mediante el uso de nombres de métodos o parámetros del método.

En este caso, para evitar una revisión completa de su código, puede cambiar el nombre de método de la clase Triángulo .area () para .tri_area (). De esta manera, los métodos de área puede seguir utilizando las propiedades de clase en lugar de tomar parámetros externos:

class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
super().__init__()

def tri_area(self):
return 0.5 * self.base * self.height

Vamos también a seguir adelante y utilizar esto en la clase RightPyramid:

class RightPyramid(Square, Triangle):
def __init__(self, base, slant_height):
self.base = base
self.slant_height = slant_height
super().__init__(self.base)

def area(self):
base_area = super().area()
perimeter = super().perimeter()
return 0.5 * perimeter * self.slant_height + base_area

def area_2(self):
base_area = super().area()
triangle_area = super().tri_area()
return triangle_area * 4 + base_area

La siguiente cuestión aquí es que el código no tiene un triángulo delegado objeto, como lo hace con un objeto de la plaza, así que llamar .area_2 () nos dará una AttributeError desde .base y .height no tienen ningún valor.

que tiene que hacer dos cosas para solucionar este problema:

Todos los métodos que son llamados con súper necesidad () para tener una llamada a la versión de su superclase de ese método. Esto significa que usted tendrá que añadir super () .__ init __ () para los métodos .__ init __ () de triángulo y rectángulo.

Rediseño todo el .__ init __ () llama a tomar un diccionario de palabras clave. Ver el código completo a continuación.

completo Código Ejemplo Mostrar / Ocultar

class Rectangle:
def __init__(self, length, width, **kwargs):
self.length = length
self.width = width
super().__init__(**kwargs)

def area(self):
return self.length * self.width

def perimeter(self):
return 2 * self.length + 2 * self.width

# Here we declare that the Square class inherits from
# the Rectangle class
class Square(Rectangle):
def __init__(self, length, **kwargs):
super().__init__(length=length, width=length, **kwargs)

class Cube(Square):
def surface_area(self):
face_area = super().area()
return face_area * 6

def volume(self):
face_area = super().area()
return face_area * self.length

class Triangle:
def __init__(self, base, height, **kwargs):
self.base = base
self.height = height
super().__init__(**kwargs)

def tri_area(self):
return 0.5 * self.base * self.height

class RightPyramid(Square, Triangle):
def __init__(self, base, slant_height, **kwargs):
self.base = base
self.slant_height = slant_height
kwargs["height"] = slant_height
kwargs["length"] = base
super().__init__(base=base, **kwargs)

def area(self):
base_area = super().area()
perimeter = super().perimeter()
return 0.5 * perimeter * self.slant_height + base_area

def area_2(self):
base_area = super().area()
triangle_area = super().tri_area()
return triangle_area * 4 + base_area

Hay una serie de diferencias importantes en este código: kwargs

  • se modifica en algunos lugares (como init RightPyramid .__ __ ()): Esto permitirá a los usuarios de estos objetos para crear una instancia ellos sólo con los argumentos que tienen sentido para ese objeto en particular.
  • Configuración de argumentos con nombre antes de kwargs **: Usted puede ver esto en init RightPyramid .__ __ (). Esto tiene el efecto de hacer estallar ordenado que fuera clave correcta de los kwargs ** diccionario, de modo que para el momento en que termina al final de la MRO en la clase de objeto, ** kwargs está vacía. kwargs

se modifica en algunos lugares (como init RightPyramid .__ __ ()): Esto permitirá a los usuarios de estos objetos a instanciarlas sólo con los argumentos que tienen sentido para ese objeto en particular.

Configuración de argumentos con nombre antes de kwargs **: Esto se puede ver en RightPyramid .__ init __ (). Esto tiene el efecto de hacer estallar ordenado que fuera clave correcta de los kwargs ** diccionario, de modo que para el momento en que termina al final de la MRO en la clase de objeto, ** kwargs está vacía.

Nota: que sigue el estado de kwargs puede ser complicado aquí, así que aquí tiene una tabla de .__ init __ () llamadas en orden, que muestran la clase que posee esa llamada, y el contenido de kwargs durante esa llamada:

Ahora, cuando se utiliza estas clases actualizadas, tienes esto:

>>> pyramid = RightPyramid(base=2, slant_height=4)
>>> pyramid.area()
20.0
>>> pyramid.area_2()
20.0

funciona! Tienes superaleaciones utilizadas () para navegar con éxito una complicada jerarquía de clases mientras se utiliza tanto la herencia y la composición para crear nuevas clases con reimplementación mínima.

herencia múltiple Alternativas

Como se puede ver, la herencia múltiple puede ser útil, sino también dar lugar a situaciones muy complicadas y código que es difícil de leer. También es raro tener objetos que heredan cuidadosamente todo, desde más de varios otros objetos.

Si usted se ve que comienza a utilizar la herencia múltiple y una jerarquía de clases complicado, vale la pena preguntarse si se puede lograr código que es más limpio y más fácil de comprender mediante el uso de composición en lugar de herencia. Dado que este artículo se centra en la herencia, no voy a entrar en demasiados detalles sobre la composición y la forma de ponerla en marcha en Python. Por suerte, el Real Python ha publicado una guía de profundidad de la inmersión tanto a la herencia y la composición en Python que le hará un profesional de programación orientada a objetos en ningún momento.

No es otra técnica que puede ayudarle a obtener en torno a la complejidad de la herencia múltiple, mientras que todavía proporciona muchos de los beneficios. Esta técnica se presenta en forma de una clase especializada, simple llamado un mixin .

Un mixin funciona como una especie de herencia, pero en lugar de definir una relación “es-un” relación puede ser más exacto decir que se define una “incluye: una” relación. Con una confusión en la que puede escribir un comportamiento que puede incluirse directamente en cualquier número de otras clases.

A continuación, verá un ejemplo corto usando VolumeMixin para dar funcionalidad específica a nuestra objetos 3D, en este caso, un cálculo de volumen:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width

def area(self):
return self.length * self.width

class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)

class VolumeMixin:
def volume(self):
return self.area() * self.height

class Cube(VolumeMixin, Square):
def __init__(self, length):
super().__init__(length)
self.height = length

def face_area(self):
return super().area()

def surface_area(self):
return super().area() * 6

En este ejemplo, el código fue revisado a fin de incluir una llamada mixin VolumeMixin. El mixin se utiliza entonces por Cube y da Cube la capacidad de calcular su volumen, que se muestra a continuación:

>>> cube = Cube(2)
>>> cube.surface_area()
24
>>> cube.volume()
8

Este mixin se puede utilizar de la misma manera en cualquier otra clase que tiene un área definida por ella y para el cual el área de la fórmula * altura devuelve el volumen correcto.

Un super () Crónica

En este tutorial, aprendió el poder de sus clases con super (). Su viaje comenzó con una revisión de la herencia simple y luego mostró cómo llamar a los métodos de superclase fácilmente con super ().

A continuación, aprendió cómo funciona la herencia múltiple en Python, y las técnicas para combinar super () con la herencia múltiple. También ha aprendido acerca de cómo Python resuelve las llamadas a métodos que utilizan el orden de resolución de métodos (MRO), así como la forma de inspeccionar y modificar el MRO para garantizar métodos apropiados son llamados en los momentos apropiados.

Para obtener más información acerca de la programación orientada a objetos en Python y el uso de super (), consulte estos recursos: super documentación

  • Oficial super ()
  • Python () Considerado Súper por Raymond Hettinger
  • Programación orientada a objetos en Python 3

Mira ahora Este tutorial tiene un vídeo relacionado curso creado por el equipo del real Python. Mira que junto con el tutorial escrito para profundizar su comprensión: Supercharge sus clases con Python super ()

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *