Saltar a contenido

7. POO

Clase

Una clase es una idea (más o menos abstracta) que se puede utilizar para crear varias encarnaciones; una encarnación de este tipo se denomina objeto.

Para definir una clase de Python,se necesita usar la palabra clave reservada class. Por ejemplo:

  class This_Is_A_Class:
      pass

Herencia, subclase y superclase

Cuando una clase se deriva de otra clase, su relación se denomina herencia. La clase que deriva de la otra clase se denomina subclase. El segundo lado de esta relación se denomina superclase. Una forma de presentar dicha relación es en un diagrama de herencia, donde:

  • Las superclases siempre se presentan encima de sus subclases.

  • Las relaciones entre clases se muestran como flechas dirigidas desde la subclase hacia su superclase.

Objetos

Los objetos están equipados con:

  • Un nombre que los identifica y nos permite distinguirlos.

  • Un conjunto de propiedades (el conjunto puede estar vacío).

  • Un conjunto de métodos (también puede estar vacío).

Para crear un objeto de la clase previamente definida, se necesita usar la clase como si fuera una función. Por ejemplo:

this_is_an_object = This_Is_A_Class()
class ExampleClass:
    def __init__(self, val = 1):
        self.__first = val

    def set_second(self, val = 2):
        self.__second = val


example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)

example_object_2.set_second(3)

example_object_3 = ExampleClass(4)
example_object_3.__third = 5


print(example_object_1.__dict__)
print(example_object_2.__dict__)
print(example_object_3.__dict__)

Métodos

Un método es una función que está dentro de una clase.

un método está obligado a tener al menos un parámetro (no existen métodos sin parámetros; un método puede invocarse sin un argumento, pero no puede declararse sin parámetros).

El primer (o único) parámetro generalmente se denomina self. El nombre self identifica el objeto para el cual se invoca el método.

El parámetro self también se usa para invocar otros métodos desde dentro de la clase.

class Classy:
    def other(self):
        print("otro")

    def method(self):
        print("método")
        self.other()


obj = Classy()
obj.method()

Variables de instancia

Una variable de instancia es una propiedad cuya existencia depende de la creación de un objeto. Cada objeto puede tener un conjunto diferente de variables de instancia.

Diferentes objetos de la misma clase pueden poseer diferentes conjuntos de propiedades.

Si queremos ocultar alguno de los componentes de una clase al mundo exterior, debemos comenzar su nombre con __. Estos componentes se denominan privados.

Dichas propiedades privadas aún son accesibles desde fuera de la clase usando un nombre modificado construido como _ClassName__PrivatePropertyName.

class ExampleClass:
    def __init__(self, val = 1):
        self.__private_value = 2 * val
        self.first = val

    def set_second(self, val):
        self.second = val

example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)

example_object_2.set_second(3)

example_object_3 = ExampleClass(4)
example_object_3.third = 5

print(example_object_1.__dict__)
print(example_object_2.__dict__)
print(example_object_3.__dict__)
class Classy:
    def visible(self):
        print("visible")

    def __hidden(self):
        print("oculto")


obj = Classy()
obj.visible()

try:
    obj.__hidden()
except:
    print("fallido")

obj._Classy__hidden()

Los objetos de Python, cuando se crean, están dotados de un pequeño conjunto de propiedades y métodos predefinidos. Cada objeto los tiene, los quieras o no. Uno de ellos es una variable llamada dict (es un diccionario).

La variable contiene los nombres y valores de todas las propiedades (variables) que el objeto contiene actualmente

Variables de clase

Una variable de clase es una propiedad que existe en una sola copia y se almacena fuera de cualquier objeto.

class ExampleClass:
    counter = 0
    def __init__(self, val = 1):
        self.__first = val
        ExampleClass.counter += 1


example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)
example_object_3 = ExampleClass(4)

print(example_object_1.__dict__, example_object_1.counter)
print(example_object_2.__dict__, example_object_2.counter)
print(example_object_3.__dict__, example_object_3.counter)

Encapsulación

