Python - Orientado a objetos

Python ha sido un lenguaje orientado a objetos desde que existió. Debido a esto, crear y usar clases y objetos es francamente fácil. Este capítulo le ayuda a convertirse en un experto en el uso del soporte de programación orientada a objetos de Python.

Si no tiene experiencia previa con la programación orientada a objetos (OO), es posible que desee consultar un curso introductorio o al menos un tutorial de algún tipo para que comprenda los conceptos básicos.

Sin embargo, aquí hay una pequeña introducción de la programación orientada a objetos (OOP) para llevarlo a la velocidad:

Descripción general de la terminología de OOP

  • Class- Un prototipo definido por el usuario para un objeto que define un conjunto de atributos que caracterizan a cualquier objeto de la clase. Los atributos son miembros de datos (variables de clase y variables de instancia) y métodos, a los que se accede mediante notación de puntos.

  • Class variable- Una variable que es compartida por todas las instancias de una clase. Las variables de clase se definen dentro de una clase pero fuera de cualquiera de los métodos de la clase. Las variables de clase no se utilizan con tanta frecuencia como las variables de instancia.

  • Data member - Una variable de clase o variable de instancia que contiene datos asociados con una clase y sus objetos.

  • Function overloading- La asignación de más de un comportamiento a una función determinada. La operación realizada varía según los tipos de objetos o argumentos involucrados.

  • Instance variable - Una variable que se define dentro de un método y pertenece solo a la instancia actual de una clase.

  • Inheritance - La transferencia de las características de una clase a otras clases que se deriven de ella.

  • Instance- Un objeto individual de una determinada clase. Un objeto obj que pertenece a una clase Circle, por ejemplo, es una instancia de la clase Circle.

  • Instantiation - La creación de una instancia de una clase.

  • Method - Un tipo especial de función que se define en una definición de clase.

  • Object- Una instancia única de una estructura de datos definida por su clase. Un objeto comprende tanto miembros de datos (variables de clase y variables de instancia) como métodos.

  • Operator overloading - La asignación de más de una función a un operador en particular.

Creando clases

La declaración de clase crea una nueva definición de clase. El nombre de la clase sigue inmediatamente a la clase de palabra clave seguida de dos puntos de la siguiente manera:

class ClassName:
   'Optional class documentation string'
   class_suite
  • La clase tiene una cadena de documentación, a la que se puede acceder a través de ClassName .__ doc__ .

  • El class_suite se compone de todos los componentes que definen declaraciones miembros de la clase, atributos de datos y funciones.

Ejemplo

A continuación se muestra el ejemplo de una clase Python simple:

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary
  • La variable empCount es una variable de clase cuyo valor se comparte entre todas las instancias de esta clase. Se puede acceder a esto como Employee.empCount desde dentro o fuera de la clase.

  • El primer método __init __ () es un método especial, que se llama constructor de clase o método de inicialización que Python llama cuando crea una nueva instancia de esta clase.

  • Declaras otros métodos de clase como funciones normales con la excepción de que el primer argumento de cada método es self . Python agrega el argumento self a la lista por usted; no es necesario incluirlo cuando llame a los métodos.

Crear objetos de instancia

Para crear instancias de una clase, llama a la clase usando el nombre de la clase y pasa los argumentos que acepta su método __init__ .

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)

Acceder a los atributos

Puede acceder a los atributos del objeto utilizando el operador de puntos con objeto. Se accedería a la variable de clase usando el nombre de clase de la siguiente manera:

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Ahora, juntando todos los conceptos -

#!/usr/bin/python

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Cuando se ejecuta el código anterior, produce el siguiente resultado:

Name :  Zara ,Salary:  2000
Name :  Manni ,Salary:  5000
Total Employee 2

Puede agregar, eliminar o modificar atributos de clases y objetos en cualquier momento:

emp1.age = 7  # Add an 'age' attribute.
emp1.age = 8  # Modify 'age' attribute.
del emp1.age  # Delete 'age' attribute.

