@staticmethod 和 @property

问题描述 投票:0回答:10

我想要

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)
python singleton properties
10个回答
124
投票

用户 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

如上所述,这种创建单例的方式并不是一个好的设计模式;如果必须这样做,元类工厂是更好的方法。不过,我只是对阶级财产的前景感到兴奋,所以,就这样了。


4
投票

我发现的最简单的方法是使用实例属性来包装类成员:

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

4
投票

我想给出一个Python代码片段来展示propertystaticmethod如何工作会很有帮助。

它们都是实现 getset

的描述符

property是一个数据描述符(参考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__)

举个简单的例子

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
    相当于
    name = property(name)
    fget参数是name() I方法
  2. @name.setter
    相当于
    name.setter(name)
    ,第一个
    name
    是步骤1中创建的属性,第二个是name IIfset。看起来像个把戏!

staticmethod是一个非数据描述符(参考静态方法和类方法

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

3
投票

在这个非数据描述符解决方案中,linters 不会抱怨,因为它是静态方法。在

singleton
定义中,只需将最后一行更改为
return Stats.singleton
(无需调用)。

class staticproperty(staticmethod):
    def __get__(self, *_):         
        return self.__func__()

0
投票

跟进 KyleAlanHale 所写的内容:

他的例子效果很好,直到你尝试这样做:

Stats.singleton = 5

这不会给你一个错误,它会覆盖这个函数,这样当你输入next时

single = Stats.singleton
print single

你会得到

5

我认为你最好使用凯尔的答案而不使用@classproperties装饰。


0
投票

OP询问如何实现

staticproperty
,大多数答案(正确)指出OP实际上想要一个
classproperty

下面是这两个装饰器的实现。

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)

0
投票
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()

如果您考虑用

@classmethod
包裹的方法如何工作,您会注意到它的工作原理。


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

-6
投票

静态方法在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

-24
投票

单例在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
不同,所以它并不总是那么简单。但就您的目的而言,这似乎足够了:

class Stats:
    twitter_count = 0

Stats.twitter_count +=1
Stats.twitter_count +=1
assert Stats.twitter_count == 2
© www.soinside.com 2019 - 2024. All rights reserved.