Hacer que todas las variables sean privadas, reduciendo así la superficie de contacto. Acceder a las variables mediante metodos get y set.

La encapsulación se refiere a la agrupación de datos con los métodos que operan en esos datos, o la restricción del acceso directo a algunos de los componentes de un objeto.​ La encapsulación se utiliza para ocultar los valores o el estado de un objeto de datos estructurados dentro de una clase, evitando el acceso directo a ellos por parte de los clientes de una manera que podría exponer detalles de Implementación ocultos o violar la invariancia de estado mantenida por los métodos.

Comprobar la existencia de un atributo hasattr

class ExampleClass:
    def __init__(self, val):
        if val % 2 != 0:
            self.a = 1
        else:
            self.b = 1


example_object = ExampleClass(1)
print(example_object.a)

if hasattr(example_object, 'b'):
    print(example_object.b)

También puede operar en clases

class ExampleClass:
    a = 1
    def __init__(self):
        self.b = 2


example_object = ExampleClass()

print(hasattr(example_object, 'b'))
print(hasattr(example_object, 'a'))
print(hasattr(ExampleClass, 'b'))
print(hasattr(ExampleClass, 'a'))

Constructor

Si se nombra un método de esta manera: init, no será un método regular, será un constructor.

Si una clase tiene un constructor, este se invoca automática e implícitamente cuando se instancia el objeto de la clase.

El constructor:

  • Esta obligado a tener el parámetro self (se configura automáticamente).
  • Pudiera (pero no necesariamente) tener mas parámetros que solo self; si esto sucede, la forma en que se usa el nombre de la clase para crear el objeto debe tener la definición init.
  • Se puede utilizar para configurar el objeto, es decir, inicializa adecuadamente su estado interno, crea variables de instancia, crea instancias de cualquier otro objeto si es necesario, etc.
class Classy:
    def __init__(self, value):
        self.var = value


obj_1 = Classy("objeto")

print(obj_1.var)

Introspección y reflexión

  • Introspección, es la capacidad de un programa para examinar el tipo o las propiedades de un objeto en tiempo de ejecución.
  • Reflexión, es la capacidad de un programa para manipular los valores, propiedades y/o funciones de un objeto en tiempo de ejecución.

Cada clase de Python y cada objeto de Python está pre-equipado con un conjunto de atributos útiles que pueden usarse para examinar sus capacidades.

la propiedad __dict__

class Classy:
    varia = 1
    def __init__(self):
        self.var = 2

    def method(self):
        pass

    def __hidden(self):
        pass


obj = Classy()

print(obj.__dict__)
print(Classy.__dict__)

__name__ → Contiene el nombre de la clase.

Nota:

El atributo __name__ existe solo dentro de las clases no de los objetos.

type() → Encontrar la clase de un objeto en particular, (entre otras cosas) .

__module__es una cadena, también almacena el nombre del módulo que contiene la definición de la clase.

__bases__ es una tupla. La tupla contiene clases (no nombres de clases) que son superclases directas de la clase. El orden es el mismo que el utilizado dentro de la definición de clase.

Cuando Python necesita que alguna clase u objeto deba ser presentado como una cadena (es recomendable colocar el objeto como argumento en la invocación de la función print()), intenta invocar un método llamado __str__() del objeto y emplear la cadena que devuelve.

El método por default __str__() devuelve una cadena fea y poco informativa. Puedes redefinirlo con tu propio método.

class Star:
    def __init__(self, name, galaxy):
        self.name = name
        self.galaxy = galaxy

    def __str__(self):
        return self.name + ' en ' + self.galaxy


sun = Star("Sol", "Vía Láctea")
print(sun)

Nota:

Una clase sin superclases explícitas apunta a object (una clase de Python predefinida) como su antecesor directo.

Herencia

La herencia es una práctica común (en la programación de objetos) de pasar atributos y métodos de la superclase (definida y existente) a una clase recién creada, llamada subclase.

