Les variables de classe statiques sont-elles possibles en Python?


Réponses:


1900

Les variables déclarées à l'intérieur de la définition de classe, mais pas à l'intérieur d'une méthode sont des variables de classe ou statiques:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

Comme le souligne @ millerdev , cela crée une ivariable au niveau de la classe , mais elle est distincte de toute ivariable au niveau de l'instance , vous pourriez donc avoir

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

Ceci est différent de C ++ et Java, mais pas si différent de C #, où un membre statique n'est pas accessible à l'aide d'une référence à une instance.

Découvrez ce que le didacticiel Python a à dire sur le sujet des classes et des objets de classe .

@Steve Johnson a déjà répondu concernant les méthodes statiques , également documentées sous "Fonctions intégrées" dans la référence de bibliothèque Python .

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@beidy recommande les méthodes de classe par rapport à la méthode statique, car la méthode reçoit ensuite le type de classe comme premier argument, mais je suis encore un peu flou sur les avantages de cette approche par rapport à la méthode statique. Si vous l'êtes aussi, cela n'a probablement pas d'importance.


11
Je suis en train d' apprendre Python, mais les avantages de @classmethodplus @staticmethodAFAIK est que vous obtenez toujours le nom de la classe la méthode a été invoquée, même si elle est une sous - classe. Une méthode statique n'a pas ces informations, elle ne peut donc pas appeler une méthode redéfinie, par exemple.
Seb

49
@theJollySin la manière pythonique pour les constantes est de ne pas développer une classe pour les constantes. Il suffit d'en avoir const.pyavec PI = 3.14et vous pouvez l'importer partout. from const import PI
Giszmo

31
Cette réponse est susceptible de confondre le problème des variables statiques. Pour commencer, ce i = 3n'est pas une variable statique, c'est un attribut de classe, et comme il est distinct d'un attribut de niveau instance, iil ne se comporte pas comme une variable statique dans d'autres langages. Voir la réponse de millerdev , la réponse de Yann , et ma réponse ci - dessous.
Rick prend en charge Monica

2
donc une seule copie de i(variable statique) sera en mémoire même si je crée des centaines d'instances de cette classe?
sdream

2
Pour toute personne intéressée qui est Daniel mentionnée dans le commentaire @Dubslow, c'est millerdev ( machine de retour )
HeyJude

619

@Blair Conrad a déclaré que les variables statiques déclarées à l'intérieur de la définition de classe, mais pas à l'intérieur d'une méthode sont des variables de classe ou "statiques":

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

Il y a quelques pièges ici. Poursuivant sur l'exemple ci-dessus:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

Remarquez comment la variable d'instance t.is'est désynchronisée avec la variable de classe "statique" lorsque l'attribut a iété défini directement sur t. Ceci est dû au faiti a été relié à l'intérieur de l' tespace de noms, qui est distinct de l' Testespace de noms. Si vous souhaitez modifier la valeur d'une variable "statique", vous devez la modifier dans la portée (ou l'objet) où elle a été initialement définie. Je mets "statique" entre guillemets car Python n'a pas vraiment de variables statiques au sens où C ++ et Java en ont.

Bien qu'il ne dise rien de spécifique sur les variables ou méthodes statiques, le didacticiel Python contient des informations pertinentes sur classes et les objets de classe .

@Steve Johnson a également répondu concernant les méthodes statiques, également documentées sous "Fonctions intégrées" dans la référence de bibliothèque Python.

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beid a également mentionné classmethod, qui est similaire à staticmethod. Le premier argument d'une méthode de classe est l'objet classe. Exemple:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

Représentation picturale de l'exemple ci-dessus


3
Je vous suggère d'étendre un peu l'exemple: si, après avoir défini Test.i = 6, vous instanciez ensuite un nouvel objet (par exemple, u = Test ()), le nouvel objet "héritera" de la nouvelle valeur de classe (par exemple, ui == 6)
Mark

2
Une façon de garder les variables statiques de synchronisation est de leur faire des propriétés: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Maintenant , vous pouvez le faire x = Test(), x.i = 12, assert x.i == Test.i.
Rick prend en charge Monica

1
Je pourrais donc dire que toutes les variables sont statiques au départ, puis accéder aux instances crée des variables d'instance au moment de l'exécution?
Ali

C'est peut-être intéressant: si vous définissez une méthode dans Test qui change Test.i, cela affectera les valeurs Test.i et ti.
Pablo

