Declaración de variable de Python

86

Aprendiendo Python , y tiene algunas dudas básicas.

1.He visto la declaración de variable (ruta aquí) como

class writer:
    path = ""

a veces, no hay declaración explícita, pero se inicializa __init__.

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

Entiendo el propósito de __init__, pero es recomendable declarar variable en cualquier otra función.

2. ¿Cómo puedo crear una variable para contener un tipo personalizado?

class writer:
    path = "" # string value
    customObj = ??
bsr
fuente
12
Y, con respecto a la segunda pregunta: en Python, las variables no tienen tipo: los valores sí. Entonces, cualquier variable puede contener sus objetos personalizados.
jpaugh
4
Ayudará si deja de pensar en nombres / identificadores como variables . Son referencias a objetos
John La Rooy
2
@gnibbler O nombres para ellos ...
detly
1
@detly, creo que los nombres son una mala elección. Las cosas generalmente tienen un solo nombre, mientras que un objeto puede tener múltiples referencias
John La Rooy
2
@JohnClements, pero Python no funciona como las matemáticas. Si desea comprender Python, debe saber que los objetos con un __name__atributo tienen un "nombre". No todos los objetos tienen un __name__atributo, pero aún puede tener referencias a ellos. Si comenzamos a llamar a una "referencia" un "nombre", estamos haciendo un flaco favor a los principiantes en el futuro.
John La Rooy

Respuestas:

202

De acuerdo, lo primero es lo primero.

No existe tal cosa como "declaración de variable" o "inicialización de variable" en Python.

Hay simplemente lo que llamamos "asignación", pero probablemente deberíamos llamar simplemente "nombrar".

Asignación significa "este nombre en el lado izquierdo ahora se refiere al resultado de evaluar el lado derecho, sin importar a qué se refería antes (si acaso)".

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

Como tal, los nombres de Python (un término mejor que "variables", posiblemente) no tienen tipos asociados; los valores lo hacen. Puede volver a aplicar el mismo nombre a cualquier cosa independientemente de su tipo, pero la cosa aún tiene un comportamiento que depende de su tipo. El nombre es simplemente una forma de referirse al valor (objeto). Esto responde a su segunda pregunta: no crea variables para mantener un tipo personalizado. No crea variables para contener ningún tipo en particular. No "crea" variables en absoluto. Le das nombres a los objetos.

Segundo punto: Python sigue una regla muy simple cuando se trata de clases, que en realidad es mucho más consistente que lo que hacen lenguajes como Java, C ++ y C #: todo lo declarado dentro del classbloque es parte de la clase . Entonces, las funciones ( def) escritas aquí son métodos, es decir, parte del objeto de la clase (no almacenado por instancia), al igual que en Java, C ++ y C #; pero otros nombres aquí también son parte de la clase. Nuevamente, los nombres son solo nombres y no tienen tipos asociados, y las funciones también son objetos en Python. Así:

class Example:
    data = 42
    def method(self): pass

Las clases también son objetos en Python.

