Подтвердить что ты не робот

@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)
4b9b3361

Ответ 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__() для реализации фабричного шаблона чистым питоническим способом.