Python orientado a objetos: bloques de construcción

En este capítulo, discutiremos los términos orientados a objetos y los conceptos de programación en detalle. La clase es solo una fábrica para una instancia. Esta fábrica contiene el plano que describe cómo hacer las instancias. Una instancia o un objeto se construyen a partir de la clase. En la mayoría de los casos, podemos tener más de una instancia de una clase. Cada instancia tiene un conjunto de atributos y estos atributos se definen en una clase, por lo que se espera que cada instancia de una clase en particular tenga los mismos atributos.

Paquetes de clase: comportamiento y estado

Una clase te permitirá agrupar el comportamiento y el estado de un objeto. Observe el siguiente diagrama para una mejor comprensión:

Los siguientes puntos son dignos de mención cuando se habla de paquetes de clases:

  • La palabra behavior es idéntico a function - es un fragmento de código que hace algo (o implementa un comportamiento)

  • La palabra state es idéntico a variables - es un lugar para almacenar valores dentro de una clase.

  • Cuando afirmamos el comportamiento de una clase y declaramos juntos, significa que una clase empaqueta funciones y variables.

Las clases tienen métodos y atributos

En Python, la creación de un método define el comportamiento de una clase. La palabra método es el nombre OOP que se le da a una función que se define dentro de una clase. Para resumir -

  • Class functions - es sinónimo de methods

  • Class variables - es sinónimo de name attributes.

  • Class - un plano para una instancia con comportamiento exacto.

  • Object - una de las instancias de la clase, realiza la funcionalidad definida en la clase.

  • Type - indica la clase a la que pertenece la instancia

  • Attribute - Cualquier valor de objeto: object.attribute

  • Method - un "atributo invocable" definido en la clase

Observe el siguiente fragmento de código, por ejemplo:

var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN

Creación e instanciación

El siguiente código muestra cómo crear nuestra primera clase y luego su instancia.

class MyClass(object):
   pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)

Aquí hemos creado una clase llamada MyClassy que no hace ninguna tarea. El argumentoobject en MyClass La clase implica la herencia de clases y se discutirá en capítulos posteriores. pass en el código anterior indica que este bloque está vacío, es decir, es una definición de clase vacía.

Creemos una instancia this_obj de MyClass() class e imprimirlo como se muestra -

<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>

Aquí, hemos creado una instancia de MyClass.El código hexadecimal se refiere a la dirección donde se almacena el objeto. Otra instancia apunta a otra dirección.

Ahora definamos una variable dentro de la clase MyClass() y obtenga la variable de la instancia de esa clase como se muestra en el siguiente código:

class MyClass(object):
   var = 9

# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)

# Another instance of MyClass

that_obj = MyClass()
print (that_obj.var)

Salida

Puede observar la siguiente salida cuando ejecuta el código dado anteriormente:

9
9

Como la instancia sabe de qué clase se crea una instancia, cuando se solicita un atributo de una instancia, la instancia busca el atributo y la clase. Esto se llamaattribute lookup.

Métodos de instancia

Una función definida en una clase se llama method.Un método de instancia requiere una instancia para poder llamarlo y no requiere decorador. Al crear un método de instancia, el primer parámetro siempre esself. Aunque podemos llamarlo (self) por cualquier otro nombre, se recomienda usar self, ya que es una convención de nomenclatura.

class MyClass(object):
   var = 9
   def firstM(self):
      print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()

Salida

Puede observar la siguiente salida cuando ejecuta el código dado anteriormente:

9
hello, World

Tenga en cuenta que en el programa anterior, definimos un método con self como argumento. Pero no podemos llamar al método ya que no le hemos declarado ningún argumento.

class MyClass(object):
   def firstM(self):
      print("hello, World")
      print(self)
obj = MyClass()
obj.firstM()
print(obj)

Salida

Puede observar la siguiente salida cuando ejecuta el código dado anteriormente:

hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>

Encapsulamiento

La encapsulación es uno de los fundamentos de la programación orientada a objetos. OOP nos permite ocultar la complejidad del trabajo interno del objeto, lo que es ventajoso para el desarrollador de las siguientes maneras:

  • Simplifica y facilita la comprensión del uso de un objeto sin conocer las partes internas.

  • Cualquier cambio se puede gestionar fácilmente.

La programación orientada a objetos se basa en gran medida en la encapsulación. Los términos encapsulación y abstracción (también llamados ocultación de datos) se utilizan a menudo como sinónimos. Son casi sinónimos, ya que la abstracción se logra mediante encapsulación.

La encapsulación nos proporciona el mecanismo de restringir el acceso a algunos de los componentes del objeto, esto significa que la representación interna de un objeto no se puede ver desde fuera de la definición del objeto. El acceso a estos datos generalmente se logra a través de métodos especiales:Getters y Setters.

