@staticmethod with @property

☆樱花仙子☆ 提交于 2019-12-17 23:10:17

问题


I want

Stats.singleton.twitter_count += 1

and I thought I could do

class Stats:
    singleton_object = None

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

But it throws an 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)

回答1:


Singletons are pointless in 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

Python's ints are differnt from simple objects, so it's not always that simple . But for your purposes, this seems to be enough:

class Stats:
    twitter_count = 0

Stats.twitter_count +=1
Stats.twitter_count +=1
assert Stats.twitter_count == 2



回答2:


User kaizer.se was onto something as far as the original question goes. I took it a step further in terms of simplicity, so that it now requires only a single decorator:

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

As noted, this way of creating a singleton is not a good design pattern; if that must be done, a metaclass factory is a much better way to do it. I was just excited about the prospect of a class property though, so, there it is.




回答3:


Static methods don't make sense in Python. That's because they do nothing that class methods can't, and are class methods much easier to extend in the future (when multiple class methods use each other etc).

What you need is simply a class method property.

I have a class method property from my code here. It is only read-only, that was all I needed (so the rest is an exercise to the reader):

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



回答4:


Following up with what KyleAlanHale wrote:

His example works great, until you try and do:

Stats.singleton = 5

This won't give you an error, it will overwrite the function, so that when you type next

single = Stats.singleton
print single

You'll get

5

I think you're best off using Kyle's answer without the @classproperties decoration.




回答5:


I think is this the best way to implement a 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

Note that the object is (re)initialized automatically (with appropriate parameters) after being returned by __new__(). Also I made it re-initializable because I know that sometimes I need to do this, when for example loading a config file (then I'll need variables reset to defaults).

P.S. You can also use __new__ combined with __init_subclass__() to implement the factory pattern in a clean pythonic way.




回答6:


I guess giving a Python code snippet to show how do property and staticmethod work would be helpful.

Both of them are descriptors which implements __get__ or __set__

property is a data descriptor

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__)

And staticmethod is a non-data descriptor

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


来源:https://stackoverflow.com/questions/1697501/staticmethod-with-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!