Introducción a la programación orientada a objetos en Python

Aprende a escribir tus propias clases en Python. Define una clase con un constructor y atributos, crea objetos a partir de ella y dales métodos que usen y modifiquen sus datos de forma segura. Haz que los objetos se muestren con claridad y se comparen con ==, se ordenen y se combinen con + mediante métodos especiales, construye programas en los que los objetos contengan otros objetos y los llamen, y valora cuándo una clase aporta más claridad que las funciones y los diccionarios.

PrincipianteUnidad 10 de 10 del curso Curso de Introducción a la programación con Python

Lo que aprenderás

  1. Descubre que cada valor de Python es un objeto con sus propios datos y métodos, y después escribe tu propia clase y crea objetos a partir de ella.
    • Reconoce los objetos que ya usas
      Describe cadenas, listas y diccionarios como objetos que tienen un tipo, contienen datos y ofrecen métodos que llamas con notación de punto.
    • Define una clase y crea instancias
      Escribe una sentencia class, crea varios objetos independientes a partir de ella y asigna y lee sus atributos con notación de punto.
  2. Usa un constructor para dar a cada objeto nuevo los atributos que necesita, elige entre atributos de instancia y de clase e impide que se creen objetos no válidos.
    • Escribe un constructor con __init__ y self
      Define __init__ para que cada objeto nuevo comience con sus propios atributos, tomados de los argumentos o establecidos con valores iniciales.
    • Elige entre atributos de instancia y atributos de clase
      Decide si un valor pertenece a cada objeto o a la clase en su conjunto y predice cómo encuentra y modifica Python cada tipo de atributo.
    • Mantén los objetos en un estado válido desde el principio
      Comprueba los argumentos que recibe un constructor, lanza una excepción clara para los valores incorrectos y mantén las reglas que dan sentido al objeto.
  3. Escribe métodos que usen y modifiquen los datos de un objeto y protege esos datos de cambios que dejarían el objeto en un estado no válido.
    • Define y llama a métodos de instancia
      Escribe métodos que trabajen con self, llámalos sobre objetos y devuelve resultados en lugar de mostrarlos.
    • Modifica el estado de un objeto con métodos
      Escribe métodos que actualicen atributos manteniendo el objeto en un estado válido y predice cuándo un cambio será visible a través de otros nombres del mismo objeto.
    • Protege atributos con convenciones de nombres y propiedades
      Marca los atributos como internos con un guion bajo y usa propiedades para comprobar valores nuevos o calcular valores que se mantengan actualizados.
  4. Escribe métodos especiales que permitan a tus objetos mostrar texto legible, compararse con == y < y funcionar con operadores y funciones incorporadas.
    • Define representaciones en cadena legibles para los objetos
      Escribe __str__ y __repr__ para que los objetos muestren texto útil al visualizarlos, inspeccionarlos o almacenarlos en una lista.
    • Compara y combina objetos con operadores
      Define __eq__, __lt__, __add__ y __len__ para que tus objetos funcionen con ==, la ordenación, + y len(), y decide cuándo un operador resulta más claro que un método con nombre.
  5. Combina objetos para que cada uno gestione sus propios datos, sigue la ejecución de programas donde los objetos se llaman entre sí y decide cuándo una clase mejora la claridad de un programa.
    • Construye objetos que contengan otros objetos
      Almacena objetos como atributos o en listas dentro de otro objeto y escribe métodos que trabajen con toda la colección.
    • Haz que los objetos interactúen mediante métodos
      Pasa objetos a métodos y funciones, devuelve objetos nuevos y sigue la ejecución de un programa breve en el que colaboran varios objetos.
    • Decide cuándo una clase mejora la claridad de un programa
      Valora cuándo resulta útil reunir datos y comportamiento en una clase, planifica una clase pequeña a partir de una descripción y reescribe código basado en diccionarios como una clase.

Guía de estudio de Programación orientada a objetos en Python

5 capítulos

1

Comprende los objetos y define tu primera clase

Los objetos ya aparecen en cada programa de Python que escribes: una cadena tiene métodos, una lista cambia su contenido y un diccionario responde a consultas por clave. Interpretar esas operaciones conocidas como comportamiento de objetos te da un punto de partida para crear tus propias clases. Aprende cómo afectan a un programa los tipos, la identidad y las referencias compartidas; después define una clase pequeña y da a cada instancia sus propios atributos.

1.1 Reconoce los objetos que ya usas

Ya has usado objetos siempre que has trabajado con un número, una cadena, una lista o un diccionario. Un objeto tiene un tipo, un valor o estado y operaciones que admite. Incluso las funciones y None son objetos. Escribir tu propia clase amplía este sistema que ya conoces.

Averigua el tipo y los métodos de un objeto

type("hello") devuelve <class 'str'>; type([1, 2]) devuelve <class 'list'>. Las palabras tipo y clase describen aquí la misma función. isinstance(3.5, float) es True, mientras que isinstance("3.5", float) es False: el segundo valor es texto, aunque parezca numérico.

Un método es una función que proporciona un tipo. En "ada lovelace".title(), la cadena anterior al punto es el objeto, title es el método y los paréntesis lo llaman. El resultado es "Ada Lovelace". dir(value) enumera los nombres que ofrece un objeto; nombres como __len__ son métodos especiales que conectan los objetos con operaciones de Python como len(value).

Sigue el resultado y el original

Una llamada a método puede devolver un resultado, modificar un objeto o hacer ambas cosas. Comprueba el comportamiento del método antes de decidir qué asignar.

word = "level"
print(word.count("l"))  # 2
print(word.upper())     # LEVEL
print(word)             # level

nums = [3, 1, 2]
result = nums.sort()
print(nums)             # [1, 2, 3]
print(result)           # None

Las cadenas son inmutables: su contenido no puede cambiar. upper() devuelve otra cadena, por lo que word conserva su valor original. Para guardar el resultado en mayúsculas con ese nombre, escribe word = word.upper().

Las listas son mutables. sort() modifica la propia lista y devuelve None. Escribir nums = nums.sort() pierde la referencia a la lista que contenía nums y asigna None. Usa nums.sort() en su propia línea o nums = sorted(nums) para obtener una lista nueva ordenada. Para un diccionario, {"a": 1}.get("b", 0) devuelve 0 porque falta la clave solicitada.

Los paréntesis también importan. word.upper es un objeto método; word.upper() lo ejecuta. Muchos objetos pueden ofrecer el mismo comportamiento: "cat".upper() y "dog".upper() usan un mismo tipo de método con distintos datos de cadena.

Distingue igualdad e identidad

La igualdad comprueba si los valores coinciden. La identidad comprueba si dos nombres se refieren a un mismo objeto.

a = [1, 2]
b = [1, 2]
c = a
print(a == b)  # True
print(a is b)  # False
print(c is a)  # True
c.append(3)
print(a)       # [1, 2, 3]
print(b)       # [1, 2]

a y b empiezan con contenidos iguales, pero son listas independientes. c = a crea un alias, otro nombre para la misma lista. Añadir un elemento a través de c modifica el objeto al que también se refiere a. No modifica la lista independiente que contiene b. Al seguir la ejecución de un programa, dibuja una caja por objeto y una flecha desde cada nombre hasta su caja.

Puntos clave

  • Cada valor de Python es un objeto con un tipo, un estado y operaciones que admite.
  • Llama a un método con paréntesis; una referencia al método por sí sola no lo ejecuta.
  • Comprueba si un método devuelve un valor nuevo o modifica el objeto existente.
  • == compara valores; is compara la identidad.
  • La asignación puede dar a dos nombres referencias al mismo objeto mutable.

1.2 Define una clase y crea instancias

Una clase define un tipo que puedes usar en tu propio programa. Cada objeto creado a partir de ella es una instancia. Una clase describe el tipo de cosa; las instancias contienen los datos de cosas concretas. Un formulario en blanco y sus copias rellenadas ofrecen una comparación útil.

Escribe una clase y llámala

Una sentencia class tiene la palabra reservada class, un nombre, dos puntos y un cuerpo con sangría. pass proporciona un cuerpo vacío mientras pruebas la estructura.

class Book:
    pass

b1 = Book()
b2 = Book()
print(b1 is b2)           # False
print(isinstance(b1, Book))  # True

Cada llamada Book() crea una instancia independiente. En un archivo de programa, type(b1) muestra <class '__main__.Book'>. La sentencia class debe ejecutarse antes de que el código intente usar Book. También puedes definir una clase en un módulo e importarla con from library import Book, y después crear instancias en el programa que la usa.

Sigue las convenciones de nombres de Python: Book, BankAccount y ShoppingCart usan CapWords, con cada palabra iniciada en mayúscula. Las variables y los métodos usan palabras en minúsculas separadas por guiones bajos, como book_count y add_item. Usa cuatro espacios por cada nivel de sangría.

Almacena datos en cada instancia

Un atributo es un valor con nombre al que se accede con un punto. Asignar un atributo lo almacena; leer el mismo nombre lo recupera.

b1.title = "Dune"
b2.title = "Emma"
b1.pages = 412
print(b1.title)  # Dune
print(b2.title)  # Emma
print(b1.pages)  # 412

Los datos pertenecen a la instancia seleccionada. Dar a b1 un atributo pages no se lo da a b2. Leer ahora b2.pages lanza AttributeError, porque ese objeto no tiene un atributo llamado pages.

Las asignaciones pueden crear nombres nuevos sin comprobar qué querías hacer. Después de b1.titel = "X", el b1.title original sigue conteniendo "Dune"; el objeto ha adquirido un segundo atributo con un nombre mal escrito. Si parece que un valor no cambia, compara cómo está escrito donde se asigna y donde se lee.

Distingue la clase de sus instancias
ExpresiónSignificado
BookEl objeto clase, que define el tipo
Book()Una llamada que crea una instancia
b1.titleEl título almacenado en este libro
Book.titleUn atributo solicitado a la propia clase

b = Book da otro nombre a la clase. No crea un libro. Si los títulos solo se han asignado en las instancias, solicitar Book.title lanza AttributeError; no hay un único título compartido por el tipo.

Da a cada objeto un inicio fiable

Es posible asignar atributos desde fuera, pero eso deja la inicialización repartida por el programa. Un libro podría recibir un título y un número de páginas, mientras que otro solo recibe un título. El código posterior no puede dar por seguro que ambos tengan pages.

Un método de inicialización llamado __init__ coloca las asignaciones iniciales dentro de la clase. Cada llamada de creación sigue entonces el mismo proceso de inicialización. La clase puede exigir un título y crear otros atributos con valores iniciales adecuados, para que el código que la llama no tenga que recordar una secuencia aparte de asignaciones.

Puntos clave

  • Una clase define un tipo; una instancia es un objeto creado a partir de ella.
  • Usa ClassName() para crear una instancia y la notación de punto para acceder a sus atributos.
  • Cada instancia puede contener datos distintos bajo los mismos nombres de atributo.
  • Un atributo ausente lanza AttributeError; una asignación mal escrita puede crear un atributo nuevo.
2

Inicializa objetos con constructores y atributos

Un objeto necesita un estado inicial fiable antes de que sus métodos puedan realizar tareas útiles. La inicialización conecta los argumentos de una llamada a clase con los atributos almacenados en la instancia nueva. Elige qué valores pertenecen a un objeto y cuáles a la clase, y después valida los datos de entrada antes de aceptarlos. Seguir la búsqueda de atributos y los fallos de inicialización te ayudará a detectar datos compartidos por accidente y datos ausentes.

2.1 Escribe un constructor con __init__ y self

Cada cuenta recién creada necesita un titular y un saldo inicial. Colocar esa inicialización en __init__ da a cada objeto los atributos que esperan los métodos posteriores. Python crea primero la instancia y después llama a __init__ para inicializarla.

Conecta los argumentos con los atributos
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        self.transactions = []

acct = BankAccount("Maya", 50)
other = BankAccount("Leo")

En la primera llamada, Python pasa la cuenta nueva como self, "Maya" como owner y 50 como balance. Las asignaciones hacen que acct.owner sea igual a "Maya" y acct.balance sea igual a 50. La segunda llamada usa el saldo predeterminado, por lo que other.balance es 0.

self es el parámetro que recibe el objeto actual. Es una convención, no una palabra reservada de Python. Usa este nombre para que quien lea el código reconozca el objeto enseguida. El código que realiza la llamada lo omite: BankAccount("Maya", 50) proporciona dos argumentos, no tres.