@millerdev, comme vous l'avez mentionné, Python n'a pas de variables statiques comme C ++ ou JAVA..Alors ce sera correct de dire que Test.i est plus une variable de classe plutôt qu'une variable statique?
Tyto

198

Méthodes statiques et de classe

Comme les autres réponses l'ont noté, les méthodes statiques et de classe sont facilement réalisables à l'aide des décorateurs intégrés:

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

Comme d'habitude, le premier argument de MyMethod()est lié à l'objet instance de classe. En revanche, le premier argument de MyClassMethod()est lié à l'objet de classe lui-même (par exemple, dans ce cas, Test). Pour MyStaticMethod(), aucun des arguments n'est lié et avoir des arguments est facultatif.

"Variables statiques"

Cependant, implémenter des "variables statiques" (enfin, des variables statiques mutables , de toute façon, si ce n'est pas une contradiction en termes ...) n'est pas aussi simple. Comme l'a souligné millerdev dans sa réponse , le problème est que les attributs de classe de Python ne sont pas vraiment des "variables statiques". Considérer:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

En effet, la ligne x.i = 12a ajouté un nouvel attribut d'instance ià xau lieu de modifier la valeur de l' attribut de Testclasse i.

Le comportement partiel attendu des variables statiques, c'est-à-dire la synchronisation de l'attribut entre plusieurs instances (mais pas avec la classe elle-même; voir "gotcha" ci-dessous), peut être obtenu en transformant l'attribut de classe en propriété:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

Vous pouvez maintenant:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

La variable statique restera désormais synchronisée entre toutes les instances de classe .

(REMARQUE: c'est-à-dire, sauf si une instance de classe décide de définir sa propre version de _i ! Mais si quelqu'un décide de faire CELA, il mérite ce qu'il obtient, n'est-ce pas ???)

Notez que techniquement parlant, ce in'est toujours pas une «variable statique» du tout; c'est un property, qui est un type spécial de descripteur. Cependant, leproperty comportement est désormais équivalent à une variable statique (mutable) synchronisée sur toutes les instances de classe.

"Variables statiques" immuables

Pour un comportement de variable statique immuable, omettez simplement le propertysetter:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

Maintenant, tenter de définir l' iattribut d' instance retournera un AttributeError:

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

Un Gotcha à connaître

Notez que les méthodes ci-dessus ne fonctionnent qu'avec les instances de votre classe - elles ne fonctionneront pas lors de l'utilisation de la classe elle-même . Ainsi, par exemple:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

La ligne assert Test.i == x.iproduit une erreur, car l' iattribut de Testet xsont deux objets différents.

Beaucoup de gens trouveront cela surprenant. Mais cela ne devrait pas l'être. Si nous revenons en arrière et inspectons notre Testdéfinition de classe (la deuxième version), nous prenons note de cette ligne:

    i = property(get_i) 

De toute évidence, le membre ide Testdoit être un propertyobjet, qui est le type d'objet renvoyé par la propertyfonction.

Si vous trouvez cela déroutant, vous y pensez probablement encore du point de vue d'autres langages (par exemple Java ou c ++). Vous devriez aller étudier l' propertyobjet, sur l'ordre dans lequel les attributs Python sont retournés, le protocole de descripteur et l'ordre de résolution des méthodes (MRO).

Je présente ci-dessous une solution au 'gotcha' ci-dessus; cependant, je suggère - vigoureusement - que vous n'essayez pas de faire quelque chose comme ce qui suit jusqu'à ce que - au moins - vous compreniez bien pourquoi assert Test.i = x.iune erreur se produit.

Variables statiques réelles et réelles -Test.i == x.i

Je présente la solution (Python 3) ci-dessous à titre informatif uniquement. Je ne l'approuve pas comme une "bonne solution". J'ai des doutes quant à la nécessité réelle d'émuler le comportement des variables statiques d'autres langages en Python. Cependant, peu importe s'il est réellement utile, ce qui suit devrait aider à mieux comprendre le fonctionnement de Python.

MISE À JOUR: cette tentative est vraiment assez horrible ; si vous insistez pour faire quelque chose comme ça (indice: s'il vous plaît ne le faites pas; Python est un langage très élégant et le faire en se comportant comme un autre langage n'est tout simplement pas nécessaire), utilisez plutôt le code dans la réponse d'Ethan Furman .

Émulation du comportement de variables statiques d'autres langues à l'aide d'une métaclasse

Une métaclasse est la classe d'une classe. La métaclasse par défaut pour toutes les classes en Python (c'est-à-dire les classes "nouveau style" après Python 2.3 je crois) est type. Par exemple:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