Así que ahora hemos creado un objeto llamado Example, que representa la clase de todas las cosas que son Examples. Este objeto tiene dos atributos proporcionados por el usuario (en C ++, "miembros"; en C #, "campos o propiedades o métodos"; en Java, "campos o métodos"). Uno de ellos se nombra datay almacena el valor entero 42. El otro se nombra methody almacena un objeto de función. (Hay varios atributos más que Python agrega automáticamente).

Sin embargo, estos atributos todavía no forman parte del objeto. Básicamente, un objeto es solo un conjunto de más nombres (los nombres de los atributos), hasta que se llega a cosas que ya no se pueden dividir. Por lo tanto, los valores se pueden compartir entre diferentes instancias de una clase, o incluso entre objetos de diferentes clases, si lo configura deliberadamente.

Creemos una instancia:

x = Example()

Ahora tenemos un objeto separado llamado x, que es una instancia de Example. Los datay methodno son en realidad parte del objeto, pero aún podemos buscarlos a través xde algo de magia que Python hace detrás de escena. Cuando miramos hacia arriba method, en particular, obtendremos un "método vinculado" (cuando lo llamamos, xse pasa automáticamente como selfparámetro, lo que no puede suceder si miramos hacia arriba Example.methoddirectamente).

¿Qué pasa cuando intentamos usar x.data?

Cuando lo examinamos, primero se busca en el objeto. Si no se encuentra en el objeto, Python busca en la clase.

Sin embargo, cuando asignamos a x.data , Python creará un atributo en el objeto. Será no reemplazar el atributo de clase.

Esto nos permite realizar la inicialización de objetos . Python llamará automáticamente al __init__método de la clase en las nuevas instancias cuando se creen, si están presentes. En este método, simplemente podemos asignar atributos para establecer valores iniciales para ese atributo en cada objeto:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Ahora debemos especificar un namecuando creamos un Example, y cada instancia tiene el suyo name. Python ignorará el atributo de clase Example.namecada vez que busquemos el .namede una instancia, porque el atributo de la instancia se encontrará primero.

Una última advertencia: ¡la modificación (mutación) y la asignación son cosas diferentes!

En Python, las cadenas son inmutables. No se pueden modificar. Cuando tu lo hagas:

a = 'hi '
b = a
a += 'mom'

No cambia la cadena 'hola' original. Eso es imposible en Python. En su lugar, crea una nueva cadena 'hi mom'y hace aque deje de ser un nombre para 'hi 'y comience a ser un nombre para en su 'hi mom'lugar. También hicimos bun nombre para 'hi ', y después de volver a aplicar el anombre, bsigue siendo un nombre para 'hi ', porque 'hi 'todavía existe y no se ha cambiado.

Pero las listas se pueden cambiar:

a = [1, 2, 3]
b = a
a += [4]

Ahora bes [1, 2, 3, 4] también, porque hicimos bun nombre para la misma cosa que anombró, y luego cambiamos esa cosa. No creamos una nueva lista para anombrar, porque Python simplemente trata de manera +=diferente a las listas.

Esto es importante para los objetos porque si tuviera una lista como atributo de clase y usara una instancia para modificar la lista, el cambio se "vería" en todas las demás instancias. Esto se debe a que (a) los datos son en realidad parte del objeto de clase y no un objeto de instancia; (b) debido a que estaba modificando la lista y no haciendo una asignación simple, no creó un nuevo atributo de instancia que ocultaba el atributo de clase.

Karl Knechtel
fuente
12
Por eso los llamamos "identificadores" . :-)
Martijn Pieters
@Karl_Knechtel en su ejemplo de cadena al final, ¿qué pasa con 'hola' si nunca lo nombramos como 'b'? ¿Es esto una pérdida de memoria?
infinito
2
@heretoinfinity: no; Python usa la recolección de basura para liberar objetos inalcanzables
John Clements
La declaración de variable está bien explicada en este video: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan
Mira este video para la respuesta youtube.com/…
Thao N
23

Esto podría tener un retraso de 6 años, pero en Python 3.5 y superior, declaras un tipo de variable como este:

variable_name: type_name

o esto:

variable_name # type: shinyType

Entonces, en su caso (si tiene una CustomObjectclase definida), puede hacer:

customObj: CustomObject

Vea esto o aquello para más información.

O. Aroesti
fuente
22

No es necesario declarar nuevas variables en Python. Si hablamos de variables en funciones o módulos, no se necesita declaración. Basta con asignar un valor a un nombre donde lo necesita: mymagic = "Magic". Las variables en Python pueden contener valores de cualquier tipo, y no puede restringir eso.

Sin embargo, su pregunta se refiere específicamente a clases, objetos y variables de instancia. La forma idiomática de crear variables de instancia es en el __init__método y en ningún otro lugar; aunque puede crear nuevas variables de instancia en otros métodos, o incluso en código no relacionado, es simplemente una mala idea. Hará que su código sea difícil de razonar o de mantener.

Así por ejemplo:

class Thing(object):

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

Fácil. Ahora las instancias de esta clase tienen un magicatributo:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

La creación de variables en el espacio de nombres de la clase en sí conduce a un comportamiento completamente diferente. Es funcionalmente diferente y solo debe hacerlo si tiene una razón específica para hacerlo. Por ejemplo:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

Ahora intenta:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

O:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

Esto se debe a que el espacio de nombres de la clase en sí es diferente al espacio de nombres de los objetos creados a partir de ella. Te dejo a ti investigar un poco más.

El mensaje para llevar a casa es que Python idiomático es (a) inicializar atributos de objeto en su __init__método y (b) documentar el comportamiento de su clase según sea necesario. No es necesario que se tome la molestia de obtener documentación completa a nivel de Sphinx para todo lo que escriba, pero al menos algunos comentarios sobre los detalles que usted u otra persona puedan necesitar para recogerlo.

detenidamente
fuente
12

Para fines de alcance, utilizo:

custom_object = None
al rojo vivo
fuente
1

Las variables tienen alcance, por lo que sí, es apropiado tener variables que sean específicas de su función. No siempre es necesario ser explícito sobre su definición; por lo general, puede usarlos. Solo si desea hacer algo específico para el tipo de variable, como agregar para una lista, debe definirlos antes de comenzar a usarlos. Ejemplo típico de esto.

list = []
for i in stuff:
  list.append(i)

Por cierto, esta no es realmente una buena forma de configurar la lista. Sería mejor decir:

list = [i for i in stuff] # list comprehension

...pero yo divago.

Tu otra pregunta. El objeto personalizado debe ser una clase en sí misma.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
ChipJust
fuente