En otras palabras, la herencia es una forma de construir una nueva clase, no desde cero, sino utilizando un repertorio de rasgos ya definido. La nueva clase hereda (y esta es la clave) todo el equipamiento ya existente, pero puedes agregar algo nuevo si es necesario.

Gracias a eso, es posible construir clases más especializadas (más concretas) utilizando algunos conjuntos de reglas y comportamientos generales predefinidos.

class Vehicle:
    pass


class LandVehicle(Vehicle):
    pass


class TrackedVehicle(LandVehicle):
    pass

issubclass(ClassOne, ClassTwo) → devuelve True si ClassOne es una subclase de ClassTwo, y False de lo contrario.

isinstance(objectName, ClassName) → Devuelve True si el objeto es una instancia de la clase, o False de lo contrario.

object_one is object_two → Verifica si dos variables, se refieren al mismo objeto.

class Super:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "Mi nombre es " + self.name + "."


class Sub(Super):
    def __init__(self, name):
        Super.__init__(self, name)


obj = Sub("Andy")

print(obj)

super() → Accede a la superclase sin necesidad de conocer su nombre:

class Super:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "Mi nombre es " + self.name + "."


class Sub(Super):
    def __init__(self, name):
        super().__init__(name)


obj = Sub("Andy")

print(obj)

La función super() crea un contexto en el que no tiene que (además, no debe) pasar el argumento propio al método que se invoca; es por eso que es posible activar el constructor de la superclase utilizando solo un argumento.

Nota:

Puedes usar este mecanismo no solo para invocar al constructor de la superclase, también para obtener acceso a cualquiera de los recursos disponibles dentro de la superclase.

Mas sobre excepciones

El bloque finally siempre se ejecuta (finaliza la ejecución del bloque try-except, de ahí su nombre), sin importar lo que sucedió antes, incluso cuando se genera una excepción, sin importar si esta se ha manejado o no.

El bloque else: de la sentencia try se ejecuta cuando no ha habido ninguna excepción durante la ejecución del try:.

La sintaxis except Exception_Name as exception_object: te permite interceptar un objeto que contiene información sobre una excepción pendiente. La propiedad del objeto llamada args (una tupla) almacena todos los argumentos pasados al constructor del objeto.

def reciprocal(n):
    try:
        n = 1 / n
    except ZeroDivisionError:
        print("División fallida")
        n = None
    else:
        print("Todo salió bien")
    finally:
        print("Es momento de decir adiós")
        return n


print(reciprocal(2))
print(reciprocal(0))
try:
    i = int("¡Hola!")
except Exception as e:
    print(e)
    print(e.__str__())

Todas las excepciones integradas de Python forman una jerarquía de clases. Si lo deseas, puedes extenderlo sin problema.

def print_exception_tree(thisclass, nest = 0):
    if nest > 1:
        print("   |" * (nest - 1), end="")
    if nest > 0:
        print("   +---", end="")

    print(thisclass.__name__)

    for subclass in thisclass.__subclasses__():
        print_exception_tree(subclass, nest + 1)