En lugar de utilizar las declaraciones normales para acceder a los atributos, puede utilizar las siguientes funciones:

  • los getattr(obj, name[, default]) - para acceder al atributo de objeto.

  • los hasattr(obj,name) - para comprobar si un atributo existe o no.

  • los setattr(obj,name,value)- para establecer un atributo. Si el atributo no existe, se creará.

  • los delattr(obj, name) - para eliminar un atributo.

hasattr(emp1, 'age')    # Returns true if 'age' attribute exists
getattr(emp1, 'age')    # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age')    # Delete attribute 'age'

Atributos de clase integrados

Cada clase de Python sigue los atributos incorporados y se puede acceder a ellos usando el operador de punto como cualquier otro atributo:

  • __dict__ - Diccionario que contiene el espacio de nombres de la clase.

  • __doc__ - Cadena de documentación de clase o ninguna, si no está definida.

  • __name__ - Nombre de la clase.

  • __module__- Nombre del módulo en el que se define la clase. Este atributo es "__main__" en modo interactivo.

  • __bases__ - Una tupla posiblemente vacía que contiene las clases base, en el orden en que aparecen en la lista de clases base.

Para la clase anterior, intentemos acceder a todos estos atributos:

#!/usr/bin/python

class Employee:
   'Common base class for all employees'
   empCount = 0

   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary

print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__

Cuando se ejecuta el código anterior, produce el siguiente resultado:

Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2, 
'displayEmployee': <function displayEmployee at 0xb7c8441c>, 
'__doc__': 'Common base class for all employees', 
'__init__': <function __init__ at 0xb7c846bc>}

Destrucción de objetos (recolección de basura)

Python elimina los objetos innecesarios (tipos integrados o instancias de clases) automáticamente para liberar espacio en la memoria. El proceso mediante el cual Python recupera periódicamente bloques de memoria que ya no están en uso se denomina recolección de basura.

El recolector de basura de Python se ejecuta durante la ejecución del programa y se activa cuando el recuento de referencias de un objeto llega a cero. El recuento de referencias de un objeto cambia a medida que cambia el número de alias que apuntan a él.

El recuento de referencias de un objeto aumenta cuando se le asigna un nuevo nombre o se coloca en un contenedor (lista, tupla o diccionario). El recuento de referencias del objeto disminuye cuando se elimina con del , su referencia se reasigna o su referencia sale del alcance. Cuando el recuento de referencias de un objeto llega a cero, Python lo recopila automáticamente.

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c = [b]     # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c[0] = -1   # Decrease ref. count  of <40>

Normalmente no se dará cuenta cuando el recolector de basura destruya una instancia huérfana y reclame su espacio. Pero una clase puede implementar el método especial __del __ () , llamado destructor, que se invoca cuando la instancia está a punto de ser destruida. Este método puede usarse para limpiar cualquier recurso que no sea de memoria usado por una instancia.

Ejemplo

Este destructor __del __ () imprime el nombre de clase de una instancia que está a punto de ser destruida -

#!/usr/bin/python

class Point:
   def __init__( self, x=0, y=0):
      self.x = x
      self.y = y
   def __del__(self):
      class_name = self.__class__.__name__
      print class_name, "destroyed"

pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3

Cuando se ejecuta el código anterior, produce el siguiente resultado:

3083401324 3083401324 3083401324
Point destroyed

Note- Idealmente, debería definir sus clases en un archivo separado, luego debería importarlas en su archivo de programa principal usando la declaración de importación .

Herencia de clase

En lugar de comenzar desde cero, puede crear una clase derivándola de una clase preexistente enumerando la clase principal entre paréntesis después del nombre de la nueva clase.

La clase secundaria hereda los atributos de su clase principal y puede usar esos atributos como si estuvieran definidos en la clase secundaria. Una clase secundaria también puede anular los métodos y miembros de datos del padre.

Sintaxis

Las clases derivadas se declaran de forma muy similar a su clase padre; sin embargo, se proporciona una lista de clases base para heredar después del nombre de la clase:

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

