{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "view-in-github", "colab_type": "text" }, "source": [ "\"Open" ] }, { "cell_type": "markdown", "id": "ae68466d", "metadata": { "id": "ae68466d" }, "source": [ "# Atributos de clase y atributos de instancia\n" ] }, { "cell_type": "markdown", "source": [ "* Los **atributos de clase** son compartidos por todas las instancias de una clase, mientras que los **atributos de instancia** son únicos para cada instancia de la clase.\n", "* Los **atributos de clase** se definen dentro de la clase, pero fuera de cualquier método, y se acceden utilizando el nombre de la clase.\n", "* Los **atributos de instancia** se definen dentro del método ```__init__``` de la clase y se acceden utilizando la sintaxis ```self.nombre_atributo``` \n", "* Un ejemplo de la clase Persona con los atributos nombre y edad, donde nombre es un atributo de instancia y edad es un atributo de clase. \n", " - En este ejemplo, esperanza_vida es un atributo de clase porque se define dentro de la clase, pero fuera del método ```__init__``` \n", " - Por otro lado, nombre y edad son atributos de instancia porque se definen dentro del método ```__init__``` y se acceden utilizando la sintaxis ```self.nombre``` y ```self.edad```" ], "metadata": { "id": "zboz3q5JPHTy" }, "id": "zboz3q5JPHTy" }, { "cell_type": "code", "source": [ "class Persona:\n", " # Atributo de clase\n", " esperanza_vida = 80\n", "\n", " def __init__(self, nombre, edad):\n", " # Atributo de instancia\n", " self.nombre = nombre\n", " self.edad = edad\n", "\n", "# Crear dos objetos de la clase Persona\n", "persona1 = Persona(\"Juan\", 30)\n", "persona2 = Persona(\"María\", 25)\n", "\n", "# Acceder a los atributos de cada objeto\n", "print(persona1.nombre) # Salida: \"Juan\"\n", "print(persona1.edad) # Salida: 30\n", "print(persona2.nombre) # Salida: \"María\"\n", "print(persona2.edad) # Salida: 25\n", "\n", "# Acceder al atributo de clase desde la clase\n", "print(Persona.esperanza_vida) # Salida: 80\n", "\n", "# Acceder al atributo de clase desde un objeto\n", "print(persona1.esperanza_vida) # Salida: 80\n", "print(persona2.esperanza_vida) # Salida: 80\n" ], "metadata": { "id": "FN4Fcu6mP-sZ", "outputId": "26aa1c30-3d10-49cd-853b-f13ed84e8ab8", "colab": { "base_uri": "https://localhost:8080/" } }, "id": "FN4Fcu6mP-sZ", "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Juan\n", "30\n", "María\n", "25\n", "80\n", "80\n", "80\n" ] } ] }, { "cell_type": "markdown", "source": [ "Podemos crear **varios objetos** de una misma clase. Cada uno de estos objetos puede tener **diferentes valores para estos atributos**, lo que les confiere sus propias **características**. \n", "\n", "Por ejemplo, podemos tener dos instancias de la clase Perro (bobby y teddy) que tengan atributos como nombre, edad, raza, pelo, ... que sean diferentes y que identifican el carácter propio de cada uno de ellos. \n", "\n", "Tener diferentes valores de los atributos es lo que define el **estado de un objeto**.\n", "\n", "Existen dos tipos de atributos:\n", "- Los **atributos de clase**: se definen en la clase y son comunes a **todos** los objetos de esa clase. \n", "Por ejemplo, para la clase Perro son comunes genero = \"Canis\", orden = \"Carnívora\", cuadrupedo = True.\n", "- Los **atributos de instancia**: son **propios** de cada objeto y pueden tener diferentes valores. Podemos crear atributos durante la instanciación. \n", "Por ejemplo, nombre, edad, raza, pelo,..." ], "metadata": { "id": "GciGevpxPF4v" }, "id": "GciGevpxPF4v" }, { "cell_type": "markdown", "id": "5be2f88e", "metadata": { "id": "5be2f88e" }, "source": [ "En general:\n", "* los atributos de clase son para atributos (y métodos) compartidos por todas las instancias de la clase\n", "* los atributos de instancia son para datos únicos de cada instancia" ] }, { "cell_type": "code", "execution_count": null, "id": "b69677fe", "metadata": { "id": "b69677fe" }, "outputs": [], "source": [ "class Perro:\n", " genero = \"Canis\" # atributos de clase compartidos por todas las instancias\n", " orden = \"Carnívora\"\n", " cuadrupedo = True\n", "\n", " def __init__(self, nombre, raza):\n", " self.nombre = nombre # atributos de instancia únicos para cada instancia\n", " self.raza = raza\n", "\n", "l = Perro('Laika', 'Siberiana')\n", "k = Perro('Kasper', 'Pastor alemán')" ] }, { "cell_type": "code", "execution_count": null, "id": "caf0ca31", "metadata": { "id": "caf0ca31", "outputId": "82b459d1-1788-4a47-be2e-87c7e5c9df4d", "colab": { "base_uri": "https://localhost:8080/", "height": 36 } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'Canis'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 2 } ], "source": [ "l.genero # compartido por todos los perros" ] }, { "cell_type": "code", "execution_count": null, "id": "7114af6a", "metadata": { "id": "7114af6a", "outputId": "fc259e7b-9f69-4151-dc6a-c3b83f3e772f", "colab": { "base_uri": "https://localhost:8080/", "height": 36 } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'Canis'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 3 } ], "source": [ "k.genero # compartido por todos los perros" ] }, { "cell_type": "code", "execution_count": null, "id": "eb74cba8", "metadata": { "id": "eb74cba8", "outputId": "ae2d5a61-0611-49bd-beeb-efcdb6a5ade3", "colab": { "base_uri": "https://localhost:8080/", "height": 36 } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'Laika'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 4 } ], "source": [ "l.nombre # único para l" ] }, { "cell_type": "code", "execution_count": null, "id": "db796c1b", "metadata": { "id": "db796c1b", "outputId": "2d1f6d59-fc84-4172-87a5-fccade6e9625", "colab": { "base_uri": "https://localhost:8080/", "height": 36 } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'Kasper'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 5 } ], "source": [ "k.nombre # único para k" ] }, { "cell_type": "markdown", "id": "f06f8660", "metadata": { "id": "f06f8660" }, "source": [ "## Crear una variable como atributo de clase o de instancia\n", "* Al definir una variable nueva podemos tener dudas sobre si debe ser un atributo de clase o un atributo de instancia.\n", "* Los datos compartidos pueden tener efectos inesperados no deseados al usar objetos mutables, tales como pueden ser listas y diccionarios.\n", " \n", "### Ejemplo \n", "Supongamos que para recoger los trucos que saben hacer los perros creamos una variable *trucos* en forma de lista. Nuestro error será crearla como atributo de clase, común para todas las instancias, cuando debiera haber sido creado como atributo de instancia." ] }, { "cell_type": "markdown", "source": [ "#### Forma incorrecta\n", "* Usando el atributo trucos fuera del constructor estamos creando una forma incorrecta de hacerlo\n", "* ya que los trucos que se aprenda un perro se asignarán también al otro perro, y esto no debiera ser así.\n", "* Los trucos de cada perro deberían estar separados" ], "metadata": { "id": "pPDPckZzQTWo" }, "id": "pPDPckZzQTWo" }, { "cell_type": "code", "execution_count": null, "id": "e656b07d", "metadata": { "id": "e656b07d", "outputId": "9ef3f6dc-e457-4c40-f244-692641cf3843", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "['darse la vuelta', 'hacerse el muerto']" ] }, "metadata": {}, "execution_count": 6 } ], "source": [ "class Perro:\n", " trucos = [] # uso incorrecto de una variable de clase\n", "\n", " def __init__(self, nombre):\n", " self.nombre = nombre\n", "\n", " def incluir_truco(self, truco):\n", " self.trucos.append(truco)\n", "\n", "l = Perro('Laika')\n", "k = Perro('Kasper')\n", "\n", "l.incluir_truco('darse la vuelta')\n", "k.incluir_truco('hacerse el muerto')\n", "l.trucos # inesperadamente compartido por todas las instancias de la misma clase" ] }, { "cell_type": "markdown", "id": "3c76d1cb", "metadata": { "id": "3c76d1cb" }, "source": [ "#### Forma correcta de usar la variable como atributo de instancia" ] }, { "cell_type": "code", "execution_count": null, "id": "2d7c398a", "metadata": { "id": "2d7c398a", "outputId": "1dfa2607-199d-4ed2-d07d-a709dc5a9e46", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "['darse la vuelta']\n", "['hacerse el muerto']\n" ] } ], "source": [ "class Perro:\n", " def __init__(self, nombre):\n", " self.nombre = nombre\n", " self.trucos = [] # creamos una lista vacía para cada perro\n", "\n", " def incluir_truco(self, truco):\n", " self.trucos.append(truco)\n", "\n", "l = Perro('Laika')\n", "k = Perro('Kasper')\n", "\n", "l.incluir_truco('darse la vuelta')\n", "k.incluir_truco('hacerse el muerto')\n", "print(l.trucos) # ahora los trucos que sabe hacer cada perro están separados\n", "print(k.trucos)" ] }, { "cell_type": "markdown", "id": "e38d9b83", "metadata": { "id": "e38d9b83" }, "source": [ "## Modificación de atributos de clase\n", "Se pueden modificar los atributos de clase desde fuera de la propia clase.\n", "Si existe el mismo nombre de atributo en la clase y en la instancia se prioriza el de la instancia." ] }, { "cell_type": "code", "execution_count": null, "id": "dd02a6b3", "metadata": { "id": "dd02a6b3", "outputId": "a1ee5e89-7df6-4c7b-a123-a18a0c7e624c", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "False Norte\n" ] } ], "source": [ "class Bomberos:\n", " helicoptero = False # atributos de clase ¿compartidos por todas las instancias?\n", " region = 'Norte' # ¿Todas las estaciones de bombero que se instancian serán del Norte y sin helicóptero?\n", "\n", "b1 = Bomberos() # Instanciamos la primera estación de bomberos y resulta ser del Norte y sin helicóptero\n", "print(b1.helicoptero, b1.region)" ] }, { "cell_type": "code", "execution_count": null, "id": "e8b3e14b", "metadata": { "id": "e8b3e14b", "outputId": "5272a27b-bec6-4323-9488-ed2fce056da6", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "True Norte\n" ] } ], "source": [ "b2 = Bomberos() # Instanciamos la segunda estación de bomberos\n", "b2.helicoptero = True # Podemos acceder a modificar el atributo de clase desde fuera de la clase\n", "print(b2.helicoptero, b2.region) # Ahora vemos que esta estación de bomberos si tiene helicóptero" ] }, { "cell_type": "code", "source": [ "# cambiar el atributo de b2 no afecta a b1\n", "print(b1.helicoptero) # b1 continúa sin tener helicópteros" ], "metadata": { "id": "qfW8PFW8fc6e", "outputId": "3b564119-8a45-480d-8572-23ef8ed44e28", "colab": { "base_uri": "https://localhost:8080/" } }, "id": "qfW8PFW8fc6e", "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "False\n" ] } ] }, { "cell_type": "markdown", "id": "9c6427c3", "metadata": { "id": "9c6427c3" }, "source": [ "Python, por defecto, no impide, salvo por convenio, que un usuario pueda modificar un atributo de clase desde fuera de su propia clase. \n", "En otros lenguajes, por ejemplo, en JAVA cualquier atributo que se declara ha de ser público (Public) o privado (Private), de forma que los privados no son accesibles desde fuera de su propia clase." ] }, { "cell_type": "markdown", "id": "036f241f", "metadata": { "id": "036f241f" }, "source": [ "## Invocar a un método desde otro método\n", "Al igual que una función puede desde el interior de su código llamar a otra función, un método (que es una función) puede llamar a otro método ambos dentro de una clase." ] }, { "cell_type": "code", "execution_count": null, "id": "392bc795", "metadata": { "id": "392bc795", "outputId": "36b4548d-29d4-4766-81c3-e1027335db0b" }, "outputs": [ { "data": { "text/plain": [ "['Guppy']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Acuario:\n", " def __init__(self):\n", " self.poblacion = [] # atributo población en forma de lista, inicialmente vacía\n", "\n", " def incluir(self, pez):\n", " self.poblacion.append(pez)\n", "\n", " def incluir_pareja(self, pez):\n", " self.incluir(pez) # invocamos desde un método otro método dentro de la misma clase\n", " self.incluir(pez) # volvemos a invocar el método incluir para hacer la pareja\n", "\n", "a = Acuario() # instanciamos un objeto de tipo Acuario\n", "a.incluir('Guppy') # Añadimos nuestro primer pez\n", "a.poblacion # consultamos nuestra variable población" ] }, { "cell_type": "code", "execution_count": null, "id": "dcc2bb47", "metadata": { "id": "dcc2bb47", "outputId": "9cc6d935-9eb7-4e33-c370-42d7a7d18a7f" }, "outputs": [ { "data": { "text/plain": [ "['Guppy', 'Neón', 'Neón']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.incluir_pareja('Neón') # incluimos una pareja de peces\n", "a.poblacion # al consultar la población vemos que se ha añadido la pareja" ] }, { "cell_type": "markdown", "id": "8015665a", "metadata": { "id": "8015665a" }, "source": [ "## Aplicar a un objeto ```type``` y ```dir```" ] }, { "cell_type": "code", "execution_count": null, "id": "13eb5e00", "metadata": { "id": "13eb5e00", "outputId": "ea950f9b-1c68-4fb1-cf9a-6b1df96afaf0" }, "outputs": [ { "data": { "text/plain": [ "__main__.Acuario" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(a) # la instancia 'a' es un objeto de tipo Acuario" ] }, { "cell_type": "code", "execution_count": null, "id": "81a9d1c5", "metadata": { "id": "81a9d1c5", "outputId": "f4de721b-8605-4fbb-fb42-1ac7b700a866" }, "outputs": [ { "data": { "text/plain": [ "method" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(a.incluir) # el método 'incluir' es de método" ] }, { "cell_type": "code", "execution_count": null, "id": "e6ba11cd", "metadata": { "id": "e6ba11cd", "outputId": "82af54a3-c65f-4bc6-b60d-33b86cbb1dcf" }, "outputs": [ { "data": { "text/plain": [ "list" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(a.poblacion) # el atributo 'poblacion' es una variable de tipo lista" ] }, { "cell_type": "code", "execution_count": null, "id": "34b74238", "metadata": { "id": "34b74238", "outputId": "d0a7cb9b-f19a-4e73-ca1e-ae74f7274296" }, "outputs": [ { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'incluir',\n", " 'incluir_pareja',\n", " 'poblacion']" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(a) # al hacer un dir de un objeto obtenemos todos sus atributos y métodos incluidos los especiales" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" }, "colab": { "name": "calisto2_0040.ipynb", "provenance": [], "include_colab_link": true } }, "nbformat": 4, "nbformat_minor": 5 }