print_exception_tree(BaseException)
BaseException
   +---Exception
   |   +---TypeError
   |   |   +---FloatOperation
   |   |   +---MultipartConversionError
   |   +---StopAsyncIteration
   |   +---StopIteration
   |   +---ImportError
   |   |   +---ModuleNotFoundError
   |   |   +---ZipImportError
   |   +---OSError
   |   |   +---ConnectionError
   |   |   |   +---BrokenPipeError
   |   |   |   +---ConnectionAbortedError
   |   |   |   +---ConnectionRefusedError
   |   |   |   +---ConnectionResetError
   |   |   |   |   +---RemoteDisconnected
   |   |   +---BlockingIOError
   |   |   +---ChildProcessError
   |   |   +---FileExistsError
   |   |   +---FileNotFoundError
   |   |   +---IsADirectoryError
   |   |   +---NotADirectoryError
   |   |   +---InterruptedError
   |   |   +---PermissionError
   |   |   +---ProcessLookupError
   |   |   +---TimeoutError
   |   |   +---UnsupportedOperation
   |   |   +---ItimerError
   |   |   +---herror
   |   |   +---gaierror
   |   |   +---timeout
   |   |   +---SSLError
   |   |   |   +---SSLCertVerificationError
   |   |   |   +---SSLZeroReturnError
   |   |   |   +---SSLWantReadError
   |   |   |   +---SSLWantWriteError
   |   |   |   +---SSLSyscallError
   |   |   |   +---SSLEOFError
   |   |   +---BadGzipFile
   |   |   +---Error
   |   |   |   +---SameFileError
   |   |   +---SpecialFileError
   |   |   +---ExecError
   |   |   +---ReadError
   |   +---EOFError
   |   +---RuntimeError
   |   |   +---RecursionError
   |   |   +---NotImplementedError
   |   |   +---_DeadlockError
   |   |   +---BrokenBarrierError
   |   |   +---ExtractionError
   |   |   +---VariableError
   |   +---NameError
   |   |   +---UnboundLocalError
   |   +---AttributeError
   |   +---SyntaxError
   |   |   +---IndentationError
   |   |   |   +---TabError
   |   +---LookupError
   |   |   +---IndexError
   |   |   +---KeyError
   |   |   +---CodecRegistryError
   |   +---ValueError
   |   |   +---UnicodeError
   |   |   |   +---UnicodeEncodeError
   |   |   |   +---UnicodeDecodeError
   |   |   |   +---UnicodeTranslateError
   |   |   +---UnsupportedOperation
   |   |   +---JSONDecodeError
   |   |   +---Error
   |   |   +---MessageDefect
   |   |   |   +---NoBoundaryInMultipartDefect
   |   |   |   +---StartBoundaryNotFoundDefect
   |   |   |   +---CloseBoundaryNotFoundDefect
   |   |   |   +---FirstHeaderLineIsContinuationDefect
   |   |   |   +---MisplacedEnvelopeHeaderDefect
   |   |   |   +---MissingHeaderBodySeparatorDefect
   |   |   |   +---MultipartInvariantViolationDefect
   |   |   |   +---InvalidMultipartContentTransferEncodingDefect
   |   |   |   +---UndecodableBytesDefect
   |   |   |   +---InvalidBase64PaddingDefect
   |   |   |   +---InvalidBase64CharactersDefect
   |   |   |   +---InvalidBase64LengthDefect
   |   |   |   +---HeaderDefect
   |   |   |   |   +---InvalidHeaderDefect
   |   |   |   |   +---HeaderMissingRequiredValue
   |   |   |   |   +---NonPrintableDefect
   |   |   |   |   +---ObsoleteHeaderDefect
   |   |   |   |   +---NonASCIILocalPartDefect
   |   |   +---IllegalMonthError
   |   |   +---IllegalWeekdayError
   |   |   +---SSLCertVerificationError
   |   |   +---InvalidFileException
   |   |   +---InvalidVersion
   |   |   +---InvalidSpecifier
   |   |   +---InvalidMarker
   |   |   +---UndefinedComparison
   |   |   +---UndefinedEnvironmentName
   |   |   +---InvalidRequirement
   |   |   +---RequirementParseError
   |   +---AssertionError
   |   +---ArithmeticError
   |   |   +---FloatingPointError
   |   |   +---OverflowError
   |   |   +---ZeroDivisionError
   |   |   |   +---DivisionByZero
   |   |   |   +---DivisionUndefined
   |   |   +---DecimalException
   |   |   |   +---Clamped
   |   |   |   +---Rounded
   |   |   |   |   +---Underflow
   |   |   |   |   +---Overflow
   |   |   |   +---Inexact
   |   |   |   |   +---Underflow
   |   |   |   |   +---Overflow
   |   |   |   +---Subnormal
   |   |   |   |   +---Underflow
   |   |   |   +---DivisionByZero
   |   |   |   +---FloatOperation
   |   |   |   +---InvalidOperation
   |   |   |   |   +---ConversionSyntax
   |   |   |   |   +---DivisionImpossible
   |   |   |   |   +---DivisionUndefined
   |   |   |   |   +---InvalidContext
   |   +---SystemError
   |   |   +---CodecRegistryError
   |   +---ReferenceError
   |   +---MemoryError
   |   +---BufferError
   |   +---Warning
   |   |   +---UserWarning
   |   |   |   +---GetPassWarning
   |   |   +---DeprecationWarning
   |   |   +---PendingDeprecationWarning
   |   |   +---SyntaxWarning
   |   |   +---RuntimeWarning
   |   |   |   +---PEP440Warning
   |   |   +---FutureWarning
   |   |   +---ImportWarning
   |   |   +---UnicodeWarning
   |   |   +---BytesWarning
   |   |   +---ResourceWarning
   |   |   +---PkgResourcesDeprecationWarning
   |   +---Error
   |   +---_OptionError
   |   +---_Error
   |   +---error
   |   +---Verbose
   |   +---TokenError
   |   +---StopTokenizing
   |   +---EndOfBlock
   |   +---Empty
   |   +---Full
   |   +---error
   |   +---_GiveupOnSendfile
   |   +---Incomplete
   |   +---MessageError
   |   |   +---MessageParseError
   |   |   |   +---HeaderParseError
   |   |   |   +---BoundaryError
   |   |   +---MultipartConversionError
   |   |   +---CharsetError
   |   +---Error
   |   +---HTTPException
   |   |   +---NotConnected
   |   |   +---InvalidURL
   |   |   +---UnknownProtocol
   |   |   +---UnknownTransferEncoding
   |   |   +---UnimplementedFileMode
   |   |   +---IncompleteRead
   |   |   +---ImproperConnectionState
   |   |   |   +---CannotSendRequest
   |   |   |   +---CannotSendHeader
   |   |   |   +---ResponseNotReady
   |   |   +---BadStatusLine
   |   |   |   +---RemoteDisconnected
   |   |   +---LineTooLong
   |   +---ExpatError
   |   +---error
   |   +---Error
   |   |   +---ProtocolError
   |   |   +---ResponseError
   |   |   +---Fault
   |   +---Error
   |   +---LZMAError
   |   +---RegistryError
   |   +---_GiveupOnFastCopy
   |   +---ErrorDuringImport
   |   +---BadZipFile
   |   +---LargeZipFile
   |   +---ParseBaseException
   |   |   +---ParseException
   |   |   +---ParseFatalException
   |   |   |   +---ParseSyntaxException
   |   +---RecursiveGrammarException
   |   +---ResolutionError
   |   |   +---VersionConflict
   |   |   |   +---ContextualVersionConflict
   |   |   +---DistributionNotFound
   |   |   +---UnknownExtra
   |   +---error
   |   +---ArgumentError
   |   +---PickleError
   |   |   +---PicklingError
   |   |   +---UnpicklingError
   |   +---_Stop
   |   +---UnableToResolveVariableException
   |   +---InvalidTypeInArgsException
   |   +---SubprocessError
   |   |   +---CalledProcessError
   |   |   +---TimeoutExpired
   +---GeneratorExit
   +---SystemExit
   +---KeyboardInterrupt
   +---DebuggerInitializationError

Definir nuevas excepciones

class PizzaError(Exception):
    def __init__(self, pizza, message):
        Exception.__init__(self, message)
        self.pizza = pizza


class TooMuchCheeseError(PizzaError):
    def __init__(self, pizza, cheese, message):
        PizzaError.__init__(self, pizza, message)
        self.cheese = cheese


def make_pizza(pizza, cheese):
    if pizza not in ['margherita', 'capricciosa', 'calzone']:
        raise PizzaError(pizza, "no hay tal pizza en el menú")
    if cheese > 100:
        raise TooMuchCheeseError(pizza, cheese, "demasiado queso")
    print("¡Pizza lista!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:
    try:
        make_pizza(pz, ch)
    except TooMuchCheeseError as tmce:
        print(tmce, ':', tmce.cheese)
    except PizzaError as pe:
        print(pe, ':', pe.pizza)

Última actualización: 2022-11-08