78

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)
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Paul Tarjan
  • 48,968
  • 59
  • 172
  • 213

10 Answers10

124

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.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
kylealanhale
  • 1,491
  • 2
  • 10
  • 7
  • 3
    Why isn't this the first listed answer, given the upvotes? It works perfectly and serves the purpose, the answers above are unhelpful. Thank you. – Oliver May 07 '20 at 02:43
  • 1
    wait, why not just `return self.fget(owner)`, i.e. `return self.fget(objtype)`? – Kache Apr 07 '22 at 23:22
4

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 (Refers to Descriptor HowTo Guide)

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

Take a simple example

class Foo:
    def __init__(self):
        self._name = None

    @property  # I
    def name(self):
        return self._name

    @name.setter  # II
    def name(self, value):
        self._name = value
  1. @property name is equivalent to name = property(name), the fget parameter is the name() I method
  2. @name.setter is equivalent to name.setter(name), the first name is the property created in step 1, the second one is name II, the fset. Look like a trick!

staticmethod is a non-data descriptor (Refers to Static Methods and Class Methods)

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
Jacky1205
  • 3,273
  • 3
  • 22
  • 44
  • 2
    I'm trying to research the origin of the values for `fget`, `fset`, `fdel`, & `doc`. Any hints? – IAbstract May 15 '20 at 17:20
  • 1
    You could see typical use of property in https://docs.python.org/3/library/functions.html#property – Jacky1205 May 19 '20 at 02:07
  • @IAbstract Normally `property` is called implicitly with one argument - the function that is being decorated with `@property`. So `__init__` will get that function as the `fget` argument. But it also supports explicitly taking the setter and deleter as init arguments, in case you have a use-case where you want to do `foo = property(...)` instead of going through the decorator syntax. – mtraceur Jun 04 '21 at 21:38
4

The easiest method I've found is using an instance property to wrap a class member:

class MyClass:
  _configured = False

  @property
  def configured(self) -> bool:
    print("configured.getter")
    return self.__class__._configured
  
  @configured.setter
  def configured(self, value: bool) -> None:
    print("configured.setter")
    self.__class__._configured = value

  @classmethod
  def is_class_configured(cls) -> bool:
    print("is_class_configured")
    return cls._configured

m1 = MyClass()
print(f"m1.configured: {m1.configured}\n")
print(f"MyClass._configured: {MyClass._configured}\n")
print(f"m1.is_class_configured(): {m1.is_class_configured()}\n")
m1.configured = True
print(f"setting m1.configured = True")
print(f"------------------------------")
print(f"m1.configured: {m1.configured}\n")
print(f"MyClass._configured: {MyClass._configured}\n")
print(f"m1.is_class_configured(): {m1.is_class_configured()}\n")

configured.getter
m1.configured: False

MyClass._configured: False

is_class_configured
m1.is_class_configured(): False

configured.setter
setting m1.configured = True
------------------------------
configured.getter
m1.configured: True

MyClass._configured: True

is_class_configured
m1.is_class_configured(): True
mattyb
  • 1,011
  • 2
  • 10
  • 26
3

In this non-data descriptor solution linters doesn't complaint, because it is a staticmethod. In singleton definition just change last line to return Stats.singleton (without a call).

class staticproperty(staticmethod):
    def __get__(self, *_):         
        return self.__func__()
Karolius
  • 543
  • 1
  • 6
  • 12
0

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.

Charles Plager
  • 494
  • 8
  • 21
0

OP asked how to implement a staticproperty, and most of the answers (correctly) point out that OP actually wants a classproperty.

Below is an implementation for both of these decorators.

class staticproperty(property):
  def __get__(self, owner_self, owner_cls):         
    return self.fget()

class classproperty(property):
  def __get__(self, owner_self, owner_cls):
    return self.fget(owner_cls)


class Example:
  _class_val = 123

  @classproperty
  def class_val(cls):
    return cls._class_val
  
  @staticproperty
  def static_val():
    return 456

print(Example.class_val)
print(Example.static_val)
sam-6174
  • 3,104
  • 1
  • 33
  • 34
0

This somehow works:

class Stats:
    singleton_object = None

    @classmethod
    @property
    def singleton(cls):
        if cls.singleton_object:
            return cls.singleton_object
        cls.singleton_object = cls()
        return cls.singleton

print(type(Stats.singleton))
foo = Stats()
Stats.singleton.bar = 'Hello, world!'
print(foo.singleton.bar)
<class '__main__.Stats'>
Hello, world!

