Quel est l'équivalent Python idiomatique de ce code C / C ++?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
en particulier, comment implémenter le membre statique au niveau de la fonction, par opposition au niveau de la classe? Et le fait de placer la fonction dans une classe change-t-il quelque chose?
_
préfixe conventionnel .Réponses:
Un peu inversé, mais cela devrait fonctionner:
Si vous voulez que le code d'initialisation du compteur soit en haut plutôt qu'en bas, vous pouvez créer un décorateur:
Utilisez ensuite le code comme ceci:
foo.
Malheureusement, il vous faudra encore utiliser le préfixe.(Crédit: @ony )
la source
if "counter" not in foo.__dict__: foo.counter = 0
les premières lignes defoo()
. Cela aiderait à éviter le code en dehors de la fonction. Je ne sais pas si cela était possible en 2008. PS J'ai trouvé cette réponse en cherchant la possibilité de créer des variables de fonction statiques, donc ce fil est toujours "vivant" :)foo
et qu'ellefoo.counter =
est intimement liée. cependant, je préfère finalement l'approche du décorateur, car il n'y a aucun moyen de ne pas appeler le décorateur et il est sémantiquement plus évident de ce qu'il fait (@static_var("counter", 0)
est plus facile et plus logique à mes yeux queif "counter" not in foo.__dict__: foo.counter = 0
, surtout que dans ce dernier que vous devez utiliser le nom de la fonction (deux fois) qui pourrait changer).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Vous pouvez ajouter des attributs à une fonction et l'utiliser comme variable statique.
Alternativement, si vous ne souhaitez pas configurer la variable en dehors de la fonction, vous pouvez utiliser
hasattr()
pour éviter uneAttributeError
exception:Quoi qu'il en soit, les variables statiques sont plutôt rares, et vous devriez trouver un meilleur endroit pour cette variable, très probablement à l'intérieur d'une classe.
la source
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
devrait faire de même, en utilisant des exceptions à la place.try
entraîne des coûts? Juste curieux.On pourrait également envisager:
Raisonnement:
if
branche (pensez à l' exception StopIteration )la source
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
pour cela n'est pas plus simple et aussi moins efficace.D'autres réponses ont démontré la façon dont vous devez procéder. Voici un moyen que vous ne devriez pas:
Les valeurs par défaut ne sont initialisées que lors de la première évaluation de la fonction, et non à chaque exécution, vous pouvez donc utiliser une liste ou tout autre objet modifiable pour stocker des valeurs statiques.
la source
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
je le trouve bien lisible. Un autre bon point est le changement de nom facile des fonctions.types.SimpleNamespace
, ce qui en faitdef foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
sans avoir besoin de définir une classe spéciale.Beaucoup de gens ont déjà suggéré de tester 'hasattr', mais il existe une réponse plus simple:
Aucun essai / sauf, aucun test hasattr, juste getattr avec un défaut.
la source
try
/except
basée de ravwojdyla n'a pas de sens. Une simpleipython
%%timeit
micro-référence a donné le coût dutry
/except
à 255 ns par appel, contre 263 ns pour lagetattr
solution basée. Oui, letry
/except
est plus rapide, mais ce n'est pas exactement "gagner haut la main"; c'est une minuscule micro-optimisation. Écrivez le code qui semble plus clair, ne vous inquiétez pas des différences de performances triviales comme celle-ci.Voici une version entièrement encapsulée qui ne nécessite pas d'appel d'initialisation externe:
En Python, les fonctions sont des objets et nous pouvons simplement leur ajouter, ou patcher des singes, des variables membres via l'attribut spécial
__dict__
. La fonction intégréevars()
renvoie l'attribut spécial__dict__
.EDIT: Notez, contrairement à la
try:except AttributeError
réponse alternative , avec cette approche, la variable sera toujours prête pour la logique du code après l'initialisation. Je pense que l'try:except AttributeError
alternative aux suivantes sera moins sèche et / ou aura un flux maladroit:EDIT2: Je ne recommande l'approche ci-dessus que lorsque la fonction sera appelée à partir de plusieurs emplacements. Si à la place la fonction n'est appelée qu'à un seul endroit, il vaut mieux utiliser
nonlocal
:la source
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
plutôt quenot X in Y
(ou conseiller d'utiliser si vous l'utilisez juste pour une comparaison plus similaire entre cela ethasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python n'a pas de variables statiques mais vous pouvez le simuler en définissant un objet de classe appelable puis en l'utilisant comme fonction. Voir également cette réponse .
Notez que
__call__
rend une instance d'une classe (objet) appelable par son propre nom. C'est pourquoi appelerfoo()
ci-dessus appelle la__call__
méthode de la classe . De la documentation :la source
foo
fournie en tant que singleton.Utilisez une fonction de générateur pour générer un itérateur.
Ensuite, utilisez-le comme
Si vous souhaitez une limite supérieure:
Si l'itérateur se termine (comme dans l'exemple ci-dessus), vous pouvez également le boucler directement, comme
Bien sûr, dans ces cas simples, il vaut mieux utiliser xrange :)
Voici la documentation sur la déclaration de rendement .
la source
D'autres solutions attachent un attribut de compteur à la fonction, généralement avec une logique alambiquée pour gérer l'initialisation. Ceci est inapproprié pour le nouveau code.
En Python 3, la bonne façon est d'utiliser une
nonlocal
instruction:Voir PEP 3104 pour la spécification de l'
nonlocal
instruction.Si le compteur est destiné à être privé du module, il doit être nommé à la
_counter
place.la source
global counter
instruction au lieu denonlocal counter
(nonlocal
vous permet simplement d'écrire dans un état de fermeture dans une fonction imbriquée). La raison pour laquelle les gens attachent un attribut à la fonction est d'éviter de polluer l'espace de noms global pour un état spécifique à la fonction, de sorte que vous n'avez pas à faire des choses encore plus piratées lorsque deux fonctions ont besoin decounter
s indépendants . Cette solution n'est pas évolutive; les attributs de la fonction font. La réponse de kdb est de savoir commentnonlocal
peut aider, mais cela ajoute de la complexité.nonlocal
plusglobal
est exactement comme vous le dites - cela fonctionne dans des circonstances strictement plus.L'utilisation d'un attribut d'une fonction en tant que variable statique présente certains inconvénients potentiels:
Un python idiomatique pour le deuxième problème serait probablement de nommer la variable avec un trait de soulignement de tête pour signaler qu'elle n'est pas destinée à être accédée, tout en la gardant accessible après coup.
Une alternative serait un modèle utilisant des fermetures lexicales, qui sont prises en charge avec le
nonlocal
mot - clé en python 3.Malheureusement, je ne connais aucun moyen d'encapsuler cette solution dans un décorateur.
la source
Tout comme le code de vincent ci-dessus, il serait utilisé comme décorateur de fonction et les variables statiques doivent être accédées avec le nom de la fonction comme préfixe. L'avantage de ce code (bien que tout le monde puisse être assez intelligent pour le comprendre) est que vous pouvez avoir plusieurs variables statiques et les initialiser de manière plus conventionnelle.
la source
Un peu plus lisible, mais plus verbeux (Zen of Python: explicite vaut mieux qu'implicite):
Voir ici pour une explication de comment cela fonctionne.
la source
foo()
doit réinitialiser le dictionnaire à la valeur spécifiée dans la définition de la fonction (donc avec la clé de compteur ayant une valeur de 0). Pourquoi pas?Python utilise habituellement des traits de soulignement pour indiquer des variables privées. La seule raison en C pour déclarer la variable statique à l'intérieur de la fonction est de la cacher en dehors de la fonction, ce qui n'est pas vraiment du Python idiomatique.
la source
Après avoir essayé plusieurs approches, je finis par utiliser une version améliorée de la réponse de @ warvariuc:
la source
La manière idiomatique est d'utiliser une classe , qui peut avoir des attributs. Si vous avez besoin que les instances ne soient pas séparées, utilisez un singleton.
Il existe un certain nombre de façons de simuler ou de fusionner des variables "statiques" dans Python (une qui n'est pas mentionnée jusqu'à présent est d'avoir un argument par défaut modifiable), mais ce n'est pas la manière idiomatique Pythonique de le faire. Utilisez simplement une classe.
Ou éventuellement un générateur, si votre modèle d'utilisation convient.
la source
default
argument est le plus élégant.Invité par cette question , puis-je présenter une autre alternative qui pourrait être un peu plus agréable à utiliser et qui se ressemblera pour les méthodes et les fonctions:
Si vous aimez l'utilisation, voici l'implémentation:
la source
Une autre torsion (non recommandée!) Sur l'objet appelable comme https://stackoverflow.com/a/279598/916373 , si cela ne vous dérange pas d'utiliser une signature d'appel géniale, serait de faire
la source
Au lieu de créer une fonction ayant une variable locale statique, vous pouvez toujours créer ce qu'on appelle un "objet fonction" et lui donner une variable membre standard (non statique).
Puisque vous avez donné un exemple écrit en C ++, je vais d'abord expliquer ce qu'est un "objet fonction" en C ++. Un "objet fonction" est simplement n'importe quelle classe avec une surcharge
operator()
. Les instances de la classe se comporteront comme des fonctions. Par exemple, vous pouvez écrireint x = square(5);
même s'ilsquare
s'agit d'un objet (surchargéoperator()
) et non techniquement pas une "fonction". Vous pouvez donner à un objet-fonction n'importe laquelle des fonctionnalités que vous pourriez donner à un objet de classe.En Python, nous pouvons également surcharger
operator()
sauf que la méthode est nommée à la place__call__
:Voici une définition de classe:
Voici un exemple de la classe utilisée:
La sortie imprimée sur la console est:
Si vous souhaitez que votre fonction accepte des arguments d'entrée, vous pouvez également les ajouter à
__call__
:la source
Soulution n + = 1
la source
Une déclaration globale fournit cette fonctionnalité. Dans l'exemple ci-dessous (python 3.5 ou supérieur pour utiliser le "f"), la variable compteur est définie en dehors de la fonction. Le définir comme global dans la fonction signifie que la version "globale" en dehors de la fonction doit être mise à la disposition de la fonction. Ainsi, chaque fois que la fonction s'exécute, elle modifie la valeur en dehors de la fonction, la préservant au-delà de la fonction.
la source
Une variable statique à l'intérieur d'une méthode Python
la source
Personnellement, je préfère ce qui suit aux décorateurs. À chacun son bonheur.
la source
Cette réponse s'appuie sur la réponse de @claudiu.
J'ai trouvé que mon code devenait moins clair quand je devais toujours ajouter le nom de la fonction, chaque fois que j'avais l'intention d'accéder à une variable statique.
À savoir, dans mon code de fonction, je préférerais écrire:
au lieu de
Donc, ma solution est de:
statics
attribut à la fonctionstatics
comme alias àmy_function.statics
Remarque
Ma méthode utilise une classe nommée
Bunch
, qui est un dictionnaire qui prend en charge l'accès de style attribut, à la JavaScript (voir l' article d'origine à ce sujet, vers 2000)Il peut être installé via
pip install bunch
Il peut également être écrit à la main comme suit:
la source
types.SimpleNamespace
(disponible depuis 3.3) prend en charge ce comportement hors de la boîte (et est implémenté en C sur CPython, donc c'est à peu près aussi rapide que possible).S'appuyant sur la réponse de Daniel (ajouts):
La raison pour laquelle j'ai voulu ajouter cette partie est que les variables statiques sont utilisées non seulement pour incrémenter d'une certaine valeur, mais aussi pour vérifier si la var statique est égale à une certaine valeur, comme exemple réel.
La variable statique est toujours protégée et utilisée uniquement dans le cadre de la fonction use_foo ()
Dans cet exemple, l'appel à foo () fonctionne exactement comme (par rapport à l'équivalent c ++ correspondant):
si la classe Foo est définie de manière restrictive comme une classe singleton, ce serait idéal. Cela le rendrait plus pythonique.
la source
Bien sûr, c'est une vieille question, mais je pense que je pourrais fournir une mise à jour.
Il semble que l'argument de la performance soit obsolète. La même suite de tests semble donner des résultats similaires pour siInt_try et isInt_re2. Bien sûr, les résultats varient, mais il s'agit d'une session sur mon ordinateur avec python 3.4.4 sur le noyau 4.3.01 avec Xeon W3550. Je l'ai exécuté plusieurs fois et les résultats semblent similaires. J'ai déplacé l'expression régulière globale en fonction statique, mais la différence de performances est négligeable.
Avec un problème de performance à l'écart, il semble que try / catch produirait le code le plus futur et le plus à l'épreuve des coins, alors peut-être simplement l'envelopper dans la fonction
la source