Cependant, vous pouvez définir votre propre métaclasse comme ceci:

class MyMeta(type): pass

Et appliquez-le à votre propre classe comme ceci (Python 3 uniquement):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Ci-dessous se trouve une métaclasse que j'ai créée qui tente d'émuler le comportement de "variable statique" d'autres langues. Il fonctionne essentiellement en remplaçant le getter, le setter et le deleter par défaut par des versions qui vérifient si l'attribut demandé est une "variable statique".

Un catalogue des "variables statiques" est stocké dans l' StaticVarMeta.staticsattribut. Toutes les demandes d'attribut sont initialement tentées d'être résolues à l'aide d'un ordre de résolution de remplacement. J'ai appelé cela «l'ordre de résolution statique» ou «SRO». Cela se fait en recherchant l'attribut demandé dans l'ensemble des "variables statiques" pour une classe donnée (ou ses classes parentes). Si l'attribut n'apparaît pas dans le "SRO", la classe retombera sur le comportement par défaut de l'attribut get / set / delete (c'est-à-dire "MRO").

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

J'ai essayé d'utiliser votre chemin mais j'ai rencontré un problème, veuillez consulter ma question ici stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey: Je suppose que vous devriez généralement voir tout ce que vous faites sur l'instance de classe Test(avant de l'utiliser pour instancier des instances) comme étant dans le domaine de la méta-programmation? Par exemple, vous modifiez le comportement de classe en faisant Test.i = 0(ici, vous détruisez simplement entièrement l'objet de propriété). Je suppose que le "mécanisme de propriété" intervient uniquement sur l'accès à la propriété sur les instances d'une classe (à moins que vous ne changiez le comportement sous-jacent en utilisant une méta-classe comme intermédiaire, peut-être). Btw, veuillez terminer cette réponse :-)
Ole Thomsen Buus

1
@RickTeachey Merci :-) Votre métaclasse à la fin est intéressante mais est en fait un peu trop complexe à mon goût. Il peut être utile dans un grand framework / application où ce mécanisme est absolument nécessaire. Quoi qu'il en soit, cela illustre que si un nouveau méta-comportement (complexe) non par défaut est vraiment nécessaire, Python le permet :)
Ole Thomsen Buus

1
@OleThomsenBuus: Vérifiez ma réponse pour une métaclasse plus simple qui fait le travail.
Ethan Furman

1
@taper Vous avez raison; J'ai édité la réponse pour résoudre le problème (je n'arrive pas à croire qu'il soit resté là depuis si longtemps!). Désolé pour la confusion.
Rick soutient Monica

33

Vous pouvez également ajouter des variables de classe aux classes à la volée

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

Et les instances de classe peuvent changer les variables de classe

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

3
Les nouvelles variables de classe seront-elles conservées même si la classe est importée dans un autre module?
zakdances

Oui. Les classes sont en fait des singletons, quel que soit l'espace de noms à partir duquel vous les appelez.
Pedro

@Gregory vous avez dit "Et les instances de classe peuvent changer les variables de classe" En fait, cet exemple est appelé accès et non modification. La modification a été effectuée par l'objet lui-même via sa propre fonction append ().
Amr ALHOSSARY

19

Personnellement, j'utilisais une méthode de classe chaque fois que j'avais besoin d'une méthode statique. Principalement parce que je reçois la classe comme argument.

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

ou utilisez un décorateur

class myObj(object):
   @classmethod
   def myMethod(cls)

Pour les propriétés statiques .. Il est temps de rechercher une définition de python .. la variable peut toujours changer. Il en existe deux types mutables et immuables. Il existe également des attributs de classe et des attributs d'instance. Rien de tel que les attributs statiques au sens de java & c ++

Pourquoi utiliser la méthode statique au sens pythonique, si elle n'a aucun rapport avec la classe! Si j'étais vous, j'utiliserais la méthode de classe ou définirais la méthode indépendamment de la classe.


1
Les variables ne sont ni modifiables ni immuables; les objets sont. (Cependant, un objet peut, avec divers degrés de succès, essayer d'empêcher l'attribution à certains de ses attributs.)
Davis Herring

Java et C ++ utilisent statique (mauvaise utilisation du mot, à mon humble avis) exactement comme vous utilisez l'instance par rapport à l'attribut de classe. Un attribut / méthode de classe est statique en Java et C ++, aucune différence, sauf qu'en Python le premier paramètre d'un appel de méthode de classe est la classe.
Angel O'Sphere

