web-dev-qa-db-fra.com

@staticmethod avec @property

Je voudrais

Stats.singleton.Twitter_count += 1

et je pensais pouvoir faire

class Stats:
    singleton_object = None

    @property
    @staticmethod
    def singleton():
        if Stats.singleton_object:
            return Stats.singleton_object
        Stats.singleton_object = Stats()
        return Stats.singleton()

Mais cela lève une exception:

>>> Stats.singleton.a = "b"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'property' object has only read-only attributes (assign to .a)
43
Paul Tarjan

Les singletons sont inutiles en python.

class A:
  class_var = object()

# two objects
a, b = A(), A()

# same var everywhere
assert a.class_var is b.class_var is A.class_var

Les ints de Python sont différents des simples objects, donc ce n'est pas toujours aussi simple. Mais pour vos besoins, cela semble suffisant:

class Stats:
    Twitter_count = 0

Stats.Twitter_count +=1
Stats.Twitter_count +=1
assert Stats.Twitter_count == 2
3
Jochen Ritzel

L'utilisateur kaizer.se était sur quelque chose en ce qui concerne la question d'origine. Je suis allé encore plus loin en termes de simplicité, de sorte qu'il ne nécessite désormais qu'un seul décorateur:

class classproperty(property):
    def __get__(self, cls, owner):
        return classmethod(self.fget).__get__(None, owner)()

Usage:

class Stats:
    _current_instance = None

    @classproperty
    def singleton(cls):
        if cls._current_instance is None:
            cls._current_instance = Stats()
        return cls._current_instance

Comme indiqué, cette façon de créer un singleton n'est pas un bon modèle de conception; si cela doit être fait, une fabrique de métaclasses est une bien meilleure façon de le faire. J'étais tout simplement excité par la perspective d'une propriété de classe, donc ça y est.

70
kylealanhale

Les méthodes statiques n'ont pas de sens en Python. En effet, ils ne font rien que les méthodes de classe ne peuvent pas, et sont des méthodes de classe beaucoup plus faciles à étendre à l'avenir (lorsque plusieurs méthodes de classe s'utilisent, etc.).

Ce dont vous avez besoin est simplement une propriété de méthode de classe.

J'ai une propriété de méthode de classe de mon code ici. C'est seulement en lecture seule, c'était tout ce dont j'avais besoin (donc le reste est un exercice pour le lecteur):

class ClassProperty (property):
    """Subclass property to make classmethod properties possible"""
    def __get__(self, cls, owner):
        return self.fget.__get__(None, owner)()

# how I would use it
class Stats:
    singleton_object = None
    @ClassProperty
    @classmethod
    def singleton(cls):
        if cls.singleton_object is None:
            cls.singleton_object = cls()
        return cls.singleton_object
3
u0b34a0f6ae

Suivi de ce que KyleAlanHale a écrit:

Son exemple fonctionne très bien, jusqu'à ce que vous essayiez de faire:

Stats.singleton = 5

Cela ne vous donnera pas d'erreur, il écrasera la fonction, de sorte que lorsque vous tapez ensuite

single = Stats.singleton
print single

Tu auras

5

Je pense que vous feriez mieux d'utiliser la réponse de Kyle sans la décoration @classproperties.

1
Charles Plager

Je suppose que donner un Python pour montrer comment fonctionne propriété et méthode statique serait utile.

Les deux sont des descripteurs qui implémentent __ get __ ou __ set __

propriété est un descripteur de données

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

Et méthode statique est un descripteur non lié aux données

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f
0
Jacky1205

Je pense que c'est la meilleure façon de mettre en œuvre un singleton:

class Singleton:
    __static_self = None

    # __new__ takes object creation out of user's hands
    def __new__(cls, *args, **kwargs):
        if not cls.__static_self:
            cls.__static_self = super().__new__(cls)
        else:
            vars(cls.__static_self).clear()  # wipe old state
        return cls.__static_self

    @classmethod
    def get(cls):
        """ get the singleton instance """
        return cls.__static_self

class Foo(Singleton):
    def __init__(self, a):
        self._a = a

    @property
    def a(self):
        return self._a

f = Foo(1)
f1 = Foo.get()
f2 = Foo(2)
print(f is f1 and f1 is f2) # True
print(f.a) # 2

Notez que l'objet est (re) initialisé automatiquement (avec les paramètres appropriés) après avoir été retourné par __new__(). Je l'ai également réinitialisable car je sais que parfois je dois le faire, par exemple lors du chargement d'un fichier de configuration (alors j'ai besoin de réinitialiser les variables par défaut).

P.S. Vous pouvez également utiliser __new__ Combiné à __init_subclass__() pour implémenter le modèle d'usine d'une manière propre Pythonic.

0
profPlum