Distingue los nombres locales de los datos almacenados

En self.owner = owner, el nombre de la derecha es un parámetro local. El nombre de la izquierda es un atributo almacenado en el objeto. Cuando termina la inicialización, otros métodos pueden seguir usando self.owner.

Escribir balance = balance solo reasigna el nombre local. No crea ningún atributo, por lo que leer acct.balance después falla. Pon self. delante de cada atributo que deba permanecer como parte del objeto.

Planifica la inicialización enumerando lo que necesita cada instancia. Los valores obligatorios se convierten en parámetros después de self; los opcionales pueden tener valores predeterminados. Los valores que siempre comienzan igual pueden asignarse en el cuerpo, como self.visits = 0. Una lista creada mediante self.transactions = [] se crea de nuevo en cada llamada.

Evita una lista predeterminada compartida

Un argumento predeterminado se evalúa cuando Python define el método. Con def __init__(self, items=[]), las llamadas que omiten items reciben la misma lista. Si el inicializador almacena esa lista directamente, modificar los elementos de un objeto cambia los elementos que ve otro.

Usa None como valor predeterminado y crea una lista nueva durante cada llamada:

class Cart:
    def __init__(self, items=None):
        self.items = [] if items is None else list(items)

a = Cart()
b = Cart()
a.items.append("pen")
print(b.items)  # []

list(items) también crea una lista externa nueva cuando el código que realiza la llamada proporciona una. Los objetos de esa lista siguen siendo referencias compartidas; crear la lista no recrea cada elemento.

Interpreta los errores de inicialización

A BankAccount() le falta el argumento obligatorio owner y lanza TypeError. Compara la llamada con los parámetros que aparecen después de self.

El nombre __init__ tiene dos guiones bajos a cada lado. _init_ es un nombre de método normal y no realizará esta inicialización automáticamente. __init__ debe devolver None, así que normalmente se omite la sentencia return. Devolver un saldo lanza TypeError; la propia llamada a la clase proporciona la instancia nueva al código que la llama.

Puntos clave

  • __init__ inicializa la instancia después de que Python la cree.
  • Python proporciona self; el código que realiza la llamada proporciona los demás argumentos.
  • self.name = name almacena un parámetro como atributo de instancia.
  • Crea las listas de cada instancia dentro de la inicialización; usa None en lugar de un valor predeterminado mutable.
  • __init__ debe devolver None.

2.2 Elige entre atributos de instancia y atributos de clase

Algunos valores describen un objeto; otros describen el tipo en su conjunto. El tamaño de una pizza pertenece a esa pizza. Un límite de ingredientes o un recuento de pizzas creadas puede pertenecer a la clase. El lugar donde se realiza una asignación determina qué tipo de atributo crea.

Coloca el valor en el lugar adecuado
class Pizza:
    MAX_TOPPINGS = 5
    count = 0

    def __init__(self, size):
        self.size = size
        self.toppings = []
        Pizza.count += 1

p1 = Pizza("large")
p2 = Pizza("small")

size y toppings son atributos de instancia, asignados en self. Cada pizza tiene sus propios valores. MAX_TOPPINGS y count son atributos de clase, asignados en el cuerpo de la clase fuera de un método. Las mayúsculas indican que un valor se considera una constante; no impiden su reasignación.

Después de estas llamadas, Pizza.count es 2. Cada inicializador actualiza el mismo atributo de clase. p1.size es "large", mientras que p2.size es "small".

Sigue la búsqueda y la ocultación de nombres

Para atributos de datos normales como estos, leer p1.MAX_TOPPINGS comprueba primero la instancia y después la clase. Como no hay ningún valor almacenado en p1, Python encuentra Pizza.MAX_TOPPINGS, que es 5.

Asignar a través de una instancia tiene un efecto distinto:

p1.MAX_TOPPINGS = 8
Pizza.MAX_TOPPINGS = 6
print(p1.MAX_TOPPINGS)     # 8
print(p2.MAX_TOPPINGS)     # 6
print(Pizza.MAX_TOPPINGS)  # 6

La primera línea crea un atributo de instancia en p1. Este oculta el valor de clase para ese objeto. Cambiar después el valor de clase llega a p2, que sigue leyendo de la clase, pero no sustituye el valor propio de p1.

AsignaciónQué cambia
self.size = sizeLos datos de la instancia actual
Pizza.count += 1El contador compartido de la clase
p1.MAX_TOPPINGS = 8Un valor de instancia que oculta el valor de clase
Vigila dos formas de compartir datos por accidente

Si el inicializador usara self.count += 1, Python leería el 0 de la clase, sumaría uno y almacenaría un atributo de instancia. Cada pizza nueva recibiría su propio count de 1, mientras que Pizza.count seguiría siendo 0. Usa el nombre de la clase al actualizar un contador de toda la clase.

Una lista en el cuerpo de la clase causa un problema distinto. Con toppings = [], todas las instancias que no tienen un atributo de ingredientes independiente encuentran la misma lista. p1.toppings.append("olives") modifica esa lista compartida; no asigna un atributo de instancia nuevo. Pon self.toppings = [] en __init__ cuando cada pizza necesite sus propios ingredientes.

Inspecciona los datos propios de un objeto

Para el p2 original, vars(p2) da {'size': 'small', 'toppings': []}. Los valores de clase no aparecen porque están en Pizza. Esto resulta útil al depurar una búsqueda inesperada: comprueba si la instancia tiene su propio valor y después inspecciona el valor de clase. Un ajuste compartido pertenece a la clase; un nombre, tamaño, saldo o colección personal pertenece a la instancia.

Puntos clave

  • Los atributos de instancia contienen datos de un objeto; los de clase contienen ajustes compartidos o datos de toda la clase.
  • Un atributo de datos de instancia puede ocultar un atributo de clase del mismo nombre.
  • Actualiza un contador compartido a través del nombre de la clase.
  • Modificar una lista de clase cambia la única lista compartida.
  • vars(obj) muestra sus atributos propios almacenados, no los atributos de clase que puede encontrar.

2.3 Mantén los objetos en un estado válido desde el principio

Un objeto debe comenzar en un estado que sus métodos puedan usar. Un rectángulo con anchura negativa no puede representar la figura prevista. Una regla que debe seguir siendo verdadera para todos los objetos de una clase es un invariante. La validación comprueba esas reglas antes de aceptar datos.

Comprueba antes de almacenar
class Rectangle:
    def __init__(self, width, height):
        if width <= 0:
            raise ValueError("width must be positive")
        if height <= 0:
            raise ValueError("height must be positive")
        self.width = width
        self.height = height

Rectangle(3, 4) supera ambas comprobaciones y devuelve un objeto inicializado. Rectangle(-2, 4) lanza ValueError en la primera comprobación. Las asignaciones posteriores nunca se ejecutan. Si el programa no captura esa excepción, la ejecución se detiene ahí.

Supón que r ya se refiere a un rectángulo válido. En r = Rectangle(-2, 4), la llamada de la derecha falla antes de la asignación. r conserva su valor anterior. Si r no tenía valor antes de la llamada fallida, la asignación no crea ninguno.

Elige una excepción útil
ExcepciónProblema que describe
ValueErrorUn tipo de entrada aceptable tiene un valor inaceptable
TypeErrorLa entrada tiene un tipo incorrecto para la operación

Una anchura numérica negativa es un error de valor. Proporcionar texto donde se requiere un número es un error de tipo. La comparación con 0 de este ejemplo ya lanza TypeError para texto. Una clase también puede comprobar un tipo explícitamente con isinstance cuando necesita un mensaje más claro.

Escribe mensajes que indiquen la regla, como "width must be positive". Un mensaje que solo diga "bad input" obliga al código que realiza la llamada a adivinar qué valor debe corregirse.

Deja que el código que realiza la llamada gestione la recuperación
while True:
    try:
        width = float(input("Width: "))
        room = Rectangle(width, 3)
        break
    except ValueError as error:
        print("Try again:", error)

Introducir abc falla en float; introducir -2 supera la conversión, pero falla en Rectangle. Ambos lanzan ValueError, así que este bucle gestiona los dos casos. La clase establece su regla de datos, mientras que el código que realiza la llamada decide volver a preguntar.

Mostrar una advertencia dentro de __init__ no rechaza la llamada. Si la ejecución continúa, el programa puede recibir un objeto no válido y fallar después al usarlo. Lanzar una excepción detiene la inicialización fallida donde se produce el problema.

Limpia los datos de forma deliberada y mantén la regla

Una limpieza documentada puede ser útil. self.name = name.strip() elimina los espacios de los extremos; valida el nombre limpio si no se permiten nombres en blanco. self.items = list(items) crea una lista externa independiente, de modo que añadir elementos a la lista del código que realiza la llamada no los añade a esta. Cambiar una anchura no válida por su valor absoluto alteraría la intención del código que realiza la llamada, así que necesita una regla de diseño explícita.

Las comprobaciones de inicialización se ejecutan al crear el objeto. Una asignación simple como r.width = -5 sigue saltándoselas después. Los métodos que cambian una anchura deben comprobarla de nuevo. Una propiedad puede incluir una comprobación en la propia asignación del atributo, lo que permite ejecutar la misma regla durante la inicialización y en actualizaciones posteriores.

Puntos clave

  • Un invariante es una regla que debe cumplir el estado válido de un objeto.
  • Valida antes de aceptar datos y lanza una excepción clara si hay un fallo.
  • Una llamada de creación fallida no asigna un objeto nuevo al nombre usado por el código que la llama.
  • Las comprobaciones del constructor también necesitan apoyo en los métodos o propiedades posteriores que cambian el estado.
3

Define el comportamiento de los objetos con métodos de instancia

Los métodos convierten los datos almacenados en comportamiento útil. Un rectángulo puede calcular su área y una cuenta puede actualizar su saldo mientras registra una transacción. Sigue cómo pasa Python el objeto receptor a cada método y después distingue un resultado devuelto de un cambio de estado. Las propiedades permiten que una clase mantenga un acceso legible a los atributos mientras comprueba asignaciones o calcula valores cuando se solicitan.

3.1 Define y llama a métodos de instancia

Un método de instancia es una función definida dentro de una clase que trabaja con el objeto pasado como self. El objeto proporciona sus datos almacenados; los parámetros adicionales proporcionan cualquier otra información necesaria para la acción.

Lee los atributos a través de self

Añade estos métodos dentro de la clase Rectangle con validación, al mismo nivel de sangría que __init__:

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

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

    def is_square(self):
        return self.width == self.height

Con r = Rectangle(3, 4), r.area() devuelve 12, r.perimeter() devuelve 14 y r.is_square() devuelve False. Cada método lee los atributos del mismo objeto. Un segundo rectángulo usa las mismas definiciones de métodos con su propia anchura y altura.

Dentro de un método, self.width nombra un atributo. width por sí solo nombra una variable local u otra variable accesible; Python no la busca automáticamente en el objeto actual. Si no existe tal variable, return width * height lanza NameError. Escribir self. deja explícito a qué objeto pertenecen los datos.

Sigue la llamada

Para esta clase, r.area() funciona como Rectangle.area(r). Python encuentra la función en la clase y proporciona r como primer argumento. Una referencia a método como r.area es un método vinculado: recuerda la función y este rectángulo. Añadir () realiza la llamada.

Si un método se define como def resize(self, factor), llámalo como r.resize(2). r ocupa self y 2 ocupa factor. No vuelves a pasar r. Si un método de instancia se escribe por accidente como def area():, una llamada sobre r lanza TypeError porque Python sigue proporcionando el objeto a una función que no acepta argumentos.

Un método puede usar otro a través de self:

    def double_area(self):
        return self.area() * 2

Aquí self.area() calcula usando el mismo rectángulo. Omitir self. solicitaría una función independiente llamada area, no el método del objeto.

Devuelve resultados al código que realiza la llamada

Un método de cálculo normalmente debe devolver su resultado. Devolver proporciona al código que realiza la llamada un valor que puede almacenar, combinar, comprobar o mostrar:

r1 = Rectangle(3, 4)
r2 = Rectangle(2, 5)
total = r1.area() + r2.area()
print(total)  # 22

Si area solo mostrara su respuesta, cada llamada devolvería None. La suma fallaría entonces con TypeError, aunque hubieran aparecido dos números en la pantalla. Mostrar es una acción; devolver proporciona un valor a la expresión que contiene la llamada.

