it-swarm-es.tech

¿Por qué necesita explícitamente tener el argumento "self" en un método Python?

Al definir un método en una clase en Python, se ve algo como esto:

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

Pero en algunos otros idiomas, como C #, tiene una referencia al objeto al que está vinculado el método con la palabra clave "this" sin declararlo como un argumento en el prototipo del método.

¿Fue esta una decisión de diseño de lenguaje intencional en Python o hay algunos detalles de implementación que requieren la aprobación de "self" como argumento?

182
Readonly

Me gusta citar el Zen de Python de Peters. "Explícito es mejor que implícito".

En Java y C++, se puede deducir 'this.', excepto cuando tienes nombres de variables que hacen que sea imposible de deducir. Así que a veces lo necesitas y otras veces no.

Python elige hacer que cosas como estas sean explícitas en lugar de estar basadas en una regla.

Además, dado que nada está implícito o asumido, partes de la implementación están expuestas. self.__class__, self.__dict__ y otras estructuras "internas" están disponibles de manera obvia.

88
S.Lott

Es para minimizar la diferencia entre métodos y funciones. Le permite generar fácilmente métodos en metaclases, o agregar métodos en tiempo de ejecución a clases preexistentes.

p.ej.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

También (hasta donde sé) facilita la implementación del tiempo de ejecución de python.

57
Ryan

Sugiero que se debe leer el blog de Guido van Rossum sobre este tema - Por qué el yo explícito tiene que quedarse .

Cuando una definición de método está decorada, no sabemos si asignarle automáticamente un parámetro 'self' o no: el decorador podría convertir la función en un método estático (que no tiene 'self') o en un método de clase (que tiene un tipo de yo divertido que se refiere a una clase en lugar de una instancia, o podría hacer algo completamente diferente (es trivial escribir un decorador que implemente '@classmethod' o '@staticmethod' en Python puro). No hay forma de saber lo que hace el decorador, ya sea para dotar o no al método definido con un "yo" implícito.

Rechazo hacks como la carcasa especial '@classmethod' y '@staticmethod'.

51
bhadra

Python no te obliga a usar "self". Puedes darle el nombre que quieras. Solo debe recordar que el primer argumento en un encabezado de definición de método es una referencia al objeto.

16
Victor Noagbodji

También le permite hacer esto: (en resumen, invocar Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) devolverá 12, pero lo hará de la manera más loca.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Por supuesto, esto es más difícil de imaginar en lenguajes como Java y C #. Al hacer explícita la referencia a sí mismo, usted es libre de referirse a cualquier objeto por esa referencia a sí mismo. Además, esta forma de jugar con las clases en tiempo de ejecución es más difícil de hacer en los lenguajes más estáticos, no necesariamente es bueno o malo. Es solo que el yo explícito permite que toda esta locura exista.

Además, imagine esto: nos gustaría personalizar el comportamiento de los métodos (para perfilar, o alguna magia negra loca). Esto nos puede llevar a pensar: ¿qué pasaría si tuviéramos una clase Method cuyo comportamiento podríamos anular o controlar?

Pues aquí está:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

Y ahora: InnocentClass().magic_method() actuará como se espera. El método se vinculará con el parámetro innocent_self a InnocentClass, y con magic_self a la instancia de MagicMethod. Extraño eh? Es como tener 2 palabras clave this1 y this2 en lenguajes como Java y C #. La magia como esta permite a los marcos hacer cosas que de otra manera serían mucho más detalladas.

Una vez más, no quiero comentar sobre la ética de estas cosas. Solo quería mostrar cosas que serían más difíciles sin una referencia explícita de sí mismo.

6
vlad-ardelean

Creo que la verdadera razón, además de "El Zen de Python", es que las Funciones son ciudadanos de primera clase en Python.

Lo que esencialmente los convierte en un objeto. Ahora, el problema fundamental es si sus funciones también son objetos, entonces, en el paradigma orientado a objetos, ¿cómo enviaría mensajes a Objetos cuando los mensajes en sí mismos son objetos?

Parece un problema del huevo de gallina, para reducir esta paradoja, la única forma posible es pasar un contexto de ejecución a los métodos o detectarlo. Pero como Python puede tener funciones anidadas, sería imposible hacerlo, ya que el contexto de ejecución cambiaría para las funciones internas.

Esto significa que la única solución posible es pasar explícitamente "self" (el contexto de ejecución).

Así que creo que es un problema de implementación el Zen vino mucho más tarde.

2
pankajdoharey

Creo que tiene que ver con PEP 227:

Los nombres en el alcance de la clase no son accesibles. Los nombres se resuelven en el ámbito de la función de cierre más interno. Si una definición de clase se produce en una cadena de ámbitos anidados, el proceso de resolución omite las definiciones de clase. Esta regla evita interacciones impares entre los atributos de clase y el acceso a variables locales. Si se produce una operación de enlace de nombre en una definición de clase, crea un atributo en el objeto de clase resultante. Para acceder a esta variable en un método, o en una función anidada dentro de un método, se debe usar una referencia de atributo, ya sea a través de uno mismo o por medio del nombre de la clase.

2
daole

Como se explica en self en Python, Desmitificado

cualquier cosa como obj.meth (args) se convierte en Class.meth (obj, args). El proceso de llamada es automático, mientras que el proceso de recepción no es (es explícito). Esta es la razón por la que el primer parámetro de una función en clase debe ser el objeto en sí.

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

    def distance(self):
        """Find distance from Origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocaciones:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () define tres parámetros, pero acabamos de pasar dos (6 y 8). De forma similar, distance () requiere uno, pero se pasaron cero argumentos.

¿Por qué Python no se queja de este número de argumento no coincide?

Generalmente, cuando llamamos a un método con algunos argumentos, se llama a la función de clase correspondiente colocando el objeto del método antes del primer argumento. Entonces, cualquier cosa como obj.meth (args) se convierte en Class.meth (obj, args). El proceso de llamada es automático mientras que el proceso de recepción no es (es explícito).

Esta es la razón por la que el primer parámetro de una función en la clase debe ser el objeto en sí. Escribir este parámetro como "yo" es simplemente una convención. No es una palabra clave y no tiene un significado especial en Python. Podríamos usar otros nombres (como este), pero le sugiero que no lo haga. El uso de nombres distintos a self está mal visto por la mayoría de los desarrolladores y degrada la legibilidad del código ("la capacidad de lectura cuenta").
...
En, el primer ejemplo self.x es un atributo de instancia mientras que x es una variable local. No son lo mismo y se encuentran en diferentes espacios de nombres.

El yo está aquí para quedarse

Muchos han propuesto a hacer de self una palabra clave en Python, como esto en C++ y Java. Esto eliminaría el uso redundante de self explícito de la lista de parámetros formales en los métodos. Aunque esta idea parece prometedora, no va. que suceda Al menos no en el futuro cercano. La razón principal es la compatibilidad con versiones anteriores. Aquí hay un blog del propio creador de Python que explica por qué el yo explícito tiene que quedarse.

0
mon