English 中文(简体)
@staticmethod with @property
原标题:

I want

Stats.singleton.twitter_count += 1

and I thought I could do

class Stats:
    singleton_object = None

    @property
    @staticmethod
    def singleton():
        if Stats.singleton_object:
            return Stats.singleton_object
        Stats.singleton_object = Stats()
        return Stats.singleton()

But it throws an exception:

>>> 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)
问题回答

User kaizer.se was onto something as far as the original question goes. I took it a step further in terms of simplicity, so that it now requires only a single decorator:

class classproperty(property):
    def __get__(self, cls, owner):
        return classmethod(self.fget).__get__(None, owner)()

Usage:

class Stats:
    _current_instance = None

    @classproperty
    def singleton(cls):
        if cls._current_instance is None:
            cls._current_instance = Stats()
        return cls._current_instance

As noted, this way of creating a singleton is not a good design pattern; if that must be done, a metaclass factory is a much better way to do it. I was just excited about the prospect of a class property though, so, there it is.

The easiest method I ve found is using an instance property to wrap a class member:

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}
")
print(f"MyClass._configured: {MyClass._configured}
")
print(f"m1.is_class_configured(): {m1.is_class_configured()}
")
m1.configured = True
print(f"setting m1.configured = True")
print(f"------------------------------")
print(f"m1.configured: {m1.configured}
")
print(f"MyClass._configured: {MyClass._configured}
")
print(f"m1.is_class_configured(): {m1.is_class_configured()}
")

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

In this non-data descriptor solution linters doesn t complaint, because it is a staticmethod. In singleton definition just change last line to return Stats.singleton (without a call).

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

I guess giving a Python code snippet to show how do property and staticmethod work would be helpful.

Both of them are descriptors which implements get or set

property is a data descriptor (Refers to 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__)

Take a simple example

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 is equivalent to name = property(name), the fget parameter is the name() I method
  2. @name.setter is equivalent to name.setter(name), the first name is the property created in step 1, the second one is name II, the fset. Look like a trick!

staticmethod is a non-data descriptor (Refers to Static Methods and Class Methods)

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

Following up with what KyleAlanHale wrote:

His example works great, until you try and do:

Stats.singleton = 5

This won t give you an error, it will overwrite the function, so that when you type next

single = Stats.singleton
print single

You ll get

5

I think you re best off using Kyle s answer without the @classproperties decoration.

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)




相关问题
Can Django models use MySQL functions?

Is there a way to force Django models to pass a field to a MySQL function every time the model data is read or loaded? To clarify what I mean in SQL, I want the Django model to produce something like ...

An enterprise scheduler for python (like quartz)

I am looking for an enterprise tasks scheduler for python, like quartz is for Java. Requirements: Persistent: if the process restarts or the machine restarts, then all the jobs must stay there and ...

How to remove unique, then duplicate dictionaries in a list?

Given the following list that contains some duplicate and some unique dictionaries, what is the best method to remove unique dictionaries first, then reduce the duplicate dictionaries to single ...

What is suggested seed value to use with random.seed()?

Simple enough question: I m using python random module to generate random integers. I want to know what is the suggested value to use with the random.seed() function? Currently I am letting this ...

How can I make the PyDev editor selectively ignore errors?

I m using PyDev under Eclipse to write some Jython code. I ve got numerous instances where I need to do something like this: import com.work.project.component.client.Interface.ISubInterface as ...

How do I profile `paster serve` s startup time?

Python s paster serve app.ini is taking longer than I would like to be ready for the first request. I know how to profile requests with middleware, but how do I profile the initialization time? I ...

Pragmatically adding give-aways/freebies to an online store

Our business currently has an online store and recently we ve been offering free specials to our customers. Right now, we simply display the special and give the buyer a notice stating we will add the ...

Converting Dictionary to List? [duplicate]

I m trying to convert a Python dictionary into a Python list, in order to perform some calculations. #My dictionary dict = {} dict[ Capital ]="London" dict[ Food ]="Fish&Chips" dict[ 2012 ]="...

热门标签