2012-09-28 10 views
9

Stack Overflow tiene muchas preguntas sobre las variables globales en python, y parece generar cierta confusión para las personas que vienen de otros idiomas. Las reglas de alcance no funcionan exactamente como lo esperan muchas personas de otros orígenes.Frecuencia de variables globales en python?

Al mismo tiempo, el código debe organizarse no tanto a nivel de clase, sino a nivel de módulo. Entonces, cuando todo no está necesariamente contenido en clases, el estado que de otro modo se encontraría en las variables miembro puede ir en variables de nivel de módulo.

Así que mi pregunta es de 2 partes:

1) ¿Debo estar evitando el uso de variables globales (ajuste específicamente a ellos desde dentro de las funciones y el uso de la palabra clave global)?

2) Si el # 1 es sí, ¿hay patrones comunes donde se espera que se usen?

Trabajo en un lugar donde abundan muchos idiomas diferentes y quiero mitigar la confusión y asegurarme de que los pitonistas no me odien más tarde.

Gracias por cualquier aporte constructivo.

+0

Y hay 'nonlocal' en Python 3.x :) –

+0

@ Jon Clements: Entonces, ¿qué? – martineau

Respuesta

7

Le recomiendo que lea esta publicación de blog titulada Singletons and their Problems in Python. Me ha llevado a replantear mi uso de variables globales. Algunas cotizaciones de opciones:

Pero ten cuidado. El hecho de que no implemente el patrón de diseño singleton no significa que evite el problema central de un singleton. El problema central de un singleton es el estado global compartido. Un singleton no es más que una variable global glorificada y en idiomas como Java existen muchas razones por las que desearía usar algo así como un singleton. En Python tenemos algo diferente para singletons, y tiene un nombre muy inocente que oculta los detalles sangrientos: módulo.

Así es: un módulo de Python es un singleton. Y comparte los mismos problemas del patrón singleton simplemente que es un poco peor.

Y aquí es un ejemplo de los problemas que tienen tal estado compartido podría causar:

Con el fin de no hablar de cosas irrelevantes, vamos a echar un vistazo a uno de los módulos de la librería estándar, el módulo mimetypes

echar un vistazo:

inited = False 

def init(files=None): 
    global inited 
    db = MimeTypes() 
    ... 

Este es el código real del módulo de tipos MIME que se envía con Python, sólo con los detalles más morbosos removidos. El punto es que hay un estado compartido. Y el estado compartido es un indicador booleano que es True si el módulo se inicializó o False si no lo era. Ahora bien, ese caso particular probablemente no sea tan problemático (confíen en mí, lo es) porque mimetypes se inicializa a sí mismo, pero puede ver que hay un parámetro de archivos para la función init. Si pasa una lista de archivos a esa función, se reinicializará la base de datos mime en la memoria con la información mime de esos archivos. Ahora imagine lo que pasaría si usted tiene dos bibliotecas de tipos MIME de inicialización con dos fuentes diferentes ...

Eso es un patrón bastante común, y yo mismo lo he hecho ... pero por ejemplo una mejor manera de hacerlo sería : init devuelve una instancia de una clase que implementa todos los métodos, y otras partes del código pueden init para obtener una instancia diferente con diferentes parámetros que no interfieran con la primera. El "inconveniente" es que debe pasar esta instancia a cualquier código que no quiera inicializar uno nuevo, pero el lado positivo de ese "inconveniente" es que hace obvio cuáles son sus dependencias.

De todos modos, en pocas palabras, trataría de evitarlo tanto como sea posible, pero si estás de acuerdo con que el código tenga un singleton implícito, entonces hazlo.

+1

Otra limitación importante de usar módulos como instancias de un singleton es que, a diferencia de una clase con un método '__init __()' que se llamará y posiblemente se pasen los argumentos, es difícil afectar/controlar la construcción de la instancia (única) creada en su 'importación' inicial. – martineau

+0

@Claudiu gracias, esta respuesta me dio más de lo que esperaba :). – MrFox

2

Los globals no son realmente tabú, es solo que debes recordar declararlos como globales en tu función antes de usarlos. En mi opinión, esto los hace más claros porque el usuario ve que está usando explícitamente un global.

Tendría más miedo de que los no pythonistas no entiendan Python Globals, modifiquen su código y no agreguen las declaraciones globales correctas.

3

En resumen, sí, debe evitar usar la palabra clave global. Puede que esté en el idioma por una razón, pero generalmente considero que es un olor a código: si tiene algún estado que desea mantener, encapsúlelo en una clase. Eso es mucho menos frágil que usar global.

+1

Considero una regla difícil y rápida nunca usar 'global'. Mantener todo tu estado en una instancia de clase es bastante fácil, y si alguna vez necesitas crear más de una instancia de una clase, deberías agradecer a tus estrellas de la suerte que no las pones juntas. Si el estado realmente necesita ser común, muévalo a un nivel superior en la jerarquía de clases. – Dave

+0

A veces tiendo a hacer 'foo = [globalvar]' y luego obtener y establecer 'foo [0]' dentro de las funciones para que no tenga que usar esa palabra clave 'global'. de alguna manera hace más obvio que estoy usando un varibale global porque tengo que tratarlo "especialmente", pero sigue siendo bastante feo, al igual que el uso de variables globales es – Claudiu

3

me hizo esta observación en su otra pregunta, así que aquí están mis 2 centavos:

Python es orientada a objetos, pero usted no tiene que usarlo. Pero si lo desea, usted podría explotar este mecanismo de la clase para mantener algunas propiedades:

class Config(): 
""" 
hold some vars for example. 
""" 
def __init__(self): 
    self.CONFIG_LOG_FILE_DIR='/tmp2/ozn/venus_mon_log/' 
    self.DATE_FORMAT="%Y%m%d" 
    #self.FILE_NAME_BASE_TEMPLATE=eval('datetime.datetime.now().strftime(self.DATE_FORMAT)')+'-venus_utilization.log' 
    self.FILE_NAME_BASE_TEMPLATE='venus_utilization.log' 
    self.FILE_NAME=self.CONFIG_LOG_FILE_DIR+self.FILE_NAME_BASE_TEMPLATE 
    self.MAX_FILE_SIZE=1024*1024*50 # in Byte, 50 in MB. 

Se puede crear una instancia que le da acceso a las propiedades:

cfg = Config() 

Y luego acceder a ellos utilizando :

cfg.MAX_FILE_SIZE 

o incluso cambiarlos:

cfg.MAX_FILE_SIZE=50000 
    cfg.MAX_FILE_SIZE=calculateNewSize() 

y así sucesivamente ... También puede hacer cosas como:

# this will print all items that an instance has  
if options.debug: 
    print "DEBUG INFO:" 
    for k,v in vars(cfg).iteritems(): 
     print k,v