16

Une chose spéciale à noter sur les propriétés statiques et les propriétés d'instance, illustrée dans l'exemple ci-dessous:

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

Cela signifie avant d'affecter la valeur à la propriété d'instance, si nous essayons d'accéder à la propriété via l'instance, la valeur statique est utilisée. Chaque propriété déclarée dans la classe python a toujours un emplacement statique en mémoire .


16

Les méthodes statiques en python sont appelées méthodes de classe . Jetez un œil au code suivant

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

Notez que lorsque nous appelons la méthode myInstanceMethod , nous obtenons une erreur. En effet, il nécessite que cette méthode soit appelée sur une instance de cette classe. La méthode myStaticMethod est définie comme une méthode de classe à l'aide du décorateur @classmethod .

Juste pour les coups de pied et les rires, nous pourrions appeler myInstanceMethod sur la classe en passant dans une instance de la classe, comme ceci:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

2
Umm ... les méthodes statiques sont faites avec @staticmethod; @classmethodest (évidemment) pour les méthodes de classe (qui sont principalement destinées à être utilisées comme constructeurs alternatifs, mais peuvent servir à la rigueur de méthodes statiques qui reçoivent une référence à la classe par laquelle elles ont été appelées).
ShadowRanger

11

Lorsque vous définissez une variable membre en dehors d'une méthode membre, la variable peut être statique ou non statique selon la façon dont la variable est exprimée.

  • CLASSNAME.var est une variable statique
  • INSTANCENAME.var n'est pas une variable statique.
  • self.var à l'intérieur de la classe n'est pas une variable statique.
  • var à l'intérieur de la fonction membre de classe n'est pas définie.

Par exemple:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

Les résultats sont

self.var is 2
A.var is 1
self.var is 2
A.var is 3

L'indentation est rompue. Cela ne s'exécutera pas
Thomas Weller

10

Il est possible d'avoir staticdes variables de classe, mais cela ne vaut probablement pas la peine.

Voici une preuve de concept écrite en Python 3 - si l'un des détails exacts est faux, le code peut être modifié pour correspondre à peu près à ce que vous entendez par static variable:


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

et en cours d'utilisation:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

et quelques tests:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

8

Vous pouvez également appliquer une classe statique à l'aide de la métaclasse.

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

Ensuite, chaque fois que vous essayez d'initialiser MyClass par accident, vous obtenez une StaticClassError.


4
Pourquoi est-ce même une classe si vous n'allez pas l'instancier? Cela ressemble à tordre Python pour le transformer en Java ....
Ned Batchelder

1
L' idiome Borg est une meilleure façon de gérer cela.
Rick soutient Monica le

@NedBatchelder C'est une classe abstraite, destinée uniquement au sous
classement

1
J'espère que les sous-classes n'utilisent pas super () pour invoquer les __new__parents ...
Ned Batchelder

7

Un point très intéressant sur la recherche d'attributs de Python est qu'il peut être utilisé pour créer des " variables virtuelles ":

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

Normalement, il n'y a aucune affectation à ceux-ci après leur création. Notez que la recherche utilise selfcar, bien qu'elle labelsoit statique dans le sens où elle n'est pas associée à une instance particulière , la valeur dépend toujours de la (classe de l'instance).


6

En ce qui concerne cette réponse , pour une variable statique constante , vous pouvez utiliser un descripteur. Voici un exemple:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

résultant en ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

Vous pouvez toujours lever une exception si ignorer tranquillement la valeur de réglage ( passci-dessus) n'est pas votre truc. Si vous recherchez une variable de classe statique de style Java C ++:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

Jetez un œil à cette réponse et au HOWTO des documents officiels pour plus d'informations sur les descripteurs.


2
Vous pouvez également simplement utiliser @property, ce qui revient à utiliser un descripteur, mais c'est beaucoup moins de code.
Rick soutient Monica

6

Absolument Oui, Python en lui-même n'a pas explicitement de membre de données statiques, mais nous pouvons en avoir ainsi

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

production

0
0
1
1

explication

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"

6

Oui, certainement possible d'écrire des variables et des méthodes statiques en python.

Variables statiques: les variables déclarées au niveau de la classe sont appelées variables statiques accessibles directement à l'aide du nom de classe.

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

Variables d'instance: les variables liées et accessibles par l'instance d'une classe sont des variables d'instance.

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

Méthodes statiques: similaires aux variables, les méthodes statiques sont accessibles directement en utilisant le nom de classe. Pas besoin de créer une instance.