Usa nombres de métodos que indiquen la tarea. deposit y resize nombran acciones; is_square y has_room sugieren comprobaciones booleanas. Un método no necesita parámetros para los datos que ya están almacenados en su objeto. Necesita parámetros para entradas nuevas, como un factor de cambio de tamaño o el importe de un depósito.

Puntos clave

  • Pon self primero en un método de instancia; Python proporciona el objeto receptor.
  • Lee atributos y llama a otros métodos a través de self..
  • Los paréntesis ejecutan un método vinculado.
  • Devuelve los valores calculados para que el código que realiza la llamada pueda combinarlos, comprobarlos o mostrarlos.

3.2 Modifica el estado de un objeto con métodos

El estado de un objeto es el conjunto actual de valores de sus atributos. Un método de consulta informa sobre ese estado. Un método modificador lo cambia. Un método modificador útil comprueba sus entradas y mantiene todos los atributos relacionados, para que el código que lo llama no tenga que coordinar varias actualizaciones.

Valida primero todo el cambio

Añade estos métodos dentro de la clase BankAccount, que tiene titular, saldo y lista de transacciones:

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount
        self.transactions.append(("deposit", amount))

    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("withdrawal must be positive")
        if amount > self.balance:
            raise ValueError("insufficient funds")
        self.balance -= amount
        self.transactions.append(("withdraw", amount))

Un depósito cambia el saldo y registra la operación. Una retirada comprueba tanto el signo del importe como el saldo disponible antes de restar. Estas comprobaciones impiden que una retirada rechazada cambie el saldo o su historial.

Partiendo de BankAccount("Maya", 50), depositar 25 da 75 y retirar 30 da 45. Un intento posterior de retirar 100 lanza ValueError; deja el saldo en 45 y no añade ninguna transacción. Colocar la resta antes de la comprobación de fondos cambiaría el estado incluso en caso de fallo.

Distingue un cambio de un valor devuelto

Estos métodos modificadores no tienen sentencia return, por lo que devuelven None. Después de new_balance = acct.deposit(25), el depósito se ha realizado, pero new_balance es None. Lee acct.balance después para obtener el número actualizado.

Una clase puede ofrecer otro estilo de método que devuelva un objeto nuevo. resize sugiere cambiar un rectángulo; resized puede elegirse para un método que devuelva un rectángulo nuevo y deje intacto el original. Lee el contrato del método: el nombre ayuda, pero su definición determina el comportamiento.

Sigue las referencias compartidas
a = BankAccount("Maya", 50)
b = a
b.deposit(10)
print(a.balance)  # 60
print(a is b)     # True

La asignación vincula un nombre a un objeto; no copia ese objeto. Ambos nombres acceden a la cuenta cuyo saldo ha cambiado. Pasar a a una función también proporciona acceso a ese objeto. Por tanto, una función que llame a account.withdraw(5) puede cambiar la cuenta que ve el código que la llama.

Para obtener una cuenta independiente con el mismo titular y saldo, crea BankAccount(a.owner, a.balance). Ese constructor también proporciona un historial nuevo y vacío. Es una cuenta nueva, no una copia completa de su historial.

copy.copy(a) crea un objeto externo nuevo, pero conserva las referencias a los mismos objetos internos, incluida la lista de transacciones. copy.deepcopy(a) copia también los objetos anidados. Cuando la independencia sea importante, comprueba tanto los objetos dentro de los atributos como la instancia externa.

Asignar directamente acct.balance = -500 se salta las comprobaciones de los dos métodos. Las convenciones de atributos internos y las propiedades ayudan a dejar clara la vía prevista de actualización y a mantener la validación cerca de los cambios de estado.

Puntos clave

  • Comprueba un cambio solicitado antes de actualizar los atributos.
  • Un método modificador puede cambiar el estado y devolver None.
  • La asignación y el paso de argumentos conservan referencias al mismo objeto.
  • Una copia superficial puede seguir compartiendo objetos mutables anidados.
  • Mantén juntas las actualizaciones de estado relacionadas en el método responsable de ellas.

3.3 Protege atributos con convenciones de nombres y propiedades

El código que realiza las llamadas necesita una forma clara de leer y modificar un objeto. La encapsulación mantiene las reglas de sus datos junto a los métodos que los gestionan. Python usa convenciones y propiedades para expresar esa interfaz.

Marca el almacenamiento interno

Un guion bajo inicial en _balance indica un atributo interno. El código que usa la cuenta debe utilizar sus métodos, pero Python sigue permitiendo el acceso directo. No hay ninguna palabra reservada private que bloquee el acceso al valor.

Un nombre como __balance, con dos guiones bajos iniciales y sin dos guiones bajos finales, activa la transformación de nombres. Dentro de class Acc, self.__balance se almacena con el nombre _Acc__balance. Fuera, obj.__balance falla, pero obj._Acc__balance puede seguir funcionando. La transformación evita conflictos de nombres; no protege datos sensibles.

Ejecuta un método mediante la sintaxis de atributos

Una propiedad hace que se ejecute un método al leer un atributo. La línea @property es un decorador: cambia cómo Python expone el método. Una propiedad sin método de asignación permite leer, pero rechaza la asignación.

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    @property
    def balance(self):
        return self._balance

Después de acct = Account("Maya", 50), lee acct.balance sin paréntesis. Asignar acct.balance = 10 lanza AttributeError porque esta propiedad no tiene método de asignación. Un método de depósito dentro de la clase podría actualizar _balance después de sus comprobaciones.

Comprueba la asignación con un método de asignación
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not 0 <= value <= 100:
            raise ValueError("score must be between 0 and 100")
        self._score = value

Los métodos de consulta y de asignación comparten el nombre score; el valor almacenado usa _score. Al crear el objeto, self.score = score ejecuta el método de asignación.

Para s = Student("Ana", 88), asignar s.score = 92 almacena 92. Asignar s.score = -1 lanza ValueError antes de almacenar, por lo que la puntuación sigue siendo 92. Student("Ana", 105) falla durante la inicialización.

Dentro del método de asignación, self.score = value volvería a llamar al método, repitiéndose hasta producir RecursionError. El método de consulta tiene el mismo riesgo si devuelve self.score. Usa el nombre de almacenamiento independiente _score en ambos cuerpos.

Calcula los valores cuando se leen

Una propiedad puede calcular un valor a partir de otros atributos:

    @property
    def area(self):
        return self.width * self.height

Colocada dentro de una clase de rectángulos, permite usar r.area. Para una anchura de 3 y una altura de 4, el valor es 12. Cambiar la anchura a 5 hace que la siguiente lectura devuelva 20. Almacenar un área una sola vez durante la inicialización dejaría un valor antiguo después de cambiar la anchura.

Empieza con un atributo simple cuando baste con almacenar el valor.

Puntos clave

  • Un guion bajo inicial marca el almacenamiento interno por convención; no bloquea el acceso.
  • La transformación de nombres cambia el nombre de un atributo para evitar conflictos.
  • Una propiedad ejecuta un método de consulta mediante la sintaxis de atributos.
  • Un método de asignación puede validar la inicialización y las asignaciones posteriores en un mismo lugar.
  • Usa un atributo de almacenamiento independiente para evitar llamar a la propiedad de forma recursiva.
  • Una propiedad calculada obtiene su valor al leerla.
4

Usa métodos especiales para mostrar y comparar objetos adecuadamente

Un objeto útil debe encajar de forma natural en las expresiones habituales de Python. Sus métodos especiales pueden proporcionar texto legible, igualdad de valores, un orden, resultados aritméticos o una longitud. Aprende qué método solicita Python en cada situación y qué debe devolver ese método. Presta especial atención a la visualización dentro de contenedores, a las claves estables de diccionario y a la diferencia entre una operación y su forma reflejada.

4.1 Define representaciones en cadena legibles para los objetos

Un objeto resulta más fácil de inspeccionar cuando puede describir sus propios datos. Los métodos especiales permiten que Python solicite esa descripción mediante acciones conocidas como print, str y repr. Sus nombres empiezan y terminan con dos guiones bajos y suelen llamarse nombres dunder.

Elige texto para dos públicos

__str__ proporciona texto legible para los usuarios de un programa. __repr__ proporciona una descripción precisa para quienes programan. Ambos devuelven cadenas; ninguno debe mostrar su resultado en lugar de devolverlo.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = Point(3, 4)
print(p)        # (3, 4)
print(repr(p))  # Point(3, 4)

str(p) devuelve la cadena "(3, 4)". repr(p) devuelve "Point(3, 4)". Cuando es posible, una representación se parece a una llamada a la clase que recrearía el valor. Sigue siendo texto: llamar a repr no crea por sí mismo otro punto.

Sin una representación personalizada, una instancia simple suele mostrar un nombre de clase y un identificador parecido a una dirección. Eso ayuda a distinguir objetos, pero no muestra sus datos útiles. Rara vez llamas directamente a estos métodos especiales; defínelos una vez y usa las operaciones habituales de visualización de Python.

Predice qué muestra un contenedor
ExpresiónRepresentación solicitada
print(p) o str(p)__str__
repr(p)__repr__
f"{p}"La forma habitual de cadena para esta clase
f"{p!r}"__repr__
print([p])La visualización de la lista, que usa repr para su elemento

Con la clase anterior, print([Point(3, 4), Point(0, 0)]) muestra [Point(3, 4), Point(0, 0)]. La lista usa las representaciones de sus elementos, aunque cada elemento también tenga __str__.

Si defines solo __repr__, el __str__ predeterminado también lo usa. Definir solo __str__ no cambia repr ni la visualización de los contenedores. Por tanto, al añadir un único método de visualización, __repr__ sirve para más situaciones de inspección.

Haz que los campos de cadena sean inequívocos

Para un libro que ya almacena title, author y year, estos métodos ofrecen distintas vistas útiles:

    def __str__(self):
        return f"{self.title} by {self.author} ({self.year})"

    def __repr__(self):
        return f"Book({self.title!r}, {self.author!r}, {self.year})"

Para el libro con título "Dune", autor "Frank Herbert" y año 1965, el primer método da "Dune by Frank Herbert (1965)". El segundo da "Book('Dune', 'Frank Herbert', 1965)". !r solicita las representaciones de los campos de cadena, incluidas las comillas.

Un cuerpo que solo contiene print(self.x) devuelve None, por lo que str(p) falla con TypeError. Devolver un número entero también falla. Construye el texto necesario y después devuelve esa cadena.

Puntos clave

  • __str__ proporciona texto para los usuarios; __repr__ proporciona una representación útil para quienes programan.
  • Los contenedores muestran sus elementos con repr.
  • __repr__ puede proporcionar la forma de cadena predeterminada; __str__ no sustituye a repr.
  • Ambos métodos deben devolver cadenas.
  • Usa !r en una cadena formateada para mostrar representaciones entrecomilladas de los campos de cadena.

4.2 Compara y combina objetos con operadores

Los métodos especiales conectan una clase con operadores y funciones incorporadas. Define solo operaciones con un significado claro para ese tipo: sumar vectores se entiende; añadir un jugador a un equipo resulta más claro como team.add_player(player).

Compara valores y mantén los hashes seguros

Dos instancias simples independientes se consideran distintas de forma predeterminada. Añade igualdad de valores a una clase de puntos con:

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

Ahora dos puntos en (3, 4) se consideran iguales aunque sus identidades sean distintas. in, count, index y remove en listas pueden encontrar esos valores iguales. El resultado habitual de != se obtiene a partir de __eq__.

NotImplemented es un valor de retorno que permite a Python probar el otro operando o una alternativa predeterminada. No es una excepción que debas lanzar. Cuando ninguno de los operandos admite la igualdad de valores, == recurre a la identidad; un punto y la cadena "3,4" se consideran distintos.

Definir __eq__ sin __hash__ hace que las instancias no sean aptas para hash. Entonces no pueden ser elementos de conjuntos ni claves de diccionarios. Los objetos iguales deben tener hashes iguales y el hash de una clave debe mantenerse estable. Si los atributos comparados nunca cambian, un hash compatible puede devolver hash((self.x, self.y)). Mantén los objetos de valor mutables sin aptitud para hash cuando puedan cambiar sus campos de igualdad.

Define un orden

__lt__ proporciona <. Una clase de estudiantes puede comparar puntuaciones con return self.score < other.score. Las puntuaciones 88, 72 y 95 se ordenan entonces como 72, 88, 95. min elige la más baja; max elige la más alta usando > y su comparación reflejada <.

