2011-10-23 14 views
6

Estoy tratando de incrementar todas las marcas de tiempo (de la forma 'HH: MM: SS') en un archivo de texto por el número de segundos especificado por un parámetro de línea de comando para mi programa.python: la función de reemplazo de re.sub no acepta argumentos adicionales: ¿cómo evitar la variable global?

Aquí es una versión simplificada de mi esfuerzo hasta el momento:

import re 
from datetime import datetime, timedelta 

time_diff = timedelta(seconds=10) 

def replace_time(matchobj): 
    if matchobj.group(1) not in [None, '']: 
     return (datetime.strptime(matchobj.group(1), "%H:%M:%S") + time_diff).strftime("%H:%M:%S") 

print re.sub('(\d\d:\d\d:\d\d)', replace_time, "01:27:55") 

Esto funciona bien: el resultado de ejecutar este es 01:28:05 que es justo lo que quiero.

Sin embargo, he escuchado que debería usar variables globales lo menos posible. Así que me preguntaba si hay una manera simple de pasar time_diff como argumento a replace_time en lugar de usar una variable global.

He intentado lo obvio, pero fracasó:

def replace_time(matchobj, time_diff): 
    if matchobj.group(1) not in [None, '']: 
     return (datetime.strptime(matchobj.group(1), "%H:%M:%S") + time_diff).strftime("%H:%M:%S") 

time_diff = timedelta(seconds=10) 
print re.sub('(\d\d:\d\d:\d\d)', replace_time(matchobj, time_diff), "01:27:55") 

con este error: NameError: name 'matchobj' is not defined, por lo que no se puede pasar directamente matchobj.

Miré standard re page y standard re howto, pero no puedo encontrar la información que necesito allí. ¿Cómo puedo evitar el uso de una variable global aquí? ¿Puedo de alguna manera pasar un argumento adicional a la función replace_time? Gracias por adelantado.

Respuesta

11

Usted puede envolver una función en un cierre de la siguiente manera:

def increment_by(time_diff): 
    def replace_time(matchobj): 
     if matchobj.group(1) not in [None, '']: 
      return (datetime.strptime(matchobj.group(1), "%H:%M:%S") + time_diff).strftime("%H:%M:%S") 
    return replace_time 

time_diff = timedelta(seconds=10) 
print re.sub('(\d\d:\d\d:\d\d)', increment_by(time_diff), "01:27:55") 

O puede utilizar una partial de stdlib así:

from functools import partial 

def replace_time(time_diff, matchobj): 
    if matchobj.group(1) not in [None, '']: 
     return (datetime.strptime(matchobj.group(1), "%H:%M:%S") + time_diff).strftime("%H:%M:%S") 

time_diff = timedelta(seconds=10) 
print re.sub('(\d\d:\d\d:\d\d)', partial(replace_time, time_diff), "01:27:55") 
+0

¡Genial! Simplemente probé ambos enfoques y funcionan bien, pero todavía no entiendo cómo hace su magia. Ahora tengo que elegir entre 3 opciones: (a) usar un cierre, (b) usar un parcial, o (c) continuar usando el global, como lo sugirió David Heffernan. ¿Cuáles son las ventajas y desventajas de cada opción y cómo debo decidir? – noumenon

+0

Bueno, en realidad las opciones (a) y (b) son las mismas, ya que 'partial' hace casi lo que mi' increment_by' hace aquí, solo que es más genérico. De hecho, lo escribí para que entiendas mejor la idea. En cuanto a (b) vs. (c) - bueno, preferiría el (b) en la mayoría de los casos. En primer lugar, el estado global siempre es una fuente de errores desagradables. En segundo lugar, en Python las búsquedas globales de vars son más caras. – dmedvinsky

+0

Impresionante. Decidí evitar la opción global basada en tu consejo y el de David Heffernan.He decidido tomar la ruta (b) porque tiene más sentido para mí, es más fácil de entender y de usar, y resuelve mi problema con elegancia. Gracias. – noumenon

1

No hay nada de malo en su enfoque actual. time_diff se escribe solo una vez y luego todos los accesos futuros se leen. Su efecto es una constante de módulo ancho.

Tiene problemas con el estado global compartido cuando tiene varios hilos que acceden a un objeto y al menos uno de los hilos está escribiendo. Eso no está sucediendo aquí y no tienes nada de qué preocuparte.

+0

Gracias. A menudo he visto consejos para evitar variables globales como la peste no solo en Python, sino como una práctica de programación general. Es por eso que quería encontrar una alternativa a mi uso global de var. Entonces, ¿está bien tener variables globales si no estoy haciendo multi-threading? Además, en este caso particular, si más adelante tuviera que sacar un módulo de mi código e importarlo en otros programas, ¿la variable global seguiría siendo correcta o podría causar problemas? Estoy tratando de entender las pautas de cuando está bien tener globales. – noumenon

+0

Si va a exponer esto como un módulo, lo resumiría en una función y probablemente usaría el enfoque de cierre. –

Cuestiones relacionadas