{"id":21754111,"url":"https://github.com/csdev19/python-methods-instance-class-and-static","last_synced_at":"2025-03-21T03:13:43.009Z","repository":{"id":111232750,"uuid":"146355004","full_name":"csdev19/Python-Methods-Instance-Class-and-Static","owner":"csdev19","description":"Una explicacion de los metodso de clase, de instancia y estaticos","archived":false,"fork":false,"pushed_at":"2018-08-27T21:34:22.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-01-25T23:39:51.011Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/csdev19.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-27T21:12:40.000Z","updated_at":"2018-08-27T21:44:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"be64637a-f5f4-45a1-b935-9c44ea66f385","html_url":"https://github.com/csdev19/Python-Methods-Instance-Class-and-Static","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csdev19%2FPython-Methods-Instance-Class-and-Static","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csdev19%2FPython-Methods-Instance-Class-and-Static/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csdev19%2FPython-Methods-Instance-Class-and-Static/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csdev19%2FPython-Methods-Instance-Class-and-Static/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/csdev19","download_url":"https://codeload.github.com/csdev19/Python-Methods-Instance-Class-and-Static/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244728234,"owners_count":20500023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-26T09:12:52.858Z","updated_at":"2025-03-21T03:13:43.004Z","avatar_url":"https://github.com/csdev19.png","language":"Python","readme":"# Python's Instance, Class and Static Methods Desmitificados\n\n## Tabla de contenidos\n\n- [Instance, Class and Static Methods - Una revision general](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#intance-class-and-static-methods---una-revision-general)\n    - [Instance Methods](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#intance-methods-metodos-de-instancia)\n    - [Class Methods](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#class-methods-metodos-de-clase)\n    - [Static Methods](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#static-methods-metodos-estaticos)\n- [Vamos a verlos en accion](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#vamos-a-verlos-en-accion)\n- [Fabricas de deliciosa Pizza con @classmethod](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#delicius-pizza-factories-with-classmethod)\n- [Cuando usar Static Methods](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#cuando-usar-static-methods)\n- [Puntos Claves](https://github.com/pystudent1913/Python-Methods-Intance-Class-and-Static#key-takeaways)\n\n\nEn este tutorial vamos intentar desmitificar que pasa detras de [class methods (metodos de clase)](https://docs.python.org/3/library/functions.html#classmethod), [static methods (metodos estaticos)](https://docs.python.org/3/library/functions.html#staticmethod), y los metodos de instanciacion regular.\n\nSi tu desarrollas una comprension intuitiva de sus diferencias, podras ser capas de escribir **python orientado a objetos** que transmite lo que quieres decir de manera clara y facil de mantener a largo plazo.\n\n\n## Intance, Class and Static Methods - Una revision general\n\nVamos a empezar por escribir una clase (de Python3) que contiene ejemplos simples para estos tres tipos de metodos:\n\n```python\nclass MiClase:\n    def metodo(self):\n        return 'metodo de instancia llamado', self\n\n    @classmethod\n    def metodoDeClase(cls):\n        return 'metodo de clase llamado', cls\n\n    @staticmethod\n    def metodoEstatico():\n        return 'metodo estatico llamado'\n```\n\n### Intance Methods (Metodos de instancia)\n\nEl primer **metodo** en **MiClase**, llamado **metodo**, es un **metodo de instancia regular**. Eso es lo basico, tipo sencillo que se usara la mayor parte del tiempo. Podemos ver que este **metodo** toma un **parametro**, **self**, el cual apunta a una instancia de **MiClase** cuando el metodo es llamado (por supuesto los metodos de instancia pueden aceptar mas de un solo parametro).\n\nMediante el **parametro self**, los **metodos de instancia** pueden acceder libremente a **atributos y metodos** del mismo objeto. Esto les da muchisimo poder cuando debemos modificar el estado de un objeto.\n\nNo solo puede modificar el estado de un objeto, **los metodos de instancia** pueden tambien **acceder a la clase en sí**  a traves del atributo **self.__class__**. Esto significa que los metodos de instancia tambien pueden modificar el estado de la clase.\n\n### Class Methods (Metodos de clase)\n\nVamos a comparar esto con el segundo metodo, **MiClase.metodoDeClase**. Hemos marcado este metodo con un decorador de **@classmethod** para marcarlo como un **metodo de clase**.\n\nEn lugar de aceptar un parametro **self**, **un metodo de clase** toma un parametro **cls**, que **apunta a la clase** -- y no a la instancia del objeto -- cuando el metodo es llamado.\n\nPorque el metodo de clase **solo tiene acceso** a este argumento **cls**, este no puede modificar el estado de la instancia del objeto. Eso podria requerir acceso a **self**. Sin embargo, **un metodo de clase**puede modificar el estado de la clase que se aplica entre todas las instancia de la clase.\n\n### Static Methods (Metodos estaticos)\n\nEl tercer metodo, **Miclase.metodoEstatico** fue marcado con un decorador **@staticmethod** para denotar que es un **metodo estatico**.\n\nEste tipo de metodo no tome ni el parametro **self* o el **cls** (pero por supuesto que es libre de aceptar un numero arbitrario de parametros).\n\nPor lo tanto un **metodo estatico** no puede modificar el estado del objeto ni el estado de la clase. Los metodos estaticos estan retringidos en cuanto a los datos a los que pueden acceder, y son principalmente una forma de ponerle un **namespace** a sus metodos.\n\n\n## Vamos a verlos en accion!\n\nYo se que esta discusion ha sido mas que nada **teorice** hasta este punto. Y yo creo que es importante que se desarrollo un entendimiento intuitivo para como estos tipos de metodos difieren en la partica. Vamos a ver unos ejemplos concretos ahora.\n\nVamos a echar un vistaso a como estos metodos se comportan en accion cuando queremos llamarlos. Empezaremos creando una instancia de la clase y luego llamaremos tres diferentes metodos sobre esta.\n\n**MiClase** se configuro de tal forma para que cada implementacion de metodos retorne una tupla conteniendo informacion para que rastreemos lo que esta sucediendo -- y que partes de la clase o el metodo del objeto puede acceder.\n\n### Aqui esta lo que pasa cuando nosotros llamamos un **intance method**:\n\n```python\n\u003e\u003e\u003e obj = MiObjeto()\n\u003e\u003e\u003e obj.metodo()\n('metodo de instancia llamado', \u003cexample.MiClase object at 0x7efdb5fa5470\u003e)\n```\n\nEsto confirma que **metodo** (el metodo de instancia) a accesado a la instancia del objeto (escrito como **example.Miclase**) mediante el argumento **self**.\n\nCuando el **metodo** es llamado, Python reemplaza el argumento **self** con la **instancia del objeto**, **obj**. Podemos ignorar la **azucar sintactica**( **syntactic sugar** lo vamos a llamar asi mejor) de la sintaxis **de llama punto** ( **obj.method()** ) y pasando el **objeto de instancia** manualmente para obtener el mismo resultado:\n\n```python\n\u003e\u003e\u003e MiClase.metodo(obj)\n('metodo de instancia llamado', \u003c__main__.MiClase object at 0x7fe620869f28\u003e)\n```\n\nPueden adivinar que pasaria si intentamos llamar al metodo si no llamamos primero a una instancia?\n\nPor cierto, los **metodos de instancia** pueden acceder a la **clase** como tal a traves del atributo **self.__class__**. Esto hace que **el metodo de instancia** sea poderoso en terminos de restriccion de accesos - estos pueden modificar el estado de la instancia de un objeto y en la clase como tal.\n\n### Vamos a intentar con **class method** acontinuacion:\n```python\n\u003e\u003e\u003e obj.metodoDeClase()\n('metodo de clase llamado', \u003cclass '__main__.MiClase'\u003e)\n```\n\nLlamando **classmethod()** nos muestra que no tiene acceso al objeto **\u003c MiClase instance \u003e**, pero si al objeto **\u003c class MiClase \u003e**, representando la clase en si (todo en python es un objeto, incluso las clases como tales).\n\nDate cuenta como **Python** automaticamente pasa la clase como primer argumento a la funcion cuando llamamos **MiClase.metodoDeClase()**. Llamando un metodo en **Python** a traves de la **sintaxis punto** desencadenando este comportamiento. El parametro **self** en el metodo de instancia trabaja de la misma manera.\n\n\n### Vamos a llamar al **static method** ahora:\n```python\n\u003e\u003e\u003e obj.staticmethod()\n'metodo estatico llamado'\n```\n\nViste como llamamos al **staticmethod** sobre el objeto y fuimos capaces de hacerlo exitosamente ? Algunos desarroladores se sorprenden cuando aprenden que es posible **llamar un metodo estatico** sobre una instancia de objeto.\n\nDentras de escenas **Python** simplemente fuerza las restricciones de acceso al no pasar de argumentos **self** o **cls** cuando un metodo estatico el llamado usando la sintaxis punto.\n\nEsto confirma que los **metodos estaticos** nunca podran tener acceso a la instancia del objeto o al estado de la clase. Estos trabajaran como **funciones regulares** pero pertenece al **namespace** de clase (y al de cualquier instancia).\n\nAhora, vamos a que pasa cuando intentamos llamar a estos metodos en la clase como tal - sin crear una instancia del objeto antes:\n\n```python\n\u003e\u003e\u003e MiClase.metodoDeClase()\n('metodo de clase llamado', \u003cclass '__main__.MiClase'\u003e)\n\u003e\u003e\u003e MiClase.metodoEstatico()\n'static method called'\n\u003e\u003e\u003e MiClase.metodo()\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nTypeError: metodo() missing 1 required positional argument: 'self'\n```\n\nSomos capaces de llamar el **classmethod()** y al **staticmethod()** bastante bien, pero cuando intentamos llamar el metodo de instancia **metodo()** nos lanza un error de tipo **TypeError**.\n\nY esto es de esperar - esta vez no creamos una instancia de objeto y intentamos llamar una **funcion de instancia** directamente en la clase **blueprint** como tal. Esto significa que no hay forma de que **Python** complete el argumento **self** y por lo tanto la llamada falla.\n\nEsto deberia hacer la diferencia entre estos 3 tipos de metodos mas clara. Pero para no dejarlo solo asi. En las siguientes dos secciones vamos a ver 2 ejemplos mas practicos para saber cuando usar estos **tipos de metodos especiales**.\n\nVamos a basar los ejemplos en esta **clase Pizza**:\n\n```python\nclass Pizza:\n    def __init__(self, ingredients):\n        self.ingredients = ingredients\n\n    def __repr__(self):\n        return f'Pizza({self.ingredients!r})'\n\n\u003e\u003e\u003e Pizza(['cheese', 'tomatoes'])\nPizza(['cheese', 'tomatoes'])\n```\n**NOTA**: este codigo de ejemplo y los que estan mas adelante en el tutorial usan [Python 3.6 f-strings](https://dbader.org/blog/python-string-formatting) para construir el **string** que retorna **__repr__**. En **Python2** y versiones de **Python3** antes de **3.6** podriamos usar una expresion de formacion de **string** distinta, por ejemplo:\n\n```python\ndef __repr__(self):\n    return 'Pizza(%r)' % self.ingredients\n```\n\n## Delicius Pizza factories with @classmethod\n\nSi tu has sido expuesto a la pizza en el mundo real tu deberias saber que hay muchas deliciosas variaciones disponibles.\n\n```python\nPizza(['mozzarella', 'tomatoes'])\nPizza(['mozzarella', 'tomatoes', 'ham', 'mushrooms'])\nPizza(['mozzarella'] * 4)\n```\n\nLos italianos descubrieron su taxonomía de la pizza hace siglos, por lo que estos deliciosos tipos de pizzas tienen sus propios nombres. Haríamos bien en aprovechar eso y darles a los usuarios de nuestra clase de Pizza una mejor interfaz para crear los objetos de pizza que anhelan.\n\nUno buena y limpia forma de hacer esto es usando un **metodo de clase** como una [factory functions](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)) para los diferentes tipos de pizzas podemos crear:\n\n```\nclass Pizza:\n    def __init__(self, ingredients):\n        self.ingredients = ingredients\n\n    def __repr__(self):\n        return f'Pizza({self.ingredients!r})'\n\n    @classmethod\n    def margherita(cls):\n        return cls(['mozzarella', 'tomatoes'])\n\n    @classmethod\n    def prosciutto(cls):\n        return cls(['mozzarella', 'tomatoes', 'ham'])\n```\n\nNoten como estamos usando el argumento **cls** en los metodos de factoria **marguerita y prosciutto** en lugar de llamar el cosntructor **Pizza** directamente.\n\nEste es un truco que podemos usar para seguir el principio [Don't Repeat Yourself (DRY)](https://en.wikipedia.org/wiki/Don't_repeat_yourself). Si es que nosotros decidimos renombrar esta clase en algun punto no debemos recordar actualizar el nombre del constructor en todos los demas **classmethods factory functions**.\n\nAhora, que podemos hacer con estos **factory methods** ? vamos a probarlos:\n\n```python\n\u003e\u003e\u003e Pizza.margherita()\nPizza(['mozzarella', 'tomatoes'])\n\n\u003e\u003e\u003e Pizza.prosciutto()\nPizza(['mozzarrella', 'tomatoes', 'ham'])\n```\n\nComo tu puedes ver, podemos usar las **factory functions** para crear nuevos objetos **Pizza** que son configurados de la manera que nosotros queremos. Estos usan el mismo constructor **__init__** internamente y simplemente provee un shortcut para recordar todos los varios ingredientes.\n\nOtra manera de ver este usa pero **metodos de clase** es cuando nos permiten definir constructores alternativos para nuestras clases.\n\nPython permite un metodo __init__ por clase. Usando **metodos de clase** es posible agregar constructores alternativos como sean necesarios. Esto puede hacer la interface para nuestras clases **autodocumentativas** (hasta cierto punto) y simplificar su uso.\n\n## Cuando usar Static Methods\n\nEs un poco mas dificil tener un buen ejemplo aqui.\n\n```python\nimport math\n\nclass Pizza:\n    def __init__(self, radius, ingredients):\n        self.radius = radius\n        self.ingredients = ingredients\n\n    def __repr__(self):\n        return (f'Pizza({self.radius!r}, '\n                f'{self.ingredients!r})')\n\n    def area(self):\n        return self.circle_area(self.radius)\n\n    @staticmethod\n    def circle_area(r):\n        return r ** 2 * math.pi\n```\n\nAhora que es lo que cambiamos aqui ? Primero, modificamos el constructor y **__repr__** para aceptar como argumento extra el **radio**.\n\nY tambien agregamos un **metodo de instancia area()** que calcula y retorna el area de la pizza (esto podria ser un buen candidato para una **@property** - pero este es solo un ejemplo).\n\nEn lugar de calcular el area directamente dentro del **area()**, usando la conocida formula del area del circulo, la descomponemos en un metodos estatico **circle_area()**.\n\nVamos a probarlo:\n\n```python\n\u003e\u003e\u003e p = Pizza(4, ['mozzarella', 'tomatoes'])\n\u003e\u003e\u003e p\nPizza(4, ['mozzarella', 'tomatoes'])\n\u003e\u003e\u003e p.area()\n50.26548245743669\n\u003e\u003e\u003e Pizza.circle_area(4)\n50.26548245743669\n```\n\nSeguro, esto es un ejemplo un poco simplista, pero ayuda a explicar algunos de los beneficios que los **metodos estaticos** proveen.\n\nComo hemos aprendido, **metodos estaticos** no pueden accesar a la clase o al estado de la instancia porque no toman de argumento **cls o self**. Esa es una gran limitacion - pero es una buena señal para mostrar como un metodo en particular es independiente de todo lo que le rodea.\n\nEn el ejemplo anterior, es que claro que **circle_area()** no puede modificar la clase o la instancia de la clase en ninguna forma. (seguro, siempre podremos trabajar esto con una variable global pero ese no es el punto aqui).\n\nAhora, porque esto es util ?\n\nMarcando un metodo como uno estatico no es solo una sugrenecia de que un metodo no modificara la clase o el estado de la instancia; esta restriccion tambien se aplica por al **runtime de Python**.\n\nTecnicas como estas nos permite comunicar claramete las partes acerca de la arquitectura de nuestra clase etonces este nuevo trabajo de desarrollo es mas naturalmente guiado para pasar dentro de estos limites establecidos. Por supuesto, esto podria ser bastante facil desafiar estas restricciones. Pero en la practica suelen ayudar a evitar modificaciones accidentales que van en contra del diseño original.\n\nPuesto de otra forma, usando **metodos estaticos** y **metodos de clase** son formas de comunicar la intencicon del desarrollador mientras aplica esta intencion lo suficiente para evitar la mayoria de errores y **bugs** que podrian romper el diseño.\n\nSe aplica con moderacion y cuando tiene sentido, escribir algunos de sus metodos de esa manera puede proporcionar beneficios de mantenimiento y hacer que sea menos probable que otros desarrolladores utilicen sus clases de forma incorrecta.\n\n**Metodos estaticos** tambien tienen beneficios cuando empezamos a escribir codigo de testeo. Porque el metodo **circle_area()** es completamente independiente del resto de la clase es mucho mas facil de testear.\n\nNosotros no tenemos que preocuparnos acerca de configurar una instancia de clase completa antes podemos testear el metodo en un **unit test**. Podemos simplemente disparar como si estuvieramos probando una funciona regular. Nuevamente, esto hace que el mantenimiento futuro sea mas facil.\n\n## Key Takeaways\n\n- Metodos de instancia necesitan una clase instanciada y pueden accesar a la instancia a traves de **self**.\n- Metodos de clase no necesitan una clase instanciada. Estos no pueden acceder a la instancia (self) pero tienen acceso a la clase como tal via **cls**.\n- Metodos estaticos no tienen acceso a la clase **cls** o **self**. Estos trabajan como expresiones regulares pero extendiendo el namespace de la clase.\n- Metodos estaticos y de clase comunican y (hasta cierto grado) impoenen la intencion del desarrollador sobre el diseño de clase. Esto puede tener beneficios en el mantenimiento.\n\n\n## Referencia\n\n- Este repo esta basado en la pagina REAL PYTHON [instance class and static methods](https://realpython.com/instance-class-and-static-methods-demystified/) mas bien es una traduccion. por el bien de la comunidad XD.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcsdev19%2Fpython-methods-instance-class-and-static","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcsdev19%2Fpython-methods-instance-class-and-static","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcsdev19%2Fpython-methods-instance-class-and-static/lists"}