Mais gardez à l'esprit qu'une méthode statique ne peut pas appeler une méthode non statique en python.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

4

Pour éviter toute confusion potentielle, je voudrais opposer des variables statiques et des objets immuables.

Certains types d'objets primitifs comme les entiers, les flottants, les chaînes et les touches sont immuables en Python. Cela signifie que l'objet auquel un nom donné fait référence ne peut pas changer s'il appartient à l'un des types d'objet susmentionnés. Le nom peut être réaffecté à un autre objet, mais l'objet lui-même ne peut pas être modifié.

Le fait de rendre une variable statique va encore plus loin en interdisant au nom de la variable de pointer vers tout autre objet que celui vers lequel il pointe actuellement. (Remarque: il s'agit d'un concept logiciel général et non spécifique à Python; veuillez consulter les publications des autres pour plus d'informations sur la mise en œuvre de la statique dans Python).


4

La meilleure façon que j'ai trouvée est d'utiliser une autre classe. Vous pouvez créer un objet puis l'utiliser sur d'autres objets.

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

Avec l'exemple ci-dessus, j'ai créé une classe nommée staticFlag.

Cette classe doit présenter la var statique __success(Var Var statique statique).

tryIt classe représentait la classe régulière que nous devons utiliser.

Maintenant, j'ai créé un objet pour un drapeau ( staticFlag). Ce drapeau sera envoyé comme référence à tous les objets réguliers.

Tous ces objets sont ajoutés à la liste tryArr.


Résultats de ce script:

False
False
False
False
False
True
True
True
True
True

2

Variables statiques dans la fabrique de classes python3.6

Pour toute personne utilisant une fabrique de classes avec python3.6 et plus, utilisez le nonlocalmot - clé pour l'ajouter à la portée / au contexte de la classe en cours de création comme suit:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

oui, mais dans ce cas hasattr(SomeClass, 'x')est False. je doute que ce soit ce que quelqu'un entend par une variable statique.
Rick soutient Monica le

@RickTeachey lol, vous avez vu votre code de variable statique, stackoverflow.com/a/27568860/2026508 +1, monsieur Internet, et je pensais que hasattr ne fonctionnait pas comme ça? est donc some_varimmuable et défini statiquement, ou n'est-ce pas? Qu'est-ce que l'accès getter extérieur a à voir avec une variable statique ou non? j'ai tellement de questions maintenant. aimerait entendre des réponses lorsque vous aurez le temps.
jmunsch

Oui, cette métaclasse est assez ridicule. Je ne suis pas certain de comprendre les questions mais à mon avis, ce some_varn'est pas du tout un membre du groupe. En Python, tous les membres de la classe sont accessibles depuis l'extérieur de la classe.
Rick soutient Monica le

Le nonlocalclavier clavette la portée de la variable. La portée d'une définition de corps de classe est indépendante de la portée dans laquelle elle se trouve lorsque vous dites nonlocal some_var, c'est-à-dire simplement créer une référence de nom non local (lire: PAS dans la portée de définition de classe) à un autre objet nommé. Par conséquent, il n'est pas attaché à la définition de classe car il n'est pas dans la portée du corps de classe.
Rick prend en charge Monica le

1

C'est donc probablement un hack, mais j'ai utilisé eval(str)pour obtenir un objet statique, une sorte de contradiction, en python 3.

Il existe un fichier Records.py qui n'a que des classobjets définis avec des méthodes statiques et des constructeurs qui enregistrent certains arguments. Ensuite, à partir d'un autre fichier .py, je import Recordsdois sélectionner dynamiquement chaque objet, puis l'instancier à la demande en fonction du type de données lues.

Donc, où object_name = 'RecordOne'ou le nom de la classe, j'appelle cur_type = eval(object_name), puis pour l'instancier, vous le faites cur_inst = cur_type(args) Cependant, avant d'instancier, vous pouvez appeler des méthodes statiques, cur_type.getName()par exemple, un peu comme l'implémentation d'une classe de base abstraite ou quel que soit l'objectif. Cependant, dans le backend, il est probablement instancié en python et n'est pas vraiment statique, car eval renvoie un objet .... qui doit avoir été instancié .... qui donne un comportement statique.


0

Vous pouvez utiliser une liste ou un dictionnaire pour obtenir un "comportement statique" entre les instances.

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False

0

Si vous essayez de partager une variable statique pour, par exemple, l'augmenter sur d'autres instances, quelque chose comme ce script fonctionne très bien:

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.