Declaración de variable de Python

Aprendiendo python, y tiene algunas dudas básicas.

1. He visto statement de variable (ruta aquí) como

class writer: path = "" 

a veces, no hay una statement explícita pero se inicializa a través de __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 mantener un tipo personalizado?

 class writer: path = "" # string value customObj = ?? 

Bueno, lo primero es lo primero.

No hay tal cosa como “statement de variable” o “inicialización de variable” en Python.

Simplemente hay lo que llamamos “asignación”, pero probablemente solo deberíamos llamar “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 refirió antes (si es que hay algo)”.

 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 tales, los nombres de Python (un término mejor que “variables”, posiblemente) no tienen tipos asociados; los valores hacen. Puede volver a aplicar el mismo nombre a cualquier cosa, independientemente de su tipo, pero la cosa todavía 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 creas variables para mantener ningún tipo en particular. Usted 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 los lenguajes como Java, C ++ y C #: todo lo declarado dentro del bloque de class es parte de la clase . Por lo tanto, las funciones ( def ) escritas aquí son métodos, es decir, parte del objeto de 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 Example . 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 denomina data y almacena el valor entero 42 . El otro se denomina method y almacena un objeto de función. (Hay varios atributos más que Python agrega automáticamente).

Sin embargo, estos atributos todavía no son realmente parte del objeto. Fundamentalmente, 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 configuras deliberadamente.

Vamos a crear una instancia:

 x = Example() 

Ahora tenemos un objeto separado llamado x , que es una instancia de Example . Los data y el method no son realmente parte del objeto, pero aún podemos buscarlos a través de x debido a la magia que Python hace entre bastidores. Cuando buscamos el method , en particular, obtendremos un “método enlazado” (cuando lo llamamos, x se pasa automáticamente como el parámetro self , lo que no puede suceder si buscamos el Example.method de Example.method directamente).

¿Qué pasa cuando intentamos usar x.data ?

Cuando lo examinamos, primero se mira 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. No reemplazará el atributo de la clase.

Esto nos permite hacer la inicialización de objetos . Python llamará automáticamente al método de la clase ‘ __init__ en nuevas instancias cuando se creen, si están presentes. En este método, podemos simplemente 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 name cuando creamos un Example , y cada instancia tiene su propio name . Python ignorará el atributo de clase Example.name siempre que .name el .name de 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 cuerdas son inmutables. No pueden ser modificados. Cuando tu lo hagas:

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

No cambias la cadena original de “hola”. Eso es imposible en Python. En su lugar, crea una nueva cadena 'hi mom' , y hace que a deje de ser un nombre para 'hi ' , y comience a ser un nombre para 'hi mom' . También hicimos a b un nombre para 'hi ' , y después de volver a aplicar el nombre a, b sigue 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 b es [1, 2, 3, 4] también, porque hicimos b un nombre para la misma cosa que nombramos, y luego cambiamos esa cosa. No creamos una nueva lista para a nombre, porque Python simplemente trata += diferente para las listas.

Esto es importante para los objetos porque si tenía una lista como atributo de clase y usaba una instancia para modificarla, entonces 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 de cualquier objeto de instancia; (b) como estaba modificando la lista y no haciendo una tarea simple, no creó un nuevo atributo de instancia que oculte el atributo de clase.

No hay necesidad de declarar nuevas variables en Python. Si estamos hablando de variables en funciones o módulos, no se necesita statement. Simplemente asigne un valor a un nombre donde lo necesite: mymagic = "Magic" . Las variables en Python pueden contener valores de cualquier tipo, y usted no puede restringir eso.

Sin embargo, su pregunta pregunta específicamente acerca de las clases, objetos y variables de instancia. La forma idiomática de crear variables de instancia es en el método __init__ y en ninguna otra parte, mientras que podría 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 mantener.

Así por ejemplo:

 class Thing(object): def __init__(self, magic): self.magic = magic 

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

 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 diferente por completo. Es funcionalmente diferente, y solo debes hacerlo si tienes 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 él. Te lo dejo a ti que investigues un poco más.

El mensaje para llevar a casa es que Python idiomático es (a) inicializar los atributos del objeto en su método __init__ , y (b) documentar el comportamiento de su clase según sea necesario. No tiene que preocuparse por la documentación completa de nivel Sphinx para todo lo que escriba, pero al menos algunos comentarios sobre los detalles que usted o alguien más necesiten para recogerlos.

Esto podría tardar 6 años en llegar, pero en Python 3.5 y superior, declara un tipo de variable como este:

 variable_name: type_name 

o esto:

 variable_name # type: shinyType 

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

 customObj: CustomObject 

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

Para fines de scope, uso

custom_object = Ninguno

Las variables tienen scope, así que sí, es apropiado tener variables que sean específicas para su función. No siempre tiene que ser explícito acerca de su definición; Por lo general, solo puedes usarlos. Solo si desea hacer algo específico para el tipo de la variable, como adjuntar 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 manera 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í.

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