Actualmente estoy revisando los tutoriales de python de python.org. Vengo de C ++ y en el tutorial de clases (https://docs.python.org/ 3 / tutorial / classes.html) Veo que el alcance es similar al de C ++. Dice lo siguiente sobre el alcance y la anidación:

"En cualquier momento durante la ejecución, hay al menos tres ámbitos anidados cuyos espacios de nombres son directamente accesibles:
- el ámbito más interno, que se busca primero, contiene los nombres locales
- los ámbitos de cualquier función de cierre, que se busca comenzando con el alcance de cierre más cercano, contiene nombres no locales, pero también no globales
- el penúltimo alcance contiene los nombres globales del módulo actual
- el alcance más externo (último buscado) es el espacio de nombres que contiene los nombres integrados "

Sin embargo, intenté con el siguiente código de la misma página:

class Dog:

    tricks = []             

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

    def add_trick(self, trick):
        self.tricks.append(trick)     #this is the troublesome self

>>> d = Dog('Fido')     
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')    #without the self this complains
>>> e.add_trick('play dead')
>>> d.tricks                
['roll over', 'play dead']

Si elimino el self en self.tricks.append(trick), el código no se compilará y arrojará un NameError: name 'tricks' is not defined al llamar a la función d.add_trick('roll over').

¿Por que sucede? Como entiendo por el párrafo anterior, la función add_trick debe buscar una variable llamada tricks primero dentro de su propio alcance local, luego, si no encuentra ninguna, en el alcance adjunto más cercano, que es el alcance del Class Dog, y allí debería encontrarlo , sin la necesidad de utilizar self. ¿Qué me estoy perdiendo?

0
Leo 5 oct. 2019 a las 18:50

3 respuestas

La mejor respuesta

Como decía el tutorial, los ámbitos se buscan en el orden local, no local, global, integrado.

El alcance no local es para funciones de cierre . Una declaración de clase no es una función. Su espacio de nombres se descarta después de usarse para crear el objeto de clase __dict__, por lo que las variables a nivel de clase no pueden producir elementos no locales en funciones incluidas. Piense en las asignaciones a nivel de clase y las lecturas variables como asignaciones implícitas y lecturas de un dict oculto, en lugar de como funciones locales. (Las metaclases incluso pueden reemplazar este dict oculto con alguna otra asignación).

Pero el alcance de la clase agrega un no local, __class__. Esto rara vez se usa directamente, pero es importante para la forma de argumento cero de super().

Este es el objeto de clase en sí mismo, por lo que no se inicializa hasta que la declaración de clase termine de ejecutarse. Entonces __class__.tricks funcionaría dentro de un método si se llama después de que el cuerpo de la clase se ejecuta (el caso habitual), pero no si se llama durante la ejecución de la clase cuerpo.

Hay otros ámbitos a tener en cuenta en Python. Las comprensiones crean un ámbito local como lo hacen las funciones. (Básicamente se compilan como funciones generadoras, del tipo con yield dentro). Además, las excepciones detectadas se limitan a su cláusula de manejo.

Puede ver el espacio de nombres local utilizando locals() integrado y globales utilizando globals(). El alcance integrado es solo el módulo integrado. Los no locales son complicados. En realidad, aparecerán en locals() si el compilador los ve en uso. Los objetos de función mantienen una referencia a los no locales que usan en su atributo __closure__, que es una tupla de celdas.

1
gilch 5 oct. 2019 a las 16:56

¿Ayuda este ejemplo?

In [27]: foobar = 'pre'                                                         
In [28]: class AClass(): 
    ...:     print('class def:', foobar) 
    ...:     foobar = 'class' 
    ...:     def __init__(self): 
    ...:         print('init:', foobar) 
    ...:         print(self.foobar) 
    ...:         self.foobar='instance' 
    ...:         print(self.foobar) 
    ...:                                                                        
class def: pre                  
In [29]: AClass.foobar                                            
Out[29]: 'class'

La definición de clase es como ejecutar una función. foobar es inicialmente el valor global, pero luego se reasigna y ahora está disponible como parte del espacio de nombres de la clase.

In [30]: x = AClass()                                                           
init: pre
class
instance
In [31]: x.foobar                                                               
Out[31]: 'instance'
In [32]: x.__class__.foobar                                                     
Out[32]: 'class'

Cuando definimos una instancia, foobar proviene del espacio de nombres global externo. self.foobar inicialmente accede al espacio de nombres de la clase, pero luego se redefine para ser una variable de instancia.

Cambiar el foobar global se refleja en la siguiente creación de instancia:

In [33]: foobar = 'post'                                                        
In [34]: y = AClass()                                                           
init: post
class
instance
In [35]: y.__class__.foobar                                                     
Out[35]: 'class'

Su página de tutorial, en la sección 9.3.1. Class Definition Syntax dice que mientras está en la definición de clase, hay un nuevo espacio de nombres local. Ahí es donde se define foobar='class'. Pero una vez fuera de la definición de clase, ese espacio de nombre se renombra como AClass.

La creación de la instancia se ejecuta fuera de la definición de clase. Entonces 've' el foobar global, no el local de la clase. Tiene que especificar el espacio de nombres.

La distinción entre el alcance cuando se define una clase (o función) y cuando se 'ejecuta' puede ser confusa.

0
hpaulj 5 oct. 2019 a las 18:39

Tu error es pensar que la clase es un alcance. No lo es Como explica el documento que citó, los ámbitos son funciones o módulos.

2
Daniel Roseman 5 oct. 2019 a las 15:58
58249924