2012-08-08 8 views
11

¿Existen formas de mantener el estado (para fines de optimización, por ejemplo) sin ir completamente orientadas a objetos?¿Cómo mantener el estado en Python sin clases?

para ilustrar mi mejor pregunta, he aquí un ejemplo de un patrón de uso frecuente en JavaScript:

var someFunc = (function() { 
    var foo = some_expensive_initialization_operation(); 
    return someFunc (bar) { 
     // do something with foo and bar 
    } 
}()); 

Externamente esto es sólo una función como cualquier otro, sin necesidad de inicializar objetos ni nada de eso, pero el cierre permite calcular valores una sola vez que luego esencialmente uso como constantes.

Un ejemplo de esto en Python es la hora de optimizar las expresiones regulares - es útil el uso de re.compile y se almacena la versión compilada para match y search operaciones.

La única manera que conozco para hacer esto en Python están estableciendo una variable en el ámbito de módulo:

compiled_regex = compile_my_regex() 

def try_match(m): # In reality I wouldn't wrap it as pointlessly as this 
    return compiled_regex.match(m) 

O mediante la creación de una clase:

class MatcherContainer(object): 
    def __init__(self): 
     self.compiled_regex = compile_my_regex() 
    def try_match(self, m): 
     self.compiled_regex.match(m) 

my_matcher = MatcherContainer() 

El primer enfoque es el anuncio -hoc y no está muy claro que la función y la variable declarada arriba estén asociadas entre sí. También se sienta contamina el espacio de nombres del módulo un poco, que no estoy muy contento con.

El último enfoque parece prolijo y un poco pesado en la repetición.

La única otra forma en que puedo pensar para manejar esto es factorizar cualquier función como esta en archivos separados (módulos) e importar las funciones, para que todo esté limpio.

¿Algún consejo de Pythoners más experimentados sobre cómo lidiar con esto? ¿O simplemente no te preocupas y continúas resolviendo el problema?

+1

Personalmente no veo ningún problema con la creación de su clase. La verbosidad es buena. "' Explícito es mejor que implícito. " – tMC

+0

Una forma de hacer que al menos el uso de esa clase sea menos detallado sería renombrar' try_match' a '__call__', que le permite usarlo (después de la construcción) exactamente como una función. Pero, como en la respuesta de @ glglgl, su código de JavaScript realmente se traduce directamente en Python. – lvc

+0

es esa llamada a alguna falla correcta en su primer ejemplo? ¿O debería ser la definición de la función? – soulcheck

Respuesta

10

También puede lograr esto con los argumentos por defecto:

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match): 
    return re_match(m) 

ya que los argumentos por defecto sólo se evalúan una vez, en el momento de la importación del módulo.

O aún más simple:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m) 

O más simple aún:

try_match = re.compile(r'sldkjlsdjf').match 

Esto ahorra no sólo la re tiempo de compilación (que en realidad se almacena en caché internamente en el módulo de regreso de todos modos), sino también la búsqueda del método '.match'. En una función ocupada o un ciclo cerrado, esos '.' las resoluciones pueden sumar

+0

Gracias por la respuesta. Sé sobre el almacenamiento en caché de módulos, pero incluso en un script donde utilizo una sola expresión regular en algunos bucles ajustados, obtuve un aumento significativo en el rendimiento al usar 're.compile' en lugar de confiar en el almacenamiento en caché integrado. – Cera

5

¿Qué hay de

def create_matcher(re): 
    compiled_regex = compile_my_regex() 
    def try_match(m): 
     return compiled_regex.match(m) 
    return try_match 

matcher = create_matcher(r'(.*)-(.*)') 
print matcher("1-2") 

?

Pero las clases son mejores y más limpias en la mayoría de los casos.

+1

+1 por "Pero las clases son mejores y más limpias en la mayoría de los casos". – tMC

12

Puede definir el cierre en Python de la misma forma que define un cierre en JavaScript.

def get_matcher(): 
    compiled_regex = compile_my_regex() 

    def try_match(m) 
     return compiled_regex.match(m) 

    return try_match 

Sin embargo, en Python 2.x cierres son de sólo lectura (no se puede volver a asignar a compiled_regex interior llamada de función, para el ejemplo anterior). Si la variable de cierre es una estructura de datos mutable (por ejemplo, list, dict, set), puede modificarla dentro de su llamada de función.

def get_matcher(): 
    compiled_regex = compile_my_regex() 
    match_cache = {} 

    def try_match(m): 
     if m not in match_cache: 
      match_cache[m] = compiled_regex.match(m) 

     return match_cache[m] 

    return try_match 

En Python 3.x, puede utilizar la palabra clave nonlocal para volver a asignar a la variable de cierre en la llamada a la función. (PEP-3104)

consulta las siguientes preguntas sobre el cierre en Python:

+1

Solo para agregar: Python 3 introduce 'nonlocal' que se puede usar para otorgar acceso de escritura explícito a variables cerradas. [(PEP 3104)] (http://www.python.org/dev/peps/pep-3104/) – rwos

+1

actualizó la respuesta – Imran

+0

Estoy un poco confundido por este. ¿Por qué no se debería evaluar todo el cuerpo de 'get_matcher()' cada vez que se ejecuta esto? ¿O el propósito es hacer algo como 'try_match = get_matcher()'? – Cera

1

Una convención utilizada a menudo ha de preceder globales a nivel de módulo privados con un guión para indicar no son parte de la API exportada del módulo:

# mymodule.py 

_MATCHER = compile_my_regex() 

def try_match(m): 
    return _MATCHER.match(m) 

No debe desanimarse de hacer esto; es preferible a una variable oculta en un cierre de función.

2

Puede esconder un atributo en cualquier función. Dado que el nombre de la función es global, puede recuperarlo en otras funciones. Por ejemplo:

def memorize(t): 
    memorize.value = t 

def get(): 
    return memorize.value 

memorize(5) 
print get() 

Salida:

5 

Se puede utilizar para almacenar el estado de una sola función:

def memory(t = None): 
    if t: 
     memory.value = t 
    return memory.value 

print memory(5) 
print memory() 
print memory() 
print memory(7) 
print memory() 
print memory() 

Salida:

5 
5 
5 
7 
7 
7 

Concedido su utilidad está limitado. Solo lo he usado en SO en this question.

Cuestiones relacionadas