I simplified the singleton property:

class Stats:
    singleton_object = None

    @classmethod
    @property
    def singleton(cls):
        if cls.singleton_object is None:
            cls.singleton_object = cls()
        return cls.singleton_object

print(type(Stats.singleton))
foo = Stats()
Stats.singleton.bar = 'Hello, world!'
print(foo.singleton.bar)
<class '__main__.Stats'>
Hello, world!
Ritalin
  • 1
  • 1
-1
class StaticProperty(object):
    def __init__(self, function):
        self.function = function
    def __get__(self, *args, **kwargs):
        print("static property")
        return self.function()


class test():
    def __init__(self):
        
    @StaticProperty
    def greetings():
        return ("Hello Stack overflow")

print(test.greetings)
Rich Lysakowski PhD
  • 2,702
  • 31
  • 44
VSR
  • 49
  • 4
  • Please provide an explanation with your answer. It is at risk of being deleted for low quality (incomplete answer and misspelled English ("Property" is misspelled). – Rich Lysakowski PhD Sep 12 '22 at 05:29
-6

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
u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
  • 3
    I don't think there is an answer to your 'exercise for the reader'; a data descriptor's __set__ method doesn't get called when you're doing a lookup on a class. The binding just gets changed. – Matt Anderson Nov 08 '09 at 19:31
  • @Reid: Your comment appears to be due to terminological confusion. To clarify, `@staticmethod` makes what you would consider static methods, and `@classmethod` makes what you would consider *slightly more flexible* static methods. There's not much reason for the one that makes slightly less flexible static methods to exist, and taking it out would not deprive you of the ability to use static methods. Anything `@staticmethod` can do, `@classmethod` can do, and `@classmethod` can do a bit more. – user2357112 Nov 15 '19 at 06:31
  • 1
    When this answer says static methods don't make sense in Python, it's talking about `@staticmethod`, not about the concept of a method that belongs to a class itself instead of its instances. – user2357112 Nov 15 '19 at 06:40
-24

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
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • I have a bit of a backstory, in that I want an AppEngine db.Model object to be a singleton (just one Stats object in the database). So I need to have at least one instance in memory. – Paul Tarjan Nov 08 '09 at 20:44
  • 1
    I don't get it. When you have a DB behind that, all instances already share the same state, the state of a row in your DB. Two instances might not be identical, but any ORM deserving that name will make sure that changing either will change the other too. – Jochen Ritzel Nov 09 '09 at 12:24
  • 101
    All over you hear people say that static methods and singletons are pointless in Python, yadda, yadda. That's crap. Singleton is a valid, needed pattern. What "we" the larger programming community want to know is "how" we are supposed to do it the "right way". Any answer that does not directly answer that is, I think, unhelpful. I, for one, am stumped on this issue. :-) Help! – 101010 Sep 28 '11 at 21:23
  • 15
    'Singletons are pointless in python.' That's an extremely generic comment and conflates singletons as a semantical concept and python as a programming language. It's a bit like saying 'Drinking coffee is pointless in England,' when really you mean that drinking coffee is not as common in England as it is in the US. – yeoman Dec 28 '16 at 10:31
  • Likewise, in python the use for singletons is more or less down to the few cases when you actually need a singleton, other that as a means to get something done the way you would in a different environment. But that doesn't make the concept of a singleton pointless in python. – yeoman Dec 28 '16 at 10:41
  • How can someone emphasise on how pointless Singletons are in Python? Let me give you an example, and please let me know another way you'd do it without using the (much needed, in certain cases) singleton pattern. Imagine you have a class that is used to authenticate (the first time) and return the token the rest of the times; while restricting authentication for new objects; how would you do it? I'd opt for a Singleton. – Julian Camilleri Aug 21 '18 at 11:26
  • 3
    I agree with what others have said, singletons are a valid pattern and are very useful when describing an entity in software for which there should only be a single instance. I have seen too often describing something as pythonic in order to rationalize bad software practices. – Howard Swope Dec 06 '18 at 16:37
  • This isn't an answer – Shayne Dec 15 '20 at 06:24
  • Surprised to see this downvoted so. Not only did this answer the question, but additionally: In what situation in Python is using "The Singleton Pattern" the simplest implementation, considering there are idiomatic alternatives like module and class variables? – Kache Apr 07 '22 at 20:55