Ejemplo

#!/usr/bin/python

class Parent:        # define parent class
   parentAttr = 100
   def __init__(self):
      print "Calling parent constructor"

   def parentMethod(self):
      print 'Calling parent method'

   def setAttr(self, attr):
      Parent.parentAttr = attr

   def getAttr(self):
      print "Parent attribute :", Parent.parentAttr

class Child(Parent): # define child class
   def __init__(self):
      print "Calling child constructor"

   def childMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.childMethod()      # child calls its method
c.parentMethod()     # calls parent's method
c.setAttr(200)       # again call parent's method
c.getAttr()          # again call parent's method

Cuando se ejecuta el código anterior, produce el siguiente resultado:

Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200

De manera similar, puede conducir una clase desde varias clases principales de la siguiente manera:

class A:        # define your class A
.....

class B:         # define your class B
.....

class C(A, B):   # subclass of A and B
.....

Puede usar las funciones issubclass () o isinstance () para verificar las relaciones de dos clases e instancias.

  • los issubclass(sub, sup) La función booleana devuelve verdadero si la subclase dada sub es de hecho una subclase de la superclase sup.

  • los isinstance(obj, Class)La función booleana devuelve verdadero si obj es una instancia de la clase Class o es una instancia de una subclase de Class

Métodos primordiales

Siempre puedes anular los métodos de tu clase principal. Una razón para anular los métodos de los padres es porque es posible que desee una funcionalidad especial o diferente en su subclase.

Ejemplo

#!/usr/bin/python

class Parent:        # define parent class
   def myMethod(self):
      print 'Calling parent method'

class Child(Parent): # define child class
   def myMethod(self):
      print 'Calling child method'

c = Child()          # instance of child
c.myMethod()         # child calls overridden method

Cuando se ejecuta el código anterior, produce el siguiente resultado:

Calling child method

Métodos de sobrecarga de bases

La siguiente tabla enumera algunas funciones genéricas que puede anular en sus propias clases:

No Señor. Método, descripción y llamada de muestra
1

__init__ ( self [,args...] )

Constructor (con argumentos opcionales)

Llamada de muestra: obj = className (args)

2

__del__( self )

Destructor, elimina un objeto

Ejemplo de llamada: del obj

3

__repr__( self )

Representación de cadena evaluable

Llamada de muestra: repr (obj)

4

__str__( self )

Representación de cadena imprimible

Llamada de muestra: str (obj)

5

__cmp__ ( self, x )

Comparación de objetos

Llamada de muestra: cmp (obj, x)

Operadores de sobrecarga

Suponga que ha creado una clase Vector para representar vectores bidimensionales, ¿qué sucede cuando usa el operador más para agregarlos? Lo más probable es que Python te grite.

Sin embargo, podría definir el método __add__ en su clase para realizar la suma de vectores y luego el operador más se comportaría según las expectativas:

Ejemplo

#!/usr/bin/python

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b

   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2

Cuando se ejecuta el código anterior, produce el siguiente resultado:

Vector(7,8)

Ocultar datos

Los atributos de un objeto pueden ser visibles o no fuera de la definición de clase. Debe nombrar los atributos con un prefijo de subrayado doble, y esos atributos no serán directamente visibles para los forasteros.

Ejemplo

#!/usr/bin/python

class JustCounter:
   __secretCount = 0
  
   def count(self):
      self.__secretCount += 1
      print self.__secretCount

counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount

Cuando se ejecuta el código anterior, produce el siguiente resultado:

1
2
Traceback (most recent call last):
   File "test.py", line 12, in <module>
      print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'

Python protege a esos miembros cambiando internamente el nombre para incluir el nombre de la clase. Puede acceder a atributos como object._className__attrName . Si reemplaza su última línea de la siguiente manera, entonces funciona para usted:

.........................
print counter._JustCounter__secretCount

Cuando se ejecuta el código anterior, produce el siguiente resultado:

1
2
2