¿Cuál es el equivalente idiomático de Python de este código C / C ++?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
específicamente, ¿cómo se implementa el miembro estático en el nivel de función, en oposición al nivel de clase? ¿Y colocar la función en una clase cambia algo?
_
prefijo convencional .Respuestas:
Un poco invertido, pero esto debería funcionar:
Si desea el código de inicialización del contador en la parte superior en lugar de en la parte inferior, puede crear un decorador:
Luego usa el código como este:
Todavía requerirá que use el
foo.
prefijo, desafortunadamente.(Crédito: @ony )
fuente
if "counter" not in foo.__dict__: foo.counter = 0
como primeras líneas defoo()
. Esto ayudaría a evitar el código fuera de la función. Sin embargo, no estoy seguro de si esto fue posible en 2008. PD: Encontré esta respuesta mientras buscaba la posibilidad de crear variables de función estáticas, por lo que este hilo aún está "vivo" :)foo
yfoo.counter =
están íntimamente relacionados. sin embargo, en última instancia, prefiero el enfoque del decorador, ya que no hay forma de que no se llame al decorador y es semánticamente más obvio lo que hace (@static_var("counter", 0)
es más fácil y tiene más sentido para mis ojos queif "counter" not in foo.__dict__: foo.counter = 0
, especialmente porque en este último tiene que usar el nombre de la función (dos veces) que puede cambiar).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Puede agregar atributos a una función y usarla como una variable estática.
Alternativamente, si no desea configurar la variable fuera de la función, puede usarla
hasattr()
para evitar unaAttributeError
excepción:De todos modos, las variables estáticas son bastante raras, y debería encontrar un lugar mejor para esta variable, muy probablemente dentro de una clase.
fuente
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
debería hacer lo mismo, usando excepciones en su lugar.try
agrega algún costo? Sólo curioso.También se podría considerar:
Razonamiento:
if
rama (piense en la excepción StopIteration )fuente
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
para esto no es más simple y también menos eficiente.Otras respuestas han demostrado la forma en que debe hacer esto. Aquí hay una forma en que no deberías:
Los valores predeterminados se inicializan solo cuando la función se evalúa por primera vez, no cada vez que se ejecuta, por lo que puede usar una lista o cualquier otro objeto mutable para almacenar valores estáticos.
fuente
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
me parece bien legible. Otro buen punto es el cambio de nombre de la función fácil.types.SimpleNamespace
, haciéndolodef foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
sin necesidad de definir una clase especial.Muchas personas ya han sugerido probar 'hasattr', pero hay una respuesta más simple:
Sin prueba / excepto, sin prueba hasattr, solo getattr con un valor predeterminado.
fuente
try
/except
basado de ravwojdyla no tiene sentido. Unipython
%%timeit
microbenchmark simple dio el costo detry
/except
a 255 ns por llamada, frente a 263 ns para lagetattr
solución basada. Sí, eltry
/except
es más rápido, pero no es exactamente "ganar sin dudas"; Es una pequeña micro-optimización. Escriba cualquier código que parezca más claro, no se preocupe por diferencias de rendimiento triviales como esta.Aquí hay una versión totalmente encapsulada que no requiere una llamada de inicialización externa:
En Python, las funciones son objetos y simplemente podemos agregarles, o un parche de mono, variables miembro a través del atributo especial
__dict__
. El incorporadovars()
devuelve el atributo especial__dict__
.EDITAR: tenga en cuenta que, a diferencia de la
try:except AttributeError
respuesta alternativa , con este enfoque la variable siempre estará lista para la lógica del código después de la inicialización. Creo que latry:except AttributeError
alternativa a lo siguiente será menos SECO y / o tendrá un flujo incómodo:EDIT2: solo recomiendo el enfoque anterior cuando se llamará a la función desde múltiples ubicaciones. Si, en cambio, la función solo se llama en un lugar, es mejor usar
nonlocal
:fuente
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
lugar denot X in Y
(o aconseje usarlo si solo lo estaba usando en aras de una comparación de aspecto más similar entre eso yhasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python no tiene variables estáticas, pero puede simularlo definiendo un objeto de clase invocable y luego usándolo como una función. También vea esta respuesta .
Tenga en cuenta que
__call__
una instancia de una clase (objeto) se puede llamar por su propio nombre. Es por eso que llamarfoo()
arriba llama al__call__
método de la clase . De la documentación :fuente
foo
que se proporciona como un singleton.Use una función generadora para generar un iterador.
Entonces úsalo como
Si quieres un límite superior:
Si el iterador termina (como en el ejemplo anterior), también puede recorrerlo directamente, como
Por supuesto, en estos casos simples es mejor usar xrange :)
Aquí está la documentación en la declaración de rendimiento .
fuente
Otras soluciones adjuntan un atributo de contador a la función, generalmente con lógica enrevesada para manejar la inicialización. Esto es inapropiado para el nuevo código.
En Python 3, la forma correcta es usar una
nonlocal
declaración:Ver PEP 3104 para la especificación de la
nonlocal
declaración.Si el contador está destinado a ser privado para el módulo, se debe nombrar
_counter
en su lugar.fuente
global counter
declaración en lugar denonlocal counter
(nonlocal
solo le permite escribir en estado de cierre en una función anidada). La razón por la que las personas están adjuntando un atributo a la función es para evitar contaminar el espacio de nombres global para el estado específico de la función, por lo que no tiene que hacer cosas aún más difíciles cuando dos funciones necesitancounter
s independientes . Esta solución no escala; atributos en la función do. La respuesta de kdb es cómononlocal
puede ayudar, pero agrega complejidad.nonlocal
sobreglobal
es exactamente como usted señala - que funciona en más estrictamente las circunstancias.Usar un atributo de una función como variable estática tiene algunos inconvenientes potenciales:
Python idiomático para el segundo problema probablemente nombraría la variable con un guión bajo para indicar que no se debe acceder a ella, mientras se mantiene accesible después del hecho.
Una alternativa sería un patrón utilizando cierres léxicos, que son compatibles con la
nonlocal
palabra clave en python 3.Lamentablemente, no sé cómo encapsular esta solución en un decorador.
fuente
Al igual que el código anterior de vincent, esto se usaría como decorador de funciones y se debe acceder a las variables estáticas con el nombre de la función como prefijo. La ventaja de este código (aunque es cierto que cualquiera podría ser lo suficientemente inteligente como para descubrirlo) es que puede tener múltiples variables estáticas e inicializarlas de una manera más convencional.
fuente
Un poco más legible, pero más detallado (Zen de Python: explícito es mejor que implícito):
Vea aquí para una explicación de cómo funciona esto.
fuente
foo()
debe reinicializar el diccionario al valor especificado en la definición de la función (por lo que la clave del contador tiene un valor de 0). ¿Por qué no?Python usa habitualmente guiones bajos para indicar variables privadas. La única razón en C para declarar la variable estática dentro de la función es ocultarla fuera de la función, que no es Python realmente idiomática.
fuente
Después de probar varios enfoques, termino usando una versión mejorada de la respuesta de @ warvariuc:
fuente
La forma idiomática es usar una clase , que puede tener atributos. Si necesita que las instancias no estén separadas, use un singleton.
Hay varias formas de falsificar o combinar variables "estáticas" en Python (una no mencionada hasta ahora es tener un argumento predeterminado mutable), pero esta no es la forma idiomática e idiomática de hacerlo. Solo usa una clase.
O posiblemente un generador, si su patrón de uso se ajusta.
fuente
default
argumento es el más elegante.Impulsado por esta pregunta , ¿puedo presentar otra alternativa que podría ser un poco más agradable de usar y se verá igual para ambos métodos y funciones:
Si te gusta el uso, aquí está la implementación:
fuente
Otro giro (¡no recomendado!) En el objeto invocable como https://stackoverflow.com/a/279598/916373 , si no le importa usar una firma de llamada funky, sería hacer
fuente
En lugar de crear una función que tenga una variable local estática, siempre puede crear lo que se llama un "objeto de función" y asignarle una variable miembro estándar (no estática).
Dado que dio un ejemplo escrito en C ++, primero explicaré qué es un "objeto de función" en C ++. Un "objeto de función" es simplemente cualquier clase con una sobrecarga
operator()
. Las instancias de la clase se comportarán como funciones. Por ejemplo, puede escribirint x = square(5);
incluso sisquare
es un objeto (con sobrecargaoperator()
) y técnicamente no es una "función". Puede dar a un objeto de función cualquiera de las características que podría darle a un objeto de clase.En Python, también podemos sobrecargar,
operator()
excepto que el método se llama en su lugar__call__
:Aquí hay una definición de clase:
Aquí hay un ejemplo de la clase utilizada:
El resultado impreso en la consola es:
Si desea que su función tome argumentos de entrada, también puede agregarlos a
__call__
:fuente
Soulution n + = 1
fuente
Una declaración global proporciona esta funcionalidad. En el siguiente ejemplo (python 3.5 o superior para usar la "f"), la variable de contador se define fuera de la función. Definirlo como global en la función significa que la versión "global" fuera de la función debe estar disponible para la función. Por lo tanto, cada vez que se ejecuta la función, modifica el valor fuera de la función, manteniéndolo más allá de la función.
fuente
Una variable estática dentro de un método Python
fuente
Personalmente prefiero lo siguiente a los decoradores. A cada cual lo suyo.
fuente
Esta respuesta se basa en la respuesta de @claudiu.
Descubrí que mi código se volvía menos claro cuando siempre tenía que anteponer el nombre de la función, cada vez que tenía la intención de acceder a una variable estática.
A saber, en mi código de función preferiría escribir:
en vez de
Entonces, mi solución es:
statics
atributo a la funciónstatics
como un alias paramy_function.statics
Observación
Mi método utiliza una clase llamada
Bunch
, que es un diccionario que admite el acceso de estilo de atributo, a la JavaScript (vea el artículo original al respecto, alrededor de 2000)Se puede instalar a través de
pip install bunch
También se puede escribir a mano así:
fuente
types.SimpleNamespace
(disponible desde 3.3) admite este comportamiento fuera de la caja (y se implementa en C en CPython, por lo que es lo más rápido posible).Sobre la base de la respuesta de Daniel (adiciones):
La razón por la que quería agregar esta parte es que las variables estáticas se usan no solo para aumentar en algún valor, sino también para verificar si la var estática es igual a algún valor, como un ejemplo de la vida real.
La variable estática todavía está protegida y se usa solo dentro del alcance de la función use_foo ()
En este ejemplo, call to foo () funciona exactamente como (con respecto al equivalente de c ++ correspondiente):
si la clase Foo se define restrictivamente como una clase singleton, eso sería ideal. Esto lo haría más pitónico.
fuente
Claro que esta es una vieja pregunta, pero creo que podría proporcionar alguna actualización.
Parece que el argumento del rendimiento es obsoleto. El mismo conjunto de pruebas parece dar resultados similares para siInt_try y isInt_re2. Por supuesto, los resultados varían, pero esta es una sesión en mi computadora con Python 3.4.4 en el kernel 4.3.01 con Xeon W3550. Lo he ejecutado varias veces y los resultados parecen ser similares. Moví la expresión regular global a la función estática, pero la diferencia de rendimiento es insignificante.
Con el problema de rendimiento fuera del camino, parece que try / catch produciría el código más a prueba de futuro y de esquina, por lo que tal vez solo lo envuelva en función
fuente