Estos datos se almacenan en atributos de instancia y se pueden manipular desde cualquier lugar fuera de la clase. Para protegerlo, solo se debe acceder a esos datos mediante métodos de instancia. No se debe permitir el acceso directo.

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age

zack = MyClass()
zack.setAge(45)
print(zack.getAge())

zack.setAge("Fourty Five")
print(zack.getAge())

Salida

Puede observar la siguiente salida cuando ejecuta el código dado anteriormente:

45
Fourty Five

Los datos deben almacenarse solo si son correctos y válidos, utilizando construcciones de manejo de excepciones. Como podemos ver arriba, no hay restricciones en la entrada del usuario al método setAge (). Puede ser una cadena, un número o una lista. Por lo tanto, debemos verificar el código anterior para asegurarnos de que se almacena correctamente.

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())

Constructor inicial

Los __initEl método __ se llama implícitamente tan pronto como se crea una instancia de un objeto de una clase. Esto inicializará el objeto.

x = MyClass()

La línea de código que se muestra arriba creará una nueva instancia y asignará este objeto a la variable local x.

La operación de instanciación, es decir calling a class object, crea un objeto vacío. A muchas clases les gusta crear objetos con instancias personalizadas para un estado inicial específico. Por lo tanto, una clase puede definir un método especial llamado '__init __ ()' como se muestra:

def __init__(self):
   self.data = []

Python llama a __init__ durante la instanciación para definir un atributo adicional que debería ocurrir cuando se crea una instancia de una clase que puede estar configurando algunos valores iniciales para ese objeto o ejecutando una rutina requerida en la instanciación. Entonces, en este ejemplo, una nueva instancia inicializada se puede obtener mediante:

x = MyClass()

El método __init __ () puede tener uno o varios argumentos para una mayor flexibilidad. Init significa inicialización, ya que inicializa los atributos de la instancia. Se llama constructor de una clase.

class myclass(object):
   def __init__(self,aaa, bbb):
      self.a = aaa
      self.b = bbb

x = myclass(4.5, 3)
print(x.a, x.b)

Salida

4.5 3

Atributos de clase

El atributo definido en la clase se denomina "atributos de clase" y los atributos definidos en la función se denominan "atributos de instancia". Mientras se definen, estos atributos no están prefijados por sí mismos, ya que son propiedad de la clase y no de una instancia en particular.

Los atributos de la clase pueden ser accedidos por la propia clase (className.attributeName) así como por las instancias de la clase (inst.attributeName). Entonces, las instancias tienen acceso tanto al atributo de instancia como a los atributos de clase.

>>> class myclass():
   age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>

Un atributo de clase se puede anular en una instancia, aunque no es un buen método para romper la encapsulación.

Hay una ruta de búsqueda de atributos en Python. El primero es el método definido dentro de la clase y luego la clase superior.

>>> class myclass(object):
   classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.

>>> print(dd.classy)
class value
>>>

Estamos anulando el atributo de clase 'classy' en la instancia dd. Cuando se anula, el intérprete de Python lee el valor anulado. Pero una vez que el nuevo valor se elimina con 'del', el valor anulado ya no está presente en la instancia y, por lo tanto, la búsqueda va un nivel superior y lo obtiene de la clase.

Trabajar con datos de instancia y clase

En esta sección, comprendamos cómo se relacionan los datos de la clase con los datos de la instancia. Podemos almacenar datos en una clase o en una instancia. Cuando diseñamos una clase, decidimos qué datos pertenecen a la instancia y qué datos deben almacenarse en la clase general.

Una instancia puede acceder a los datos de la clase. Si creamos varias instancias, estas instancias pueden acceder a sus valores de atributos individuales, así como a los datos generales de la clase.

Por tanto, los datos de una clase son los datos que se comparten entre todas las instancias. Observe el código que se proporciona a continuación para comprender mejor:

class InstanceCounter(object):
   count = 0 # class attribute, will be accessible to all instances
   def __init__(self, val):
      self.val = val
      InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
   def set_val(self, newval):
      self.val = newval

   def get_val(self):
      return self.val

   def get_count(self):
      return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)

for obj in (a, b, c):
   print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
   print ('count: %s' %(obj.get_count())) # always 3

Salida

val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3

En resumen, los atributos de clase son los mismos para todas las instancias de clase, mientras que los atributos de instancia son particulares para cada instancia. Para dos instancias diferentes, tendremos dos atributos de instancia diferentes.

class myClass:
   class_attribute = 99

   def class_method(self):
      self.instance_attribute = 'I am instance attribute'

print (myClass.__dict__)

Salida

Puede observar la siguiente salida cuando ejecuta el código dado anteriormente:

{'__module__': '__main__', 'class_attribute': 99, 'class_method': 
      
       , '__dict__': 
       
        , '__weakref__': 
        
         , '__doc__': None} 
        
       
      

The instance attribute myClass.__dict__ as shown −

>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}