【问题标题】:@staticmethod with @property@staticmethod 和 @property
【发布时间】:2010-12-14 10:14:23
【问题描述】:

我想要

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)

【问题讨论】:

  • singleton_object 定义在哪里?
  • self 定义在哪里?
  • 抱歉,问题扩展了
  • tonfa:singleton_object 在第 2 行中定义。self 不是静态方法中的东西,因为它指的是实例,而静态属性和方法指的是类。

标签: python singleton properties


【解决方案1】:

在这个非数据描述符解决方案中,linters 不会抱怨,因为它是一个静态方法。在singleton 定义中,只需将最后一行更改为return Stats.singleton(无需调用)。

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

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

      【解决方案3】:

      我想提供一个 Python 代码 sn-p 来展示 propertystaticmethod 是如何工作的。

      它们都是实现 __get____set__

      的描述符

      属性是数据描述符

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

      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
      

      【讨论】:

      • 我正在尝试研究 fgetfsetfdeldoc 的值的来源。有什么提示吗?
      • 您可以在docs.python.org/3/library/functions.html#property中看到属性的典型用法
      • @IAbstract 通常property 是用一个参数隐式调用的——用@property 修饰的函数。所以__init__ 将获得该函数作为fget 参数。但它也支持显式地将 setter 和 deleter 作为 init 参数,以防你有一个用例,你想使用 foo = property(...) 而不是通过装饰器语法。
      【解决方案4】:

      跟进 KyleAlanHale 所写的内容:

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

      Stats.singleton = 5
      

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

      single = Stats.singleton
      print single
      

      你会得到

      5
      

      我认为你最好不要使用 @classproperties 装饰来使用 Kyle 的答案。

      【讨论】:

        【解决方案5】:

        就最初的问题而言,用户 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
        

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

        【讨论】:

        • 鉴于赞成票,为什么这不是第一个列出的答案?它完美地工作并达到目的,上面的答案没有帮助。谢谢。
        【解决方案6】:

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

        class Stats:
            twitter_count = 0
        
        Stats.twitter_count +=1
        Stats.twitter_count +=1
        assert Stats.twitter_count == 2
        

        【讨论】:

        • 我有一点背景故事,因为我希望 AppEngine db.Model 对象是单例(数据库中只有一个 Stats 对象)。所以我需要在内存中至少有一个实例。
        • 我不明白。当您有一个数据库时,所有实例已经共享相同的状态,即数据库中一行的状态。两个实例可能不相同,但任何值得该名称的 ORM 都将确保更改其中一个也会更改另一个。
        • 你到处听到人们说静态方法和单例在 Python 中毫无意义,yadda,yadda。那是废话。单例是一种有效的、需要的模式。 “我们”更大的编程社区想知道的是“我们应该如何”以“正确的方式”去做。我认为,任何不直接回答的答案都是无益的。一方面,我对这个问题感到困惑。 :-) 帮助!
        • '单例在 python 中毫无意义。'这是一个非常通用的注释,将单例作为语义概念和 python 作为编程语言混为一谈。这有点像说“在英国喝咖啡毫无意义”,实际上你的意思是说在英国喝咖啡不像在美国那么普遍。
        • 我同意其他人的说法,单例是一种有效的模式,并且在描述软件中应该只有一个实例的实体时非常有用。我经常看到将某些东西描述为 pythonic 以合理化不良软件实践。
        【解决方案7】:

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

        【讨论】:

        • 我认为您的“读者练习”没有答案;当您对类进行查找时,不会调用数据描述符的 set 方法。绑定刚刚更改。
        • @Reid:您的评论似乎是由于术语混淆。澄清一下,@staticmethod 实现了您认为的静态方法,@classmethod 实现了您认为稍微灵活的静态方法。没有太多理由让静态方法的灵活性稍差一些,并且将其删除不会剥夺您使用静态方法的能力。 @staticmethod 可以做的任何事情,@classmethod 可以做,@classmethod 可以做得更多。
        • 当这个答案说静态方法在 Python 中没有意义时,它是在谈论 @staticmethod,而不是关于属于类本身而不是其实例的方法的概念。
        猜你喜欢
        • 2015-01-03
        • 2011-05-27
        • 2016-03-17
        • 2020-04-11
        • 2019-08-11
        • 1970-01-01
        • 1970-01-01
        • 2015-01-03
        • 1970-01-01
        相关资源
        最近更新 更多