@staticmethod с @property
Я хочу
Stats.singleton.twitter_count += 1
и я думал, что смогу сделать
class Stats:
singleton_object = None
@property
@staticmethod
def singleton():
if Stats.singleton_object:
return Stats.singleton_object
Stats.singleton_object = Stats()
return Stats.singleton()
Но он выдает исключение:
>>> 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
Синглтоны бессмысленны в 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 int
отличаются от простых object
s, поэтому это не всегда так просто. Но для ваших целей это кажется достаточно:
class Stats:
twitter_count = 0
Stats.twitter_count +=1
Stats.twitter_count +=1
assert Stats.twitter_count == 2
Ответ 2
Пользователь kaizer.se был на что-то, что касается исходного вопроса. Я сделал шаг вперед с точки зрения простоты, так что теперь он требует только одного декоратора:
class classproperty(property):
def __get__(self, cls, owner):
return classmethod(self.fget).__get__(None, owner)()
Использование:
class Stats:
_current_instance = None
@classproperty
def singleton(cls):
if cls._current_instance is None:
cls._current_instance = Stats()
return cls._current_instance
Как уже отмечалось, этот способ создания синглета не является хорошим шаблоном проектирования; если это необходимо сделать, метакласс factory - гораздо лучший способ сделать это. Я был просто в восторге от перспективы собственности класса, так вот, вот оно.
Ответ 3
Статические методы не имеют смысла в Python. Это потому, что они ничего не делают, что методы класса не могут, и методы класса намного проще продлить в будущем (когда несколько методов класса используют друг друга и т.д.).
Вам нужно просто свойство метода класса.
У меня есть свойство метода класса из моего кода здесь. Он доступен только для чтения, это все, что мне нужно (так что остальное - упражнение для читателя):
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
Следуя за тем, что написал KyleAlanHale:
Его пример отлично работает, пока вы не попытаетесь сделать:
Stats.singleton = 5
Это не даст вам ошибку, она перезапишет функцию, так что при следующем следующем
single = Stats.singleton
print single
Вы получите
5
Я думаю, что вам лучше использовать ответ Кайла без украшения @classproperties.
Ответ 5
Я думаю, что это лучший способ реализовать синглтон:
class Singleton:
__static_self = None
# __new__ takes object creation out of user 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
Обратите внимание, что объект (повторно) инициализируется автоматически (с соответствующими параметрами) после его возврата __new__()
. Также я сделал его повторно инициализируемым, потому что я знаю, что иногда мне нужно это делать, например, при загрузке файла конфигурации (тогда мне понадобится сброс переменных к значениям по умолчанию).
PS Вы также можете использовать __new__
сочетании с __init_subclass__()
для реализации фабричного шаблона чистым питоническим способом.