Con solo __lt__, a > b puede usar b < a. Python no deduce <= ni >= a partir de esa definición; necesitan sus propios métodos. Si un orden solo resulta útil para una tarea, pasa una función de clave de ordenación:

def by_score(student):
    return student.score

ordered = sorted(students, key=by_score)
Devuelve resultados nuevos en las operaciones aritméticas

Dentro de una clase de vectores con atributos x e y:

    def __add__(self, other):
        if not isinstance(other, Vector):
            return NotImplemented
        return Vector(self.x + other.x, self.y + other.y)

    def __mul__(self, factor):
        if not isinstance(factor, (int, float)):
            return NotImplemented
        return Vector(self.x * factor, self.y * factor)

    def __rmul__(self, factor):
        return self * factor

Vector(1, 2) + Vector(3, 4) produce un vector nuevo (4, 6). Multiplicar el primer vector por 3 produce (3, 6) y deja intacto el original. __mul__ gestiona el vector a la izquierda; __rmul__ admite un número a la izquierda. Python no supone que ambos órdenes signifiquen lo mismo.

Proporciona una longitud con sentido

Para una lista de reproducción que almacena self.songs, define __len__ para que devuelva len(self.songs). El resultado debe ser un número entero no negativo. len(playlist) cuenta entonces las canciones. En esta clase, una longitud de cero también hace que if playlist: sea falso; un __bool__ personalizado tendría prioridad sobre la longitud al comprobar el valor de verdad.

Puntos clave

  • __eq__ define la igualdad de valores; devuelve NotImplemented para los tipos de operando no admitidos.
  • Los objetos iguales aptos para hash necesitan hashes iguales y estables.
  • __lt__ permite ordenar, pero no proporciona <= ni >=.
  • Usa una función de clave de ordenación para un orden específico de una tarea.
  • Los métodos aritméticos pueden crear objetos nuevos sin modificar los operandos.
  • __rmul__ permite multiplicar con el objeto a la derecha.
  • __len__ devuelve un número entero no negativo y puede determinar el valor de verdad cuando no existe __bool__.
5

Construye programas con objetos que colaboran

Los programas más grandes conectan objetos con distintas tareas. Un carrito puede pedir subtotales a sus artículos; una cuenta puede pedir a otra que reciba un depósito. Sigue las referencias y la secuencia de llamadas para ver qué estado cambia y dónde un error detiene la ejecución. Después usa esas ideas para elegir un diseño claro, planificar una clase pequeña y probar su comportamiento público en pasos manejables.

5.1 Construye objetos que contengan otros objetos

Los atributos de un objeto pueden referirse a otros objetos. Un carrito contiene artículos, una biblioteca contiene libros y un curso contiene estudiantes. La composición construye un objeto mayor a partir de otros más pequeños mediante esta relación tiene.

Da a cada objeto su propia responsabilidad
class Item:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

    def subtotal(self):
        return self.price * self.quantity

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, item):
        if not isinstance(item, Item):
            raise TypeError("a cart holds Item objects")
        self.items.append(item)

    def total(self):
        total = 0
        for item in self.items:
            total += item.subtotal()
        return total

El carrito tiene una lista independiente creada durante la inicialización. Su método add comprueba el objeto recibido antes de añadirlo. cart.add("pen") lanza TypeError en lugar de almacenar texto que fallará cuando se solicite un subtotal.

Crea los artículos antes de las llamadas o durante ellas:

cart = ShoppingCart()
cart.add(Item("notebook", 2.50, 4))
cart.add(Item("pen", 1.25, 3))
print(cart.total())         # 13.75
print(cart.items[0].name)   # notebook

Los subtotales son 10.0 y 3.75; el carrito los suma para obtener 13.75. La cadena cart.items[0].name accede primero a la lista del carrito, después a su primer objeto y después al nombre de ese objeto.

Delega el trabajo en el objeto que sabe hacerlo

El carrito pide subtotal() a cada artículo. Esto es delegación: un objeto llama al método de otro objeto para realizar parte de una tarea. Si un descuento por volumen cambia cómo se calcula un subtotal, esa regla pertenece a Item.subtotal. El carrito puede seguir sumando los subtotales devueltos sin duplicar las reglas de precios.

Pasar Item en lugar de Item(...) pasa la clase, no un artículo comprado. La comprobación de tipo rechaza ese objeto clase. Realiza todas las adiciones a través de la vía con comprobación; acceder directamente a cart.items.append(...) se saltaría add.

Busca un objeto

Añade este método dentro de ShoppingCart:

    def find(self, name):
        for item in self.items:
            if item.name == name:
                return item
        return None

La primera coincidencia devuelve el artículo existente. Si no hay coincidencia, devuelve None. Comprueba found is None antes de usar found.name; None no tiene ese atributo. Un método __len__ que devuelva len(self.items) también podría hacer que len(cart) contara los objetos artículo almacenados, mientras que __str__ podría unir las líneas de los artículos para formar un recibo.

Sigue las referencias dentro de las colecciones

Una lista almacena referencias, por lo que colocar el mismo artículo en dos carritos no lo copia. Actualizar el precio de ese artículo afecta al siguiente total que calculen ambos carritos. Las listas de carrito independientes evitan compartir listas por accidente, pero no hacen independientes los objetos que contienen. Crea artículos separados cuando los carritos deban contener datos independientes.

Puntos clave

  • La composición almacena objetos dentro de otro objeto.
  • La delegación pide a un objeto contenido que realice su parte de una tarea.
  • Comprueba un objeto antes de almacenarlo cuando los métodos posteriores requieran su tipo.
  • Una búsqueda puede devolver el objeto coincidente o None.
  • Los contenedores independientes pueden seguir refiriéndose al mismo objeto contenido.

5.2 Haz que los objetos interactúen mediante métodos

Los objetos pueden ser argumentos de métodos y valores de retorno igual que los números o las cadenas. Sigue por separado el objeto receptor, los argumentos y el resultado: un método puede modificar un objeto existente, devolver uno nuevo o simplemente informar de un valor.

Usa otro objeto como entrada

Dentro de una clase de puntos con x e y, un método puede leer ambos puntos:

    def midpoint(self, other):
        return Point((self.x + other.x) / 2,
                     (self.y + other.y) / 2)

Para p = Point(3, 4) y q = Point(1, 0), middle = p.midpoint(q) crea un tercer punto en (2.0, 2.0). Durante la llamada, self se refiere a p y other se refiere a q. El método calcula coordenadas y construye un punto nuevo; no asigna nada a ninguno de los originales.

Con import math, un método de distancia puede devolver math.hypot(self.x - other.x, self.y - other.y). Point(0, 0).distance_to(Point(3, 4)) da entonces 5.0. Esta llamada devuelve un número, mientras que midpoint devuelve un objeto.

Deja que cada objeto receptor gestione sus cambios

Dentro de una clase de cuentas con métodos withdraw y deposit que realizan comprobaciones:

    def transfer_to(self, other, amount):
        self.withdraw(amount)
        other.deposit(amount)

Si Maya tiene 100 y Leo tiene 20, maya.transfer_to(leo, 30) primero retira de la cuenta de Maya, dejándola en 70, y después deposita en la de Leo, dejándola en 50. En la primera llamada, self es la cuenta de Maya. En la llamada de depósito, su propio self es la cuenta de Leo.

Una transferencia posterior de 500 falla en withdraw porque Maya solo tiene 70. La ejecución se detiene antes de deposit, por lo que ambos saldos conservan sus valores anteriores. El orden de las llamadas importa. Python no deshace un cambio de estado anterior si una llamada posterior lanza una excepción; este método breve depende de que el depósito de la cuenta receptora pueda aceptar el importe.

Llamar a other.deposit(amount) ejecuta las reglas de la cuenta receptora en un mismo lugar. Cambiar directamente other.balance se las saltaría.

Sigue los objetos, no solo los nombres de las variables

Dibuja cada instancia como una caja que contiene sus atributos. Dibuja una flecha desde cada variable hasta su caja. En una llamada, identifica qué caja recibe self y qué cajas se pasan como argumentos. Actualiza la caja afectada después de cada línea que cambie el estado correctamente. Dos flechas hacia una caja significan que un cambio a través de cualquiera de los nombres llega al mismo objeto.

Una función normal también puede trabajar con varios objetos. total_balance(accounts) puede recorrer las cuentas y sumar sus saldos. Ese cálculo sobre toda la lista no tiene por qué pertenecer a una sola cuenta.

Representa una relación ausente

Un libro puede empezar con self.borrower = None y después almacenar un objeto miembro. Antes de leer un atributo del miembro, comprueba la relación:

if book.borrower is not None:
    print(book.borrower.name)

De lo contrario, book.borrower.name falla cuando el libro no tiene prestatario. Las clases pueden definirse encima de una función main() que crea y conecta los objetos. Llamar a main() bajo if __name__ == "__main__": impide que esas acciones se ejecuten al importar las clases.

Puntos clave

  • Identifica el objeto receptor como self y el argumento de tipo objeto mediante su propio parámetro.
  • Un método puede devolver un objeto nuevo y dejar intactas sus entradas.
  • Las llamadas ocurren en orden; una excepción detiene las llamadas posteriores, pero no deshace los cambios anteriores.
  • Usa los métodos públicos de otro objeto para aplicar sus reglas.
  • Comprueba si hay None antes de seguir una relación opcional entre objetos.
  • Una función puede organizar cálculos que abarquen varios objetos.

5.3 Decide cuándo una clase mejora la claridad de un programa

Una clase se justifica cuando resulta más fácil entender juntos los datos relacionados y las operaciones repetidas. No necesitas convertir cada función o registro en una clase. Elige la estructura que deje claras las responsabilidades del programa.

Busca datos y comportamiento que vayan juntos

Las clases ayudan cuando varias funciones reciben continuamente el mismo grupo de valores, muchos registros comparten una estructura y los cambios de estado deben seguir reglas comunes. Varias cuentas que admiten depósitos y retiradas encajan bien. Un cálculo aislado puede seguir siendo una función. Un registro fijo de solo lectura puede seguir siendo una tupla o un diccionario.

Una clase que solo tiene inicialización y una operación que se realiza una vez suele añadir más formalidades que estructura útil. Pregúntate qué representa una instancia y qué acciones repetidas le corresponden. Si no puedes responder esas preguntas de forma sencilla, reconsidera el diseño.

Mueve una operación junto a sus datos

Una cuenta basada en un diccionario podría empezar así:

account = {"owner": "Maya", "balance": 50}

def deposit(account, amount):
    account["balance"] += amount

La versión con clase puede colocar la misma operación junto a su estado y sus reglas:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount

    def __repr__(self):
        return f"BankAccount({self.owner!r}, {self.balance})"

Después de acct = BankAccount("Maya", 50) y acct.deposit(25), su representación es BankAccount('Maya', 75). La regla de depósito positivo se ejecuta ahora siempre que el código usa ese método. Leer account["balence"] lanzaría KeyError; leer acct.balence lanza AttributeError. Una clase no elimina las erratas, pero da a los datos y a sus operaciones un lugar con nombre.

Planifica a partir de una descripción breve

En «Una biblioteca presta libros a sus miembros. Un miembro puede tener prestados como máximo 3 libros», los sustantivos sugieren Book, Member y Library. Los verbos sugieren métodos de préstamo y devolución.

  1. Enumera lo que sabe cada instancia: el título, autor y prestatario de un libro; el nombre y los libros prestados de un miembro; los libros y miembros de una biblioteca.
  2. Identifica las reglas: un libro no puede estar ya prestado y un miembro no puede superar MAX_LOANS = 3.
  3. Coloca cada regla en la operación que cambia la relación, como Library.check_out(book, member).
  4. Escribe la inicialización y una representación útil; después añade y prueba una acción cada vez.

Los títulos distintos necesitan instancias distintas de Book, no clases separadas con el nombre de cada título. Usa sustantivos en singular para los objetos individuales, como Book; nombra una colección por lo que representa, como Library. Mantén la tarea de cada clase lo bastante clara como para describirla en una frase.

Prueba el comportamiento observable
acct = BankAccount("Maya", 50)
acct.deposit(25)
assert acct.balance == 75

assert lanza AssertionError si su condición es falsa. Prueba también las acciones rechazadas: captura la excepción esperada y verifica que el estado no haya cambiado. Usa validación explícita con raise para los datos de entrada del código que realiza la llamada; las aserciones son comprobaciones durante el desarrollo y pueden desactivarse cuando Python se ejecuta con optimización.

