我想要
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)
用户 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
如上所述,这种创建单例的方式并不是一个好的设计模式;如果必须这样做,元类工厂是更好的方法。不过,我只是对阶级财产的前景感到兴奋,所以,就这样了。
我发现的最简单的方法是使用实例属性来包装类成员:
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
我想给出一个Python代码片段来展示property和staticmethod如何工作会很有帮助。
它们都是实现 get 或 set
的描述符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
@property name
相当于name = property(name)
,fget参数是name() I方法@name.setter
相当于name.setter(name)
,第一个name
是步骤1中创建的属性,第二个是name II,fset。看起来像个把戏!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
在这个非数据描述符解决方案中,linters 不会抱怨,因为它是静态方法。在
singleton
定义中,只需将最后一行更改为 return Stats.singleton
(无需调用)。
class staticproperty(staticmethod):
def __get__(self, *_):
return self.__func__()
跟进 KyleAlanHale 所写的内容:
他的例子效果很好,直到你尝试这样做:
Stats.singleton = 5
这不会给你一个错误,它会覆盖这个函数,这样当你输入next时
single = Stats.singleton
print single
你会得到
5
我认为你最好使用凯尔的答案而不使用@classproperties装饰。
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)
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
包裹的方法如何工作,您会注意到它的工作原理。
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)
静态方法在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
单例在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