Puntos clave

  • Elige una clase cuando el estado relacionado y las operaciones repetidas se beneficien de estar juntos.
  • Mantén un cálculo sencillo como función cuando no se necesite un estado de objeto persistente.
  • Planifica los atributos a partir de los datos y los métodos a partir de las acciones y las reglas.
  • Modela los distintos valores de datos como instancias de una misma clase.
  • Prueba tanto las actualizaciones correctas como los cambios rechazados.

Ejemplos de Programación orientada a objetos en Python

5 Lecciones Interactivas

1

De los valores conocidos a tu primera clase

11 pasos
Sigue las llamadas a métodos y las referencias compartidas, y después crea dos objetos con sus propios atributos.
  1. 1
    IntroducciónEmpieza con objetos que conoces

    Una lista, una cadena y un número son objetos. Sus tipos determinan qué operaciones admiten y cada valor tiene su propia identidad.

    Empieza siguiendo un método de cadena y uno de lista. Después usa las mismas ideas para crear una clase sencilla cuyas instancias almacenen datos distintos. Sigue haciéndote dos preguntas: ¿a qué objeto accede este nombre y qué devuelve esta llamada?

  2. 2
    EjemploPregunta a un valor por su tipo

    Python puede decirte qué tipo de objeto es un valor.

    text = "river"
    values = [4, 2]
    print(type(text))              # <class 'str'>
    print(isinstance(values, list))  # True
    

    La cadena y la lista ofrecen métodos distintos porque tienen tipos distintos. Puedes usar dir(values) para inspeccionar los nombres disponibles en la lista, incluidos los métodos normales y los especiales.

  3. 3
    EjemploSigue una cadena devuelta

    Un método de cadena puede producir un valor nuevo sin sustituir el original.

    name = "robin"
    result = name.title()
    print(result)  # Robin
    print(name)    # robin
    

    title() devuelve una cadena nueva. La asignación almacena ese resultado con el nombre result, mientras que name sigue refiriéndose a "robin". Escribir name = name.title() haría, en cambio, que name se refiriera a la cadena devuelta.

  4. 4
    EjemploSigue un cambio sobre la propia lista

    Compara la llamada sobre la cadena con la ordenación de una lista.

    values = [4, 2]
    result = values.sort()
    print(values)  # [2, 4]
    print(result)  # None
    

    La propia lista cambia y el valor de retorno es None. Asignar la llamada de nuevo a values sustituiría la referencia a la lista de ese nombre por None. Usa values.sort() por sí solo cuando quieras modificar la lista existente.

  5. 5
    Desafío¿Dónde aparece el elemento añadido?

    Predice las dos listas que se muestran.

    a = [7]
    b = a
    c = [7]
    b.append(2)
    print(a, c)
    

    ---reveal---

    La salida es [7, 2] [7]. a y b se refieren a una misma lista, por lo que añadir un elemento a través de b modifica la lista que se ve a través de a. c contiene una lista independiente. Las listas eran iguales al principio, pero a is c era falso.

  6. 6
    ConsejoDibuja las referencias antes de seguir los cambios

    Dibuja una caja por cada lista creada por separado. Haz que tanto a como b apunten a la primera caja y que c apunte a la segunda. Añadir un elemento modifica la caja; una asignación cambia adónde apunta un nombre.

    Distingue también un método de su llamada: name.title da un objeto método, mientras que name.title() lo ejecuta. Los paréntesis pueden marcar la diferencia entre tener un comportamiento y obtener su resultado.

  7. 7
    AnalogíaUn formulario, varias copias rellenadas

    Piensa en una clase como un formulario en blanco que describe la información que contiene un tipo de objeto. Una instancia es una copia rellenada con sus propios datos.

    Una clase Badge puede representar todas las credenciales de un programa. Una instancia contiene el nombre de una persona visitante; otra contiene un nombre distinto. La clase describe el tipo de objeto compartido. No necesitas una clase nueva para cada valor escrito en el formulario.

  8. 8
    EjemploDefine el tipo y crea dos instancias

    Usa una clase mínima mientras aprendes cómo funcionan las instancias.

    class Badge:
        pass
    
    first = Badge()
    second = Badge()
    first.name = "Rae"
    second.name = "Ivo"
    

    pass proporciona un cuerpo vacío. Cada llamada a la clase crea una instancia y las asignaciones con punto almacenan datos en el objeto seleccionado. first is second es falso. first = Badge daría, en cambio, otro nombre a la clase sin crear una instancia.

  9. 9
    DesafíoEncuentra un atributo mal escrito

    Continúa con las dos credenciales:

    first.nmae = "Tess"
    print(first.name, second.name)
    

    ¿Qué aparece?

    ---reveal---

    La salida es Rae Ivo. La asignación crea un atributo independiente llamado nmae; no cambia name. Leer un atributo que no existe lanza AttributeError, pero se permite asignar un nombre nuevo. Revisa la escritura en la asignación y en la lectura cuando una lectura dé un resultado inesperado.

  10. 10
    DesafíoCombina instancias y alias

    Continúa con first y second del ejemplo de las credenciales:

    copy_name = first
    copy_name.name = "Noor"
    print(first.name, second.name)
    

    ---reveal---

    La salida es Noor Ivo. copy_name es un alias de la primera credencial, por lo que la asignación modifica ese objeto. La segunda credencial sigue siendo independiente. Crear instancias controla cuántos objetos existen; asignar nombres controla a qué objetos acceden esos nombres.

  11. 11
    RepasoSigue otro programa en papel

    Prueba un programa breve con dos llamadas a la clase y un tercer nombre asignado a la primera instancia. Da atributos distintos a los objetos y después cambia uno a través del alias.

    Antes de ejecutarlo, dibuja las cajas de los objetos y predice cada lectura. Si una predicción difiere de la salida, inspecciona la escritura de los atributos y las flechas desde los nombres hasta los objetos. Esto distingue un error de inicialización de un error de referencias.

2

Construye objetos con datos iniciales fiables

14 pasos
Inicializa una pequeña colección de tareas, distingue los ajustes compartidos de los datos propios y rechaza una inicialización no válida.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónPrepara una colección de tareas
  2. 2
    ExplicaciónDale un inicializador a Python
  3. 3
    EjemploRelaciona una llamada con sus parámetros
  4. 4
    DesafíoDetecta el atributo ausente
  5. 5
    EjemploAcepta una lista inicial opcional
  6. 6
    ConsejoComprueba hasta dónde llega la copia
  7. 7
    ExplicaciónAñade datos sobre la clase
  8. 8
    EjemploSigue una asignación que oculta un atributo
  9. 9
    DesafíoCorrige el contador compartido
  10. 10
    ExplicaciónRechaza un límite que no puede funcionar
  11. 11
    EjemploElige el error y la vía de recuperación
  12. 12
    Desafío¿Mostrar un mensaje rechaza la inicialización?
  13. 13
    DesafíoConserva el objeto anterior si hay un fallo
  14. 14
    RepasoComprueba la inicialización desde el código que la llama
3

Controla el estado mediante métodos y propiedades

16 pasos
Construye un contador de existencias con comprobaciones, sigue un alias y ofrece acceso validado a los atributos.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónHaz útil un contador de existencias
  2. 2
    EjemploEmpieza con una cantidad almacenada
  3. 3
    ExplicaciónDeja que el objeto receptor proporcione self
  4. 4
    DesafíoHaz que un resultado pueda reutilizarse
  5. 5
    EjemploAñade existencias después de comprobar la cantidad
  6. 6
    EjemploRechaza una retirada antes de restar
  7. 7
    DesafíoSigue un cambio a través de un alias
  8. 8
    ConexiónCopia el nivel de estado adecuado
  9. 9
    ExplicaciónMarca el almacenamiento interno
  10. 10
    EjemploLee a través de una propiedad
  11. 11
    EjemploPermite la asignación pública con comprobaciones
  12. 12
    DesafíoEvita un método de asignación recursivo
  13. 13
    Idea claveCalcula un valor cuando se lee
  14. 14
    DesafíoCombina un método de asignación con un alias
  15. 15
    ConsejoLee juntas la definición y la llamada
  16. 16
    RepasoPrueba una operación cada vez
4

Haz que los objetos encajen en las expresiones de Python

17 pasos
Da a un par de coordenadas representaciones útiles, comparaciones, aritmética y un comportamiento claro con las operaciones incorporadas.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónElige qué debe significar un objeto
  2. 2
    EjemploEmpieza con un punto
  3. 3
    EjemploAñade texto para la inspección
  4. 4
    DesafíoPredice cómo se muestra una lista
  5. 5
    ConsejoDevuelve texto en ambos métodos de visualización
  6. 6
    EjemploCompara los valores de las coordenadas
  7. 7
    Idea claveDeja a Python las comparaciones no admitidas
  8. 8
    Desafío¿Pueden entrar pares iguales en un conjunto?
  9. 9
    EjemploElige un orden para las coordenadas
  10. 10
    ConexiónEl orden puede pertenecer a una tarea
  11. 11
    EjemploSuma sin modificar los operandos
  12. 12
    EjemploAdmite ambos órdenes de multiplicación
  13. 13
    DesafíoDistingue un resultado nuevo de un alias
  14. 14
    ExplicaciónUsa la longitud para una colección
  15. 15
    DesafíoPredice la rama de la cola vacía
  16. 16
    ConsejoMantén visible el significado
  17. 17
    RepasoComprueba cada expresión por separado
5

Conecta objetos con tareas claras

15 pasos
Construye un pedido pequeño a partir de objetos de línea, sigue las referencias compartidas y prueba una operación rechazada.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónConstruye un pedido a partir de partes más pequeñas
  2. 2
    EjemploDale a una línea su propio cálculo
  3. 3
    EjemploAlmacena líneas en un pedido
  4. 4
    EjemploDelega cada cálculo de coste
  5. 5
    DesafíoSigue una línea compartida por dos pedidos
  6. 6
    ConsejoComprueba si falta el resultado de la búsqueda
  7. 7
    ExplicaciónPasa un objeto a otro método
  8. 8
    EjemploSigue el objeto receptor y el resultado
  9. 9
    Desafío¿Dónde se detiene una combinación fallida?
  10. 10
    ConexiónSigue llamadas que modifican dos objetos
  11. 11
    ExplicaciónDecide qué comportamientos deben estar juntos
  12. 12
    DesafíoEvita una clase para cada producto
  13. 13
    EjemploPrueba el punto de entrada público con comprobaciones
  14. 14
    DesafíoElige una prueba para el diseño combinado
  15. 15
    RepasoPlanifica tu propia colaboración pequeña

Ejercicios de Programación orientada a objetos en Python

50 Preguntas de Práctica en 5 sets

Set 1: Métodos, cambios de estado y propiedades10 Preguntas de Práctica

Pregunta 1 de 10

¿Qué argumento proporciona Python como self cuando el programa llama a panel.area()?
  1. El objeto clase Panel.
  2. El objeto que contiene panel.
  3. El valor que devuelve area.
  4. El primer atributo de panel.
Mostrar respuestaOcultar respuesta
Respuesta: B
Llamar a un método de instancia lo vincula al objeto anterior al punto, así que panel proporciona self. El método puede leer entonces los atributos de ese panel. La clase contiene la definición del método, pero el objeto clase no es el objeto receptor que proporciona esta llamada de instancia.

Pregunta 2 de 10

¿Qué cambio permite al código que realiza la llamada calcular double?

class Pace:
    def __init__(self, minutes):
        self.minutes = minutes
    def seconds(self):
        print(self.minutes * 60)
p = Pace(3)
double = p.seconds() * 2
  1. Llamar a p.seconds sin paréntesis.
  2. Pasar p explícitamente a p.seconds(p).
  3. Devolver self desde el inicializador.
  4. Devolver el producto dentro de seconds.
Mostrar respuestaOcultar respuesta
Respuesta: D
seconds() muestra actualmente un número y devuelve None, por lo que multiplicar su resultado por 2 falla. Devolver self.minutes * 60 proporciona el número al código que realiza la llamada y hace que double sea igual a 360. Quitar los paréntesis daría un objeto método, que tampoco es el número calculado.

Pregunta 3 de 10

Un constructor requiere una lista de lecturas y rechaza cualquier otro tipo antes de almacenarlo. ¿Qué excepción describe mejor que el código que lo llama pase la cadena "12,14"?
  1. TypeError
  2. ValueError
  3. AttributeError
  4. AssertionError
Mostrar respuestaOcultar respuesta
Respuesta: A
El valor proporcionado es texto, mientras que este constructor requiere una lista, por lo que el problema es su tipo. TypeError comunica esa incompatibilidad. ValueError encajaría con una lista cuyo contenido incumpliera una regla de valores permitidos, pero la cadena ni siquiera cumple el tipo de entrada requerido.

Pregunta 4 de 10

¿Qué muestra este código?

class Tank:
    def __init__(self):
        self.level = 5
    def add(self, amount):
        self.level += amount
a = Tank()
b = a
result = b.add(4)
print(a.level, result)
  1. 5 None
  2. 9 9
  3. 9 None
  4. 5 9
Mostrar respuestaOcultar respuesta
Respuesta: C
a y b se refieren al mismo tanque, así que añadir a través de b cambia a 9 el nivel que se ve a través de a. El método no tiene sentencia return, por lo que result es None. Un atributo modificado no se convierte automáticamente en el valor de retorno del método.

Pregunta 5 de 10

¿Qué muestra este código?

import copy
class Log:
    def __init__(self):
        self.entries = []
a = Log()
b = copy.copy(a)
b.entries.append("open")
b.entries = ["closed"]
print(a.entries, b.entries, a is b)
  1. [] ['closed'] False
  2. ['closed'] ['closed'] True
  3. ['open'] ['open'] False
  4. ['open'] ['closed'] False
Mostrar respuestaOcultar respuesta
Respuesta: D
Una copia superficial crea un objeto externo distinto, pero al principio comparte su lista entries. Añadir el elemento modifica esa lista compartida y después la asignación da a b una lista diferente. Esperar que a.entries siga vacía confunde una copia superficial con una copia de todos los objetos mutables anidados.

Pregunta 6 de 10

Una sesión rechaza los nombres que quedan en blanco después de quitar los espacios de los extremos. ¿Qué cuerpo de inicializador aplica esa regla a name?
  1. if not name: raise ValueError("name required")

    self.name = name.strip()

  2. name = name.strip()

    if not name: raise ValueError("name required")

    self.name = name

  3. self.name = name.strip()

    if not name: raise ValueError("name required")

  4. if name == "": raise ValueError("name required")

    self.name = name

Mostrar respuestaOcultar respuesta
Respuesta: B
Quita primero los espacios de los extremos, comprueba después el valor limpio y almacénalo solo si no está vacío. Una cadena de espacios queda vacía y se rechaza. Comprobar el name original antes de limpiar los espacios acepta una cadena que solo contiene espacios, porque una cadena no vacía es verdadera en una condición.

Pregunta 7 de 10

¿Por qué asignar a reading.value llama repetidamente a este método de asignación?

class Reading:
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        self.value = number
  1. La asignación vuelve a usar el nombre de la propiedad.
  2. El método de consulta lee otro nombre de atributo.
  3. El método de asignación necesita un retorno explícito.
  4. El decorador debe usar el nombre de la clase.
Mostrar respuestaOcultar respuesta
Respuesta: A
La asignación a self.value pasa por el mismo método de asignación de la propiedad, así que el método se llama a sí mismo hasta que la recursión falla. Almacena el valor con un nombre distinto, como self._value. El método de consulta ya usa ese nombre de almacenamiento; cambiarlo a self.value también crearía recursión en las lecturas.

Pregunta 8 de 10

Un contador debe mantenerse no negativo. Su método remove(n) debe rechazar cantidades no positivas y retiradas mayores que el valor actual. ¿Qué secuencia deja intacto el estado ante cualquiera de los dos rechazos?
  1. Restar n; comprobar su signo; comprobar el valor nuevo.
  2. Comprobar el signo; restar n; comprobar el valor nuevo.
  3. Comprobar el signo; comprobar el valor disponible; restar n.
  4. Comprobar el valor disponible; restar n; comprobar el signo.
Mostrar respuestaOcultar respuesta
Respuesta: C
Ambas comprobaciones de rechazo deben terminar antes de que la resta cambie el estado. Comprobar que n sea positivo y que haya suficiente valor disponible hace que una retirada fallida deje intacto el contador. Restar antes de la última comprobación puede dejar un valor negativo o aumentado incluso cuando el método lanza una excepción.

Pregunta 9 de 10

¿Qué muestra este código?

class Dial:
    def __init__(self, value):
        self.value = value
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        if not 0 <= number <= 9:
            raise ValueError("out of range")
        self._value = number
d = Dial(4)
try:
    d.value = 12
except ValueError:
    pass
print(d.value)
  1. 12
  2. 4
  3. 9
  4. None
Mostrar respuestaOcultar respuesta
Respuesta: B
Tanto la inicialización como la asignación pública posterior ejecutan el método de asignación. El 4 inicial supera la comprobación, pero 12 lanza una excepción antes de asignar _value, por lo que el valor mostrado sigue siendo 4. El método de asignación rechaza la entrada; no la ajusta al máximo de 9.

Pregunta 10 de 10

Una clase calcula area con una propiedad de solo lectura a partir de width y height. Un objeto tiene anchura 6 y altura 2; después se asigna 8 a su anchura. ¿Qué devuelve leer obj.area?
  1. 12
  2. 6
  3. 16
  4. 8
Mostrar respuestaOcultar respuesta
Respuesta: C
El método de consulta calcula con las dimensiones actuales cada vez que se lee la propiedad, por lo que el área es 8 * 2, es decir, 16. El resultado anterior 12 solo permanecería si el área se almacenara una vez y nunca se actualizara. Solo lectura describe la asignación a la propiedad, no si su resultado calculado puede variar.
Set 2: Objetos, referencias e inicialización fiable10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

¿Qué contiene first.name al final?

class Badge:
    pass
first = Badge()
second = Badge()
first.name = "Rae"
second.name = "Ivo"
first.nmae = "Sol"
  1. "Sol"
  2. "Ivo"
  3. Ningún valor; la asignación falla.
  4. "Rae"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué muestra este código?

label = "cedar"
label.upper()
print(label)
  1. Texto en mayúsculas: CEDAR
  2. El valor None
  3. Texto original: cedar
  4. Texto con inicial mayúscula: Cedar
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

¿Qué muestra este código?

a = [6, 2]
b = a
a = sorted(a)
b.append(9)
print(a, b)
  1. [2, 6] [6, 2, 9]
  2. [2, 6, 9] [2, 6, 9]
  3. [6, 2, 9] [2, 6]
  4. [2, 6] [2, 6, 9]
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un programa necesita tres sensores con etiquetas distintas, pero los mismos métodos de lectura y reinicio. ¿Cómo deben organizarse sus clases y objetos?
  1. Una clase de sensor por etiqueta, con un objeto cada una.
  2. Una clase de sensor, con tres instancias independientes.
  3. Una instancia de sensor, con tres nombres para ella.
  4. Tres clases de etiqueta que comparten una instancia de sensor.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

¿Qué muestra este código?

class Counter:
    def __init__(self, start=4):
        self.value = start
c = Counter(9)
print(c.value)
  1. 4
  2. 0
  3. 9
  4. None
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

¿Por qué falla la última línea?

class Lamp:
    def __init__(self, brightness):
        brightness = brightness
lamp = Lamp(7)
print(lamp.brightness)
  1. La asignación solo cambia un parámetro local.
  2. La llamada a la clase deja el parámetro sin asignar.
  3. El atributo se almacena en la clase.
  4. El inicializador debe devolver el brillo.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

¿Qué muestra este código?

class Folder:
    def __init__(self, tags=[]):
        self.tags = tags
a = Folder()
b = Folder()
a.tags.append("urgent")
a.tags = []
print(b.tags, a.tags)
  1. [] []
  2. ['urgent'] ['urgent']
  3. [] ['urgent']
  4. ['urgent'] []
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

¿Qué muestra este código?

class Ticket:
    fee = 3
x = Ticket()
y = Ticket()
x.fee = 8
Ticket.fee = 5
print(x.fee, y.fee)
  1. 5 5
  2. 8 5
  3. 8 3
  4. 3 5
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

¿Qué muestra este código?

class Job:
    count = 0
    def __init__(self):
        self.count += 1
a = Job()
Job.count = 10
b = Job()
print(a.count, b.count, Job.count)
  1. 1 11 10
  2. 11 11 10
  3. 1 1 10
  4. 1 11 11
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

¿Qué muestra este código?

class Batch:
    def __init__(self, size):
        if size <= 0:
            raise ValueError("size must be positive")
        self.size = size
batch = Batch(6)
try:
    batch = Batch(0)
except ValueError:
    pass
print(batch.size)
  1. 0
  2. None
  3. 6
  4. ValueError
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 3: Representaciones legibles y operadores útiles10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Una clase define tanto __str__ como __repr__. ¿Qué método proporciona el texto de una instancia cuando aparece dentro de una lista mostrada?
  1. __str__
  2. __init__
  3. __len__
  4. __repr__
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué muestra este código?

class Tag:
    def __init__(self, text):
        self.text = text
    def __repr__(self):
        return f"Tag({self.text!r})"
t = Tag("ok")
print(t)
  1. Tag('ok')
  2. ok
  3. Tag(ok)
  4. 'ok'
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

¿Por qué str(marker) lanza TypeError cuando su método __str__ solo llama a print(self.code)?
  1. El nombre del método tiene demasiados guiones bajos.
  2. El código mostrado debe usar comillas simples.
  3. El método devuelve None en lugar de una cadena.
  4. El código debe invocar marker.__str__() directamente.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Una clase mutable Label define la igualdad por su texto y no define __hash__. Dos etiquetas contienen el mismo texto. ¿Qué ocurre cuando se introducen en un conjunto?
  1. Forman dos entradas porque sus identidades difieren.
  2. Crear el conjunto lanza un error de tipo no apto para hash.
  3. Forman una entrada porque sus textos coinciden.
  4. Crear el conjunto convierte sus textos en claves.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Una clase Mark solo define __lt__, que compara puntuaciones numéricas, y ninguna otra comparación personalizada. Para a con puntuación 2 y b con puntuación 7, ¿qué ocurre?
  1. a > b es falso; a <= b es verdadero.
  2. a > b lanza una excepción; a <= b es verdadero.
  3. a > b lanza una excepción; a <= b también.
  4. a > b es falso; a <= b lanza una excepción.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un Vector define __mul__ para vector * number, pero ningún método de multiplicación reflejada. ¿Qué método debe añadirse para admitir también number * vector?
  1. __rmul__
  2. __add__
  3. __lt__
  4. __len__
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Una clase define __eq__ usando code y __hash__ como hash(self.code). Sus objetos se usan como claves de diccionario. ¿Qué cambio de diseño hace fiable el comportamiento de las claves?
  1. Cambiar el hash cada vez que falle una consulta.
  2. Usar id(self) como hash para todos los objetos.
  3. Mantener code intacto mientras el objeto sea una clave.
  4. Comparar la identidad y conservar el hash de code.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Una clase Queue define __len__ como el número de trabajos pendientes y no define __bool__. Sin trabajos pendientes, ¿cuáles son los valores de len(q) y bool(q)?
  1. 0 y True
  2. 0 y False
  3. None y False
  4. 1 y True
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

¿Qué muestra la última línea?

class Offset:
    def __init__(self, value):
        self.value = value
    def __add__(self, other):
        return Offset(self.value + other.value)
a = Offset(6)
b = a
a = a + Offset(4)
print(a.value, b.value, a is b)
  1. 10 6 False
  2. 10 10 True
  3. 6 10 False
  4. 10 10 False
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Rank.__lt__ ordena registros por prioridad. Un informe debe ordenar esos registros por fecha y hora de creación sin cambiar su orden habitual. ¿Qué enfoque encaja?
  1. Sustituir __lt__ por una comparación temporal para todos los registros.
  2. Dar a cada registro una clase nueva para el orden temporal del informe.
  3. Cambiar primero la prioridad de cada registro por su momento de creación.
  4. Llamar a sorted con una función de clave que devuelva el momento de creación.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 4: Objetos que colaboran y diseños que mantienen la claridad10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Un Workshop almacena objetos Tool en self.tools. ¿Qué lee workshop.tools[0].label?
  1. La etiqueta del propio taller.
  2. Las etiquetas de todas las herramientas almacenadas.
  3. La etiqueta de la primera herramienta almacenada.
  4. El número de etiquetas almacenadas.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Una cesta calcula su total llamando a line.cost() para cada línea almacenada. Un descuento por volumen cambia cómo se calcula el coste de cada línea. ¿Dónde debe ir esa regla?
  1. En la inicialización de la lista de la cesta.
  2. En el método cost de la clase de líneas.
  3. En cada parte del código que muestra una cesta.
  4. En el bucle de la cesta, después de cada llamada.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

¿Qué total muestra el código?

class Line:
    def __init__(self, rate, count):
        self.rate = rate
        self.count = count
    def cost(self):
        return self.rate * self.count
x = Line(4, 3)
a = [x]
b = [x, Line(2, 5)]
a[0].count = 6
print(sum(line.cost() for line in b))
  1. 22
  2. 34
  3. 24
  4. 28
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

El método find(name) de una colección devuelve un objeto si tiene éxito y None si falla. ¿Qué código gestiona ambos casos antes de leer result.name?
  1. Leer result.name y después comprobar result is None.
  2. Usar if result.name: antes de mostrar el nombre.
  3. Usar if type(result) is None: antes de leer.
  4. Usar if result is not None: antes de leer.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un método Position.midpoint(other) devuelve una posición nueva cuyas coordenadas son las medias. Si a está en (2, 8) y b en (10, 4), ¿qué deja c = a.midpoint(b)?
  1. c en (6, 6); a y b intactos.
  2. a en (6, 6); c se refiere a a.
  3. b en (6, 6); c se refiere a b.
  4. c en (12, 12); a y b intactos.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

send_to(other, n) llama a self.remove(n) y después a other.add(n). remove rechaza una cantidad mayor que las existencias del emisor antes de cambiar nada; add acepta cantidades positivas. Partiendo de existencias de 9 en el emisor y 4 en el receptor, un envío de 3 se realiza correctamente; después se intenta un envío de 8 y se captura su excepción. ¿Qué existencias quedan?
  1. Emisor 9, receptor 4.
  2. Emisor 6, receptor 15.
  3. Emisor 6, receptor 7.
  4. Emisor -2, receptor 15.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Una transferencia resta existencias al emisor y después llama al método add del receptor. El receptor rechaza la cantidad antes de cambiar sus propias existencias. No hay código que revierta los cambios y el código que realiza la llamada captura la excepción. ¿Qué estado queda?
  1. Emisor sin cambios; receptor sin cambios.
  2. Emisor con menos existencias; receptor sin cambios.
  3. Emisor con menos existencias; receptor con más.
  4. Emisor sin cambios; receptor con más existencias.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un archivo de programa convierte una temperatura de entrada y muestra el resultado una vez. No necesita guardar estado ni repetir comportamientos de objetos. ¿Qué estructura resulta más clara?
  1. Una clase para cada temperatura posible.
  2. Una clase que contenga cada paso aritmético.
  3. Varios objetos con un resultado compartido.
  4. Una función que devuelva la conversión.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un sistema de alquiler tiene muchas bicicletas. Cada bicicleta tiene una etiqueta y un estado de disponibilidad, y las mismas acciones de alquiler y devolución con comprobaciones. ¿Qué plan de clases resulta útil?
  1. Una clase Bike con estado y métodos con comprobaciones.
  2. Una clase por etiqueta con el código de las acciones copiado.
  3. Un objeto bicicleta con todas las etiquetas como alias.
  4. Una clase que solo contenga llamadas a print.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

La estantería debe rechazar un tercer libro y conservar sus dos libros existentes. Después del error capturado a continuación, ¿qué aserción revela la implementación incorrecta?

class Shelf:
    def __init__(self):
        self.limit = 2
        self.books = []
    def add(self, title):
        self.books.append(title)
        if len(self.books) > self.limit:
            raise ValueError("shelf full")
shelf = Shelf()
shelf.add("Atlas")
shelf.add("Birch")
try:
    shelf.add("Cove")
except ValueError:
    pass
  1. assert len(shelf.books) == 3
  2. assert shelf.limit == 2
  3. assert len(shelf.books) == 2
  4. assert shelf.books[-1] == "Cove"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 5: Aplica las ideas en situaciones nuevas10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Para una lista incorporada items, ¿qué devuelve isinstance(items, list)?
  1. El número de elementos de la lista.
  2. Los nombres de sus métodos de lista.
  3. El objeto clase llamado list.
  4. El valor booleano True.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

¿Qué muestra este código?

class Tray:
    labels = []
a = Tray()
b = Tray()
a.labels.append("red")
b.labels = ["blue"]
Tray.labels.append("green")
print(a.labels, b.labels)
  1. ['red', 'green'] ['blue']
  2. ['red', 'green'] ['blue', 'green']
  3. ['red'] ['blue', 'green']
  4. ['blue', 'green'] ['blue', 'green']
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

¿Qué muestra este código?

class Kit:
    def __init__(self, parts=None):
        self.parts = [] if parts is None else list(parts)
source = [[1]]
k = Kit(source)
source.append([2])
source[0].append(3)
print(k.parts)
  1. [[1]]
  2. [[1, 3], [2]]
  3. [[1, 3]]
  4. [[1], [2]]
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

El método de asignación de un medidor rechaza los valores negativos antes de almacenar _reading. Su inicializador asigna directamente a self._reading, así que Meter(-2) funciona. ¿Qué cambio hace que la creación use la misma comprobación que las asignaciones públicas posteriores?
  1. Añadir una sentencia return al método de consulta.
  2. Asignar self.reading = reading en __init__.
  3. Asignar self.reading = reading dentro del método de asignación.
  4. Quitar la propiedad y mantener _reading público.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un Key compara la igualdad solo por name y su hash también usa name. Dos instancias distintas empiezan con el nombre "north"; una se convierte en clave de diccionario. ¿Por qué debe mantenerse estable el nombre de esa clave?
  1. Cambiar un nombre cambia las identidades de ambos objetos.
  2. Todos los atributos de instancia deben ser inmutables en Python.
  3. Las claves de diccionario se copian automáticamente al insertarlas.
  4. Un hash modificado puede impedir encontrar la clave almacenada.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Dos clubes tienen cada uno una lista members independiente, pero ambas listas contienen la misma instancia de Member. Un club cambia el nombre de ese miembro. Ahora los clubes necesitan nombres de miembro independientes. ¿Qué cambio hace falta?
  1. Crear un objeto miembro independiente para cada club.
  2. Copiar solo una de las listas externas members.
  3. Asignar otra variable al miembro compartido.
  4. Mover members al cuerpo de la clase de clubes.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Una clase de mensajes tiene un __str__ útil, pero una lista de mensajes mostrada sigue presentando descripciones de objeto predeterminadas. ¿Qué añadido mejora esas entradas de la lista?
  1. Un __len__ que devuelva la longitud del texto.
  2. Un __init__ que devuelva el valor del texto.
  3. Un __repr__ que devuelva texto útil del mensaje.
  4. Una llamada adicional a print dentro de __str__.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

a.combine(b) debe dejar intactas ambas entradas. Su cuerpo llama a self.parts.extend(other.parts) y después devuelve self. ¿Qué comportamiento tiene realmente esa implementación?
  1. Crea un contenedor nuevo con las partes copiadas.
  2. Modifica a y devuelve ese mismo objeto.
  3. Modifica b y devuelve ese mismo objeto.
  4. Deja ambos intactos y devuelve None.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Dentro de class Vault, la inicialización asigna self.__code = "x". ¿Qué explica que vault.__code falle mientras que vault._Vault__code puede funcionar?
  1. El código se cifra durante la inicialización.
  2. Los guiones bajos convierten el valor en un atributo de clase.
  3. El método de consulta solo se llama con paréntesis.
  4. La transformación de nombres cambia el nombre del atributo almacenado.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

El método take de una clase usa assert n > 0 como única comprobación de los datos del código que lo llama. La regla debe cumplirse incluso cuando Python se ejecute con optimización. ¿Qué cambio hace falta?
  1. Devolver el resultado de la aserción al código que lo llama.
  2. Mover la aserción al bucle que realiza la llamada.
  3. Usar una condición explícita que lance una excepción.
  4. Colocar la aserción después de actualizar el estado.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

¿Te atascas con una pregunta tuya? Ayuda con Tareas

Términos Clave

62 Términos Clave, cada uno con su definición

A

Alias
Un alias es otro nombre que se refiere a un objeto existente, de modo que los cambios en un objeto mutable compartido pueden verse a través de cualquiera de los nombres.
Después de backup = entries, añadir elementos a través de backup también modifica la lista a la que se accede mediante entries.
Aptitud para hash
La aptitud para hash permite que un objeto sirva como elemento de conjunto o clave de diccionario mediante un hash estable y coherente con su regla de igualdad.
Una clase que define __eq__ sin __hash__ deja de ser apta para hash, aunque sus instancias funcionen en una lista.
Argumento de tipo objeto
Un argumento de tipo objeto pasa una referencia a una instancia a una función o un método y permite que el código llamado use ese objeto.
El parámetro other en p.midpoint(q) se refiere a q, cuyas coordenadas lee el método para calcular su resultado.
Argumento predeterminado
Un argumento predeterminado es un valor que se usa cuando el código que realiza la llamada omite un parámetro y que se evalúa al definir la función o el método.
Con balance=0 en el inicializador, Account("Rae") comienza con cero sin proporcionar un argumento de saldo.
Aserción
Una aserción es una comprobación de desarrollo que lanza AssertionError si su condición es falsa; Python puede omitirla en una ejecución con optimización.
Después de un depósito, assert acct.balance == 75 comprueba el comportamiento; las entradas rechazadas del código que realiza la llamada siguen necesitando validación explícita.
Atributo
Un atributo es un valor con nombre al que se accede a través de un objeto o una clase con notación de punto, como el título almacenado de un libro.
Asignar book.title = "Emma" almacena un título que después puede leerse evaluando book.title.
Atributo de almacenamiento de una propiedad
Un atributo de almacenamiento de una propiedad almacena el valor que gestiona una propiedad con un nombre distinto y evita el acceso recursivo a la propiedad.
El método de consulta de score lee _score; almacenar en score dentro de su propio método de asignación llamaría repetidamente a ese método.
Atributo de clase
Un atributo de clase se almacena en la clase y pueden encontrarlo las instancias que no tengan un atributo del mismo nombre que lo oculte.
Un ajuste MAX_TOPPINGS de toda la clase puede proporcionar el límite predeterminado que se lee a través de muchos objetos pizza distintos.
Atributo de instancia
Un atributo de instancia se almacena en un objeto concreto y da a esa instancia sus propios datos bajo el nombre del atributo.
Dos pizzas pueden almacenar cada una un size distinto y una lista de ingredientes independiente mediante asignaciones en self.
Atributo interno
Un atributo interno está destinado a usarse dentro de la implementación de una clase y suele indicarse con un guion bajo inicial en su nombre.
En una clase de cuenta, el código que la usa lee una propiedad pública balance, mientras que los métodos propios de la clase gestionan el valor almacenado _balance.

B

Búsqueda de atributos
La búsqueda de atributos es el proceso de encontrar un atributo con nombre; los datos normales de instancia pueden encontrarse antes que un valor de clase.
Si p no tiene un limit propio, leer p.limit puede encontrar el valor compartido limit definido en su clase.

C

Clase
Una clase define un tipo de Python y proporciona una definición común de los atributos y el comportamiento de sus instancias.
Una clase Book puede crear muchos libros, cada uno con un título distinto, pero con las mismas definiciones de métodos.
Composición
La composición construye un objeto mayor a partir de otros objetos almacenados en sus atributos o colecciones y modela una relación «tiene».
Una biblioteca puede almacenar objetos libro y cada libro mantiene su título y la información de su prestatario actual.
Condición de ejecución directa
Una condición de ejecución directa comprueba si un módulo se está ejecutando directamente e impide que las acciones de inicio se ejecuten al importar el módulo.
Coloca main() bajo if __name__ == "__main__": para que otro programa pueda importar las clases sin iniciarlo.
Contenedor
Un contenedor es un objeto que contiene referencias a otros objetos y permite almacenarlos y procesarlos juntos.
La lista de artículos de un carrito contiene objetos artículo; seguir cart.items[0] permite acceder a su primer artículo almacenado.
Copia profunda
Una copia profunda copia un objeto y sus objetos anidados, separando el estado mutable anidado que una copia superficial puede dejar compartido.
Si una cuenta almacena una lista de transacciones, copy.deepcopy(account) también copia esa lista, por lo que añadir una entrada al historial de la copia deja intacto el original.
Copia superficial
Una copia superficial crea un objeto externo nuevo y conserva las referencias a sus objetos anidados existentes, que pueden seguir compartidos.
Si una cuenta almacena una lista de transacciones, la cuenta creada por copy.copy(account) puede seguir compartiendo esa lista con la original.

D

Delegación
La delegación pasa parte de una tarea al método de otro objeto y permite que ese objeto aplique las reglas de sus propios datos.
Un carrito delega cada subtotal en su artículo y después suma los importes devueltos para obtener el total del carrito.
Diseño de clases
El diseño de clases elige los tipos, el estado, las acciones y las reglas de validez necesarios para modelar un problema con claridad mediante objetos que colaboran.
Planificar una biblioteca empieza con libros y miembros y después coloca un límite de préstamos en una operación de préstamo.

E

Encapsulación
La encapsulación mantiene juntos los datos de un objeto y el comportamiento que los gestiona, y da al código que lo usa una interfaz clara para realizar cambios válidos.
El método de depósito de una cuenta comprueba el importe y actualiza el almacenamiento interno sin que el código que lo llama repita la regla.
Estado
El estado de un objeto son los datos actuales de sus atributos, que los métodos pueden inspeccionar y actualizar durante la ejecución del programa.
Después de un depósito aceptado en una cuenta que mantiene un historial, el estado de la cuenta incluye tanto su saldo nuevo como la entrada añadida al historial.

F

Función de clave de ordenación
Una función de clave de ordenación extrae el valor que una operación de ordenación debe comparar para cada elemento y selecciona un orden para esa tarea concreta.
Un informe puede llamar a sorted(records, key=by_date) sin cambiar el método de comparación habitual de la clase de registros.

I

Identidad
La identidad distingue un objeto de todos los demás; el operador is comprueba si dos referencias acceden a ese mismo objeto.
Dos listas creadas por separado pueden cumplir a == b mientras que a is b es falso porque sus identidades difieren.
Igualdad de valores
La igualdad de valores compara objetos por sus datos significativos, normalmente mediante __eq__, en lugar de exigir que tengan la misma identidad de objeto.
Dos puntos independientes con las mismas coordenadas pueden considerarse iguales y coincidir durante una comprobación de pertenencia a una lista.
Inicialización
La inicialización establece el estado inicial de un objeto recién creado, normalmente almacenando argumentos y valores iniciales como atributos.
El inicializador de una cuenta puede almacenar su titular, aceptar un saldo inicial y crear una lista vacía de transacciones.
Instancia
Una instancia es un objeto que pertenece a una clase y contiene el estado particular asociado a ese objeto.
Las llamadas Book() y Book() crean dos instancias de libro independientes, aunque ambas tengan el mismo tipo.
Invariante
Un invariante es una regla que debe seguir siendo verdadera para que el estado de un objeto sea válido, también después de cambios realizados correctamente.
Una clase de puntuaciones puede exigir que cada puntuación permanezca entre 0 y 100 durante la inicialización y las asignaciones posteriores.

M

Método
Un método es una función proporcionada por una clase que puede llamarse sobre un objeto para trabajar con él o con sus datos.
Llamar a "cedar".upper() ejecuta un método de cadena, mientras que scores.append(7) ejecuta un método sobre una lista.
Método de asignación de una propiedad
Un método de asignación de una propiedad es el método que se invoca al asignar a una propiedad y que suele validar el valor propuesto antes de almacenarlo por separado.
Un método de asignación de score que rechaza 105 antes de asignar _score deja intacta una puntuación anterior válida.
Método de consulta
Un método de consulta es un método que informa sobre el estado de un objeto sin cambiarlo de forma intencionada.
El método de consulta area() de un rectángulo lee su anchura y altura y devuelve un número sin cambiar su tamaño.
Método de instancia
Un método de instancia es una función definida en una clase que recibe el objeto que realiza la llamada, por convención a través de su primer parámetro, self.
Para r.area(), Python proporciona r al método para que pueda multiplicar la anchura y la altura de ese rectángulo.
Método especial
Un método especial tiene un nombre con dos guiones bajos a cada lado y proporciona comportamiento para la sintaxis de Python o las operaciones incorporadas.
Python solicita __str__ al convertir un objeto a su forma de cadena legible y __len__ para obtener su longitud.
Método modificador
Un método modificador es un método que cambia el estado de un objeto existente y suele comprobar las entradas antes de actualizar los atributos relacionados.
Un método modificador de retirada con comprobaciones puede reducir el saldo y registrar una transacción solo después de que el importe supere la validación.
Método vinculado
Un método vinculado es un objeto método que recuerda tanto la función como la instancia que debe proporcionarse cuando se llama al método.
r.area obtiene un método vinculado; añadir paréntesis como en r.area() lo ejecuta y devuelve el área calculada.
Mutabilidad
La mutabilidad es la capacidad de un objeto para cambiar su contenido y seguir siendo el mismo objeto con la misma identidad.
Añadir un elemento a una lista modifica la lista existente; aplicar upper() a una cadena devuelve una cadena nueva.

N

None
None es el valor único de Python para un resultado ausente o no disponible y también sirve para una relación que todavía no tiene ningún objeto asignado.
Un libro puede almacenar borrower = None hasta que un miembro lo tome prestado, así que el código lo comprueba antes de leer el nombre del prestatario.
Notación de punto
La notación de punto es la sintaxis que coloca un punto entre un objeto o una clase y el nombre del atributo al que se accede.
En cart.items, el nombre anterior al punto identifica el objeto y el nombre posterior selecciona su atributo de artículos.
NotImplemented
NotImplemented es un valor de retorno especial que indica que un método de comparación o numérico no admite los operandos proporcionados.
El método de igualdad de un punto puede devolver NotImplemented para una cadena y dejar que Python gestione el par no admitido.

O

Objeto
Un objeto es un valor de Python con una identidad, un tipo y un estado, junto con las operaciones que admite su tipo.
La cadena "hello" y la lista [2, 5] son objetos, aunque sus tipos ofrecen operaciones distintas.
Objeto receptor
El objeto receptor es el objeto anterior al punto en una llamada a método de instancia, que se pasa automáticamente como argumento self del método.
En sender.transfer_to(receiver, 5), sender recibe la llamada de transferencia; el argumento de cuenta es el destino.
Ocultación de nombres
La ocultación de nombres ocurre cuando un atributo de datos normal propio de una instancia oculta un atributo de clase del mismo nombre durante la búsqueda.
Después de p.limit = 8, cambiar Pizza.limit no sustituye el límite independiente ya almacenado en p.
Operación reflejada
Una operación reflejada permite que el operando derecho gestione una operación cuando el operando izquierdo no admite el par.
Para 3 * vector, Python puede usar el __rmul__ del vector cuando la multiplicación de enteros no puede gestionar ese vector.

P

Propiedad
Una propiedad proporciona acceso controlado por métodos mediante la sintaxis de atributos y permite que una lectura calcule un resultado o una asignación ejecute comprobaciones.
Leer student.score puede llamar a un método de consulta y asignar student.score = 90 puede invocar un método de asignación que valide el valor.
Prueba de comportamiento
Una prueba de comportamiento crea objetos, realiza una acción y comprueba un resultado observable, incluido el estado que se conserva después de una entrada rechazada.
Una prueba de retirada captura el error esperado y comprueba que el saldo y el historial sigan coincidiendo con sus valores anteriores.

R

Referencia
Una referencia conecta un nombre o un valor almacenado con un objeto y permite que varios nombres o contenedores accedan al mismo objeto.
Colocar un artículo en dos listas de carrito comparte ese artículo, por lo que cambiar su precio puede afectar a ambos totales calculados.
Representación alternativa predeterminada
La representación alternativa predeterminada permite que la conversión predeterminada a cadena use un __repr__ personalizado cuando una clase no define un __str__ personalizado.
Añadir solo __repr__ puede mejorar tanto print(point) como la apariencia del punto dentro de una lista mostrada.
Representación en cadena
Una representación en cadena es el texto legible que devuelve __str__, normalmente destinado a los usuarios del programa.
Un punto puede mostrarse como "(3, 4)" al visualizarlo directamente, dejando intactos sus atributos numéricos de coordenadas.
Representación para programación
Una representación para programación es el texto inequívoco que devuelve __repr__ y que se usa para la inspección y dentro de la visualización de contenedores.
La representación de un libro puede mostrar "Book('Emma', 'Jane Austen', 1815)", incluidos los campos de texto entrecomillados.
Responsabilidad
La responsabilidad de una clase es la tarea clara que cumplen sus datos y métodos, y ayuda a decidir qué comportamientos deben estar juntos.
Un artículo calcula su propio subtotal, mientras que un carrito organiza los artículos y suma esos subtotales para obtener un total.
Resultado de búsqueda
Un resultado de búsqueda puede ser el objeto coincidente existente o un valor especial como None que indica que no se ha encontrado ninguna coincidencia.
El código que realiza la llamada comprueba found is not None antes de leer found.name en una búsqueda de artículos del carrito.

S

self
El parámetro self recibe por convención la instancia actual y permite que un método lea o modifique los atributos de ese objeto.
En self.owner = owner, el lado izquierdo almacena datos en la instancia y el derecho lee un parámetro local.
Sobrecarga de operadores
La sobrecarga de operadores define qué significa un operador para una clase al implementar el método especial asociado a esa operación.
El __add__ de un vector puede hacer que a + b produzca un vector nuevo formado al sumar sus coordenadas correspondientes.

T

Tipo
Un tipo es la clase de un objeto y define qué operaciones admite y cómo se comportan los valores de esa clase.
type([2, 5]) devuelve la clase list e isinstance([2, 5], list) confirma que el valor es una lista.
Transformación de nombres
La transformación de nombres reescribe los nombres del cuerpo de la clase que tienen dos guiones bajos iniciales y no tienen dos guiones bajos finales para incluir el nombre de la clase.
Dentro de Acc, self.__balance se convierte en _Acc__balance, al que se puede seguir accediendo con el nombre transformado.
TypeError
Un TypeError indica que una operación ha recibido un tipo inadecuado o se ha llamado con argumentos que su definición no acepta.
Pasar texto a una comparación de anchura numérica lanza TypeError porque esa comparación no puede ordenar texto y cero.

V

Validación
La validación comprueba si los datos recibidos o un cambio solicitado cumplen las reglas que exige una clase antes de aceptarlos.
Un rectángulo valida una anchura numérica rechazando el cero o los valores negativos antes de almacenar la dimensión.
Valor de retorno
Un valor de retorno es el objeto que una llamada proporciona a la expresión que la contiene y que puede usarse en una asignación, un cálculo o una comprobación.
Devolver el área permite que r1.area() + r2.area() sume los resultados, mientras que solo mostrarla deja None como resultado de cada llamada.
Valor de verdad en un contexto booleano
El valor de verdad en un contexto booleano es la interpretación booleana de un objeto en una condición; sin __bool__, una longitud definida de cero hace que sea falso.
Una lista de reproducción vacía con __len__ y sin un __bool__ personalizado hace que if playlist: omita su cuerpo.
Valor predeterminado mutable
Un valor predeterminado mutable es un objeto predeterminado modificable que puede reutilizarse entre llamadas y causar que se compartan datos por accidente si se almacena o modifica.
Si items=[] se almacena directamente, dos objetos creados sin ese argumento pueden referirse a la misma lista.
ValueError
Un ValueError indica que un argumento tiene un tipo aceptable, pero su valor está fuera de lo que acepta la operación.
Una anchura numérica de -2 puede lanzar ValueError con el mensaje "width must be positive".

#

__init__
El método __init__ inicializa una instancia después de que Python la cree y debe devolver None cuando termina la inicialización.
Llamar a Counter(5) pasa la instancia nueva y 5 a su inicializador antes de devolver el contador configurado.
__lt__
El método __lt__ implementa la comparación menor que y proporciona un orden que pueden usar operaciones como sorted.
Un __lt__ basado en la puntuación permite ordenar una lista de objetos estudiante de menor a mayor puntuación.

Sobre esta unidad

Por StudeliInformar de un error

Crea tus propios Materiales de Estudio

Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.

Comenzar