317

I have a class with two class methods (using the classmethod() function) for getting and setting what is essentially a static variable. I tried to use the property() function with these, but it results in an error. I was able to reproduce the error with the following in the interpreter:

class Foo(object):
    _var = 5
    @classmethod
    def getvar(cls):
        return cls._var
    @classmethod
    def setvar(cls, value):
        cls._var = value
    var = property(getvar, setvar)

I can demonstrate the class methods, but they don't work as properties:

>>> f = Foo()
>>> f.getvar()
5
>>> f.setvar(4)
>>> f.getvar()
4
>>> f.var
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>>> f.var=5
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable

Is it possible to use the property() function with @classmethod decorated functions?

0

19 Answers 19

200

3.8 < Python < 3.11

Can use both decorators together. See this answer.

Python 2 and python 3 (works in 3.9-3.10 too)

A property is created on a class but affects an instance. So if you want a classmethod property, create the property on the metaclass.

>>> class foo(object):
...     _var = 5
...     class __metaclass__(type):  # Python 2 syntax for metaclasses
...         pass
...     @classmethod
...     def getvar(cls):
...         return cls._var
...     @classmethod
...     def setvar(cls, value):
...         cls._var = value
...     
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3

But since you're using a metaclass anyway, it will read better if you just move the classmethods in there.

>>> class foo(object):
...     _var = 5
...     class __metaclass__(type):  # Python 2 syntax for metaclasses
...         @property
...         def var(cls):
...             return cls._var
...         @var.setter
...         def var(cls, value):
...             cls._var = value
... 
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3

or, using Python 3's metaclass=... syntax, and the metaclass defined outside of the foo class body, and the metaclass responsible for setting the initial value of _var:

>>> class foo_meta(type):
...     def __init__(cls, *args, **kwargs):
...         cls._var = 5
...     @property
...     def var(cls):
...         return cls._var
...     @var.setter
...     def var(cls, value):
...         cls._var = value
...
>>> class foo(metaclass=foo_meta):
...     pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
12
  • 1
    This doesn't seem to work for me in Python 3.2. If I change foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func) to foo.__metaclass__.var = property(foo.getvar.__func__, foo.setvar.__func__) I get "AttributeError: type object 'foo' has no attribute 'var'" when executing "foo.var". Commented Dec 16, 2011 at 3:29
  • 2
    I am not quite sure to understand, what would be the Python 3.x way to write this then ?
    – SylvainD
    Commented Jun 24, 2014 at 7:29
  • 9
    @Josay: You'd need to define the metaclass first, then define the class using the new class Foo(metaclass=...) syntax.
    – Kevin
    Commented Dec 9, 2014 at 0:21
  • 9
    It's worth noting that these properties won't be available on instances of your class (foo in this case), just on the class itself -- by virtue of how metaclasses work.
    – nedned
    Commented May 7, 2020 at 2:07
  • 4
    @Devyzr It happens because... well, actually, it's right in the name, though it's a non-obvious consequence. A class Foo's metaclass Meta is the class of the class Foo, and Foo itself will therefore be an instance of Meta — but instances of Foo will NOT be instances of Meta. (They're instances of Foo, and Foo does not inherit from Meta, it instantiates Meta.) So, any properties Foo gains by virtue of being an instance of Meta are not passed along to its instances. If they were, Meta would be a parent class of Foo, not a meta class for Foo.
    – FeRD
    Commented Oct 5, 2021 at 9:07
165

Update: The ability to chain @classmethod and @property was removed in Python 3.13 😕.

In Python 3.9 You could use them together, but (as noted in @xgt's comment) it was deprecated in Python 3.11, so it is not longer supported (but it may work for a while or reintroduced at some point).

Check the version remarks here:

https://docs.python.org/3.11/library/functions.html#classmethod

However, it used to work like so:

class G:
    @classmethod
    @property
    def __doc__(cls):
        return f'A doc for {cls.__name__!r}'

Order matters - due to how the descriptors interact, @classmethod has to be on top.

23
  • 9
    @Pycz just stack @classmethod @property @functools.cache to get the same behavior Commented Apr 23, 2021 at 5:19
  • 8
    What about the setter?
    – Paul
    Commented Sep 30, 2021 at 0:04
  • 21
    classproperty chain was removed with Python 3.11. You cannot use @classproperty together with @property any longer: docs.python.org/3.11/library/…
    – xqt
    Commented May 29, 2022 at 9:59
  • 43
    @xqt WHHHYyyy!? The documentation says nothing about the rationale! People wrote code relying on stuff like this! What are they doing!? What's the justification? Any links to mailing list or bug tracker discussions or PEP where this was decided?
    – mtraceur
    Commented Jun 10, 2022 at 9:31
  • 27
    Looks like at least some of the rationale is here.
    – mtraceur
    Commented Jun 10, 2022 at 9:38
119

I hope this dead-simple read-only @classproperty decorator would help somebody looking for classproperties.

class classproperty(property):
    def __get__(self, owner_self, owner_cls):
        return self.fget(owner_cls)

class C(object):

    @classproperty
    def x(cls):
        return 1

assert C.x == 1
assert C().x == 1
10
  • 3
    Does this work with subclasses? (can a subclass override classproperties?)
    – zakdances
    Commented Mar 3, 2013 at 0:43
  • 2
    Umm yes? class D(C): x = 2; assert D.x == 2
    – dtheodor
    Commented Sep 30, 2014 at 20:23
  • 2
    Easily fixed: add a __set__ that raises an ValueError to prevent override. Commented Feb 24, 2018 at 16:52
  • 1
    AttributeError, and that only works to block C().x = val, not C.x = val Commented Feb 24, 2018 at 16:59
  • 1
    Thanks for this awesome snippet! I've created a pip installable package with this and a cached_classproperty here: github.com/hottwaj/classproperties
    – jcdude
    Commented Jan 22, 2021 at 11:54
76

Reading the Python 2.2 release notes, I find the following.

The get method [of a property] won't be called when the property is accessed as a class attribute (C.x) instead of as an instance attribute (C().x). If you want to override the __get__ operation for properties when used as a class attribute, you can subclass property - it is a new-style type itself - to extend its __get__ method, or you can define a descriptor type from scratch by creating a new-style class that defines __get__, __set__ and __delete__ methods.

NOTE: The below method doesn't actually work for setters, only getters.

Therefore, I believe the prescribed solution is to create a ClassProperty as a subclass of property.

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

class foo(object):
    _var=5
    def getvar(cls):
        return cls._var
    getvar=classmethod(getvar)
    def setvar(cls,value):
        cls._var=value
    setvar=classmethod(setvar)
    var=ClassProperty(getvar,setvar)

assert foo.getvar() == 5
foo.setvar(4)
assert foo.getvar() == 4
assert foo.var == 4
foo.var = 3
assert foo.var == 3

However, the setters don't actually work:

foo.var = 4
assert foo.var == foo._var # raises AssertionError

foo._var is unchanged, you've simply overwritten the property with a new value.

You can also use ClassProperty as a decorator:

class foo(object):
    _var = 5

    @ClassProperty
    @classmethod
    def var(cls):
        return cls._var

    @var.setter
    @classmethod
    def var(cls, value):
        cls._var = value

assert foo.var == 5
5
  • 21
    I don't think the setter part of the ClassProperty actually works as described: while the example's assertions all pass, at the end foo._var == 4 (not 3, as implied). Setting the property clobbers the property itself. When class-properties were discussed on python-dev it was pointed out that, while getters are trivial, setters are difficult (impossible?) without a metaclass Commented Aug 15, 2011 at 0:14
  • 4
    @Gabriel Totally correct. I can't believe no one pointed that out for two years.
    – agf
    Commented Sep 13, 2011 at 19:48
  • I'm also not sure why you don't not just use self.fget(owner) and remove the need to have to use a @classmethod at all here? (that's what classmethod does, translate .__get__(instance, owner)(*args, **kwargs) to function(owner, *args, **kwargs) calls, via an intermediary; properties don't need the intermediary). Commented Oct 19, 2018 at 11:58
  • Your demonstration is lacking any actual transformation in either the getter or the setter that would neatly demonstrate that your foo.var = 3 assignment doesn't actually go through the property, and instead has simply replaced the property object on foo with an integer. If you added assert isinstance(foo.__dict__['var'], ClassProperty) calls between your assertions you'd see that fail after foo.var = 3 is executed. Commented Oct 19, 2018 at 12:14
  • 4
    Python classes don't support descriptor binding on setting on the class itself, only on getting (so instance.attr, instance.attr = value and del instance.attr will all bind the descriptor found on type(instance), but while classobj.attr binds, classobj.attr = value and del classobj.attr do not and instead replace or delete the descriptor object itself). You need a metaclass to support setting and deleting (making the class object the instance, and the metaclass the type). Commented Oct 19, 2018 at 12:14
49

Is it possible to use the property() function with classmethod decorated functions?

No.

However, a classmethod is simply a bound method (a partial function) on a class accessible from instances of that class.

Since the instance is a function of the class and you can derive the class from the instance, you can can get whatever desired behavior you might want from a class-property with property:

class Example(object):
    _class_property = None
    @property
    def class_property(self):
        return self._class_property
    @class_property.setter
    def class_property(self, value):
        type(self)._class_property = value
    @class_property.deleter
    def class_property(self):
        del type(self)._class_property

This code can be used to test - it should pass without raising any errors:

ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')

And note that we didn't need metaclasses at all - and you don't directly access a metaclass through its classes' instances anyways.

writing a @classproperty decorator

You can actually create a classproperty decorator in just a few lines of code by subclassing property (it's implemented in C, but you can see equivalent Python here):

class classproperty(property):
    def __get__(self, obj, objtype=None):
        return super(classproperty, self).__get__(objtype)
    def __set__(self, obj, value):
        super(classproperty, self).__set__(type(obj), value)
    def __delete__(self, obj):
        super(classproperty, self).__delete__(type(obj))

Then treat the decorator as if it were a classmethod combined with property:

class Foo(object):
    _bar = 5
    @classproperty
    def bar(cls):
        """this is the bar attribute - each subclass of Foo gets its own.
        Lookups should follow the method resolution order.
        """
        return cls._bar
    @bar.setter
    def bar(cls, value):
        cls._bar = value
    @bar.deleter
    def bar(cls):
        del cls._bar

And this code should work without errors:

def main():
    f = Foo()
    print(f.bar)
    f.bar = 4
    print(f.bar)
    del f.bar
    try:
        f.bar
    except AttributeError:
        pass
    else:
        raise RuntimeError('f.bar must have worked - inconceivable!')
    help(f)  # includes the Foo.bar help.
    f.bar = 5

    class Bar(Foo):
        "a subclass of Foo, nothing more"
    help(Bar) # includes the Foo.bar help!
    b = Bar()
    b.bar = 'baz'
    print(b.bar) # prints baz
    del b.bar
    print(b.bar) # prints 5 - looked up from Foo!

    
if __name__ == '__main__':
    main()

But I'm not sure how well-advised this would be. An old mailing list article suggests it shouldn't work.

Getting the property to work on the class:

The downside of the above is that the "class property" isn't accessible from the class, because it would simply overwrite the data descriptor from the class __dict__.

However, we can override this with a property defined in the metaclass __dict__. For example:

class MetaWithFooClassProperty(type):
    @property
    def foo(cls):
        """The foo property is a function of the class -
        in this case, the trivial case of the identity function.
        """
        return cls

And then a class instance of the metaclass could have a property that accesses the class's property using the principle already demonstrated in the prior sections:

class FooClassProperty(metaclass=MetaWithFooClassProperty):
    @property
    def foo(self):
        """access the class's property"""
        return type(self).foo

And now we see both the instance

>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>

and the class

>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>

have access to the class property.

0
38

Luckily, it's easy with the metaclass kwarg:

class FooProperties(type):
    @property
    def var(cls):
        return cls._var

class Foo(object, metaclass=FooProperties):
    _var = 'FOO!'

Then,

>>> Foo.var
'FOO!'

This works in any version of Python 3, see @Amit Portnoy's answer for an even cleaner method in Python 3.9 or newer.

6
  • 2
    that is to say there is no simple way out of the box
    – mehmet
    Commented Dec 7, 2018 at 17:41
  • @mehmet Is this not simple? Foo is an instance of its metaclass, and @property can be used for its methods just as it can for those of instances of Foo.
    – OJFord
    Commented Dec 9, 2018 at 20:39
  • 4
    you had to define another class for a class, that is double the complexity assuming the metaclass is not reusable.
    – mehmet
    Commented Dec 9, 2018 at 21:44
  • A classmethod works from both the class and the instance. This property only works from the class. I don't think this is what is being asked for.
    – Aaron Hall
    Commented Feb 22, 2020 at 21:05
  • 3
    @AaronHall If that's important, it's easily added in Foo.__new__. Though at that point it might be worth either using getattribute instead, or questioning if pretending a language feature exists is really the approach you want to take at all.
    – OJFord
    Commented Feb 24, 2020 at 9:42
18

There is no reasonable way to make this "class property" system to work in Python.

Here is one unreasonable way to make it work. You can certainly make it more seamless with increasing amounts of metaclass magic.

class ClassProperty(object):
    def __init__(self, getter, setter):
        self.getter = getter
        self.setter = setter
    def __get__(self, cls, owner):
        return getattr(cls, self.getter)()
    def __set__(self, cls, value):
        getattr(cls, self.setter)(value)

class MetaFoo(type):
    var = ClassProperty('getvar', 'setvar')

class Foo(object):
    __metaclass__ = MetaFoo
    _var = 5
    @classmethod
    def getvar(cls):
        print "Getting var =", cls._var
        return cls._var
    @classmethod
    def setvar(cls, value):
        print "Setting var =", value
        cls._var = value

x = Foo.var
print "Foo.var = ", x
Foo.var = 42
x = Foo.var
print "Foo.var = ", x

The knot of the issue is that properties are what Python calls "descriptors". There is no short and easy way to explain how this sort of metaprogramming works, so I must point you to the descriptor howto.

You only ever need to understand this sort of things if you are implementing a fairly advanced framework. Like a transparent object persistence or RPC system, or a kind of domain-specific language.

However, in a comment to a previous answer, you say that you

need to modify an attribute that in such a way that is seen by all instances of a class, and in the scope from which these class methods are called does not have references to all instances of the class.

It seems to me, what you really want is an Observer design pattern.

4
  • I like the idea of the code example, but it seems like it would be a little clunky in practice.
    – Mark Roddy
    Commented Sep 24, 2008 at 21:16
  • What I'm trying to accomplish is pretty straight forward setting and getting of a single attribute that's used as a flag to modify the behavior of all instances so I think Observer would be overblown for what I'm trying to do. If there were multiple attributes in question then I'd be more inclined.
    – Mark Roddy
    Commented Sep 24, 2008 at 21:16
  • It seems that just making the functions public and calling them directly was the simplist solution. I was curious if I was doing something wrong or if I was trying to do was impossible. Sorry about the multiple comments by the way. 300 character limit sucks.
    – Mark Roddy
    Commented Sep 24, 2008 at 21:19
  • The nifty thing about the code example is that you can implement all the clunky bits once and then inherit them. Just move the _var into the derived class. class D1(Foo): _var = 21 class D2(Foo) _var = "Hello" D1.var 21 D2.var Hello Commented Dec 27, 2008 at 16:29
7

Setting it only on the meta class doesn't help if you want to access the class property via an instantiated object, in this case you need to install a normal property on the object as well (which dispatches to the class property). I think the following is a bit more clear:

#!/usr/bin/python

class classproperty(property):
    def __get__(self, obj, type_):
        return self.fget.__get__(None, type_)()

    def __set__(self, obj, value):
        cls = type(obj)
        return self.fset.__get__(None, cls)(value)

class A (object):

    _foo = 1

    @classproperty
    @classmethod
    def foo(cls):
        return cls._foo

    @foo.setter
    @classmethod
    def foo(cls, value):
        cls.foo = value

a = A()

print a.foo

b = A()

print b.foo

b.foo = 5

print a.foo

A.foo = 10

print b.foo

print A.foo
0
4

Half a solution, __set__ on the class does not work, still. The solution is a custom property class implementing both a property and a staticmethod

class ClassProperty(object):
    def __init__(self, fget, fset):
        self.fget = fget
        self.fset = fset

    def __get__(self, instance, owner):
        return self.fget()

    def __set__(self, instance, value):
        self.fset(value)

class Foo(object):
    _bar = 1
    def get_bar():
        print 'getting'
        return Foo._bar

    def set_bar(value):
        print 'setting'
        Foo._bar = value

    bar = ClassProperty(get_bar, set_bar)

f = Foo()
#__get__ works
f.bar
Foo.bar

f.bar = 2
Foo.bar = 3 #__set__ does not
3

Because I need to modify an attribute that in such a way that is seen by all instances of a class, and in the scope from which these class methods are called does not have references to all instances of the class.

Do you have access to at least one instance of the class? I can think of a way to do it then:

class MyClass (object):
    __var = None

    def _set_var (self, value):
        type (self).__var = value

    def _get_var (self):
        return self.__var

    var = property (_get_var, _set_var)

a = MyClass ()
b = MyClass ()
a.var = "foo"
print b.var
3

Give this a try, it gets the job done without having to change/add a lot of existing code.

>>> class foo(object):
...     _var = 5
...     def getvar(cls):
...         return cls._var
...     getvar = classmethod(getvar)
...     def setvar(cls, value):
...         cls._var = value
...     setvar = classmethod(setvar)
...     var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val))
...
>>> f = foo()
>>> f.var
5
>>> f.var = 3
>>> f.var
3

The property function needs two callable arguments. give them lambda wrappers (which it passes the instance as its first argument) and all is well.

1
  • As Florian Bösch points out, the syntax required (by third-party libraries or legacy code) is foo.var. Commented Dec 27, 2008 at 16:14
3

For a functional approach pre Python 3.9 you can use this:

def classproperty(fget):
  return type(
      'classproperty',
      (),
      {'__get__': lambda self, _, cls: fget(cls), '__module__': None}
  )()
  
class Item:
  a = 47

  @classproperty
  def x(cls):
    return cls.a

Item.x
2

Here's a solution which should work for both access via the class and access via an instance which uses a metaclass.

In [1]: class ClassPropertyMeta(type):
   ...:     @property
   ...:     def prop(cls):
   ...:         return cls._prop
   ...:     def __new__(cls, name, parents, dct):
   ...:         # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
   ...:         dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
   ...:         dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
   ...:         return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
   ...:

In [2]: class ClassProperty(object):
   ...:     __metaclass__ = ClassPropertyMeta
   ...:     _prop = 42
   ...:     def __getattr__(self, attr):
   ...:         raise Exception('Never gets called')
   ...:

In [3]: ClassProperty.prop
Out[3]: 42

In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1

AttributeError: can't set attribute

In [5]: cp = ClassProperty()

In [6]: cp.prop
Out[6]: 42

In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1

<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
      6         # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
      7         dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8         dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
      9         return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)

AttributeError: can't set attribute

This also works with a setter defined in the metaclass.

2

I found one clean solution to this problem. It's a package called classutilities (pip install classutilities), see the documentation here on PyPi.

Consider example:

import classutilities

class SomeClass(classutilities.ClassPropertiesMixin):
    _some_variable = 8  # Some encapsulated class variable

    @classutilities.classproperty
    def some_variable(cls):  # class property getter
        return cls._some_variable

    @some_variable.setter
    def some_variable(cls, value):  # class property setter
        cls._some_variable = value

You can use it on both class level and instance level:

# Getter on class level:
value = SomeClass.some_variable
print(value)  # >>> 8
# Getter on instance level
inst = SomeClass()
value = inst.some_variable
print(value)  # >>> 8

# Setter on class level:
new_value = 9
SomeClass.some_variable = new_value
print(SomeClass.some_variable)   # >>> 9
print(SomeClass._some_variable)  # >>> 9
# Setter on instance level
inst = SomeClass()
inst.some_variable = new_value
print(SomeClass.some_variable)   # >>> 9
print(SomeClass._some_variable)  # >>> 9
print(inst.some_variable)        # >>> 9
print(inst._some_variable)       # >>> 9

As you can see, it works correctly under all circumstances.

1
  • I tried a few other answers and this is what I've gone with as it seems to be best. It also keeps type checks and pylint happy if you put the @classmethod decorator under both @classproperty and .setter.
    – Peter
    Commented May 16, 2024 at 9:12
1

Based on https://stackoverflow.com/a/1800999/2290820:

class MetaProperty(type):

    def __init__(cls, *args, **kwargs):
        super()

    @property
    def praparty(cls):
        return cls._var

    @praparty.setter
    def praparty(cls, val):
        cls._var = val


class A(metaclass=MetaProperty):
    _var = 5


print(A.praparty)
A.praparty = 6
print(A.praparty)
0

If you need the type hints, here's the answer (works with pyright):

from multiprocessing import RLock
from typing import TypeVar, Callable, Generic, Type

GetterReturnType = TypeVar("GetterReturnType")


class classproperty(Generic[GetterReturnType]):
    def __init__(self, func: Callable[..., GetterReturnType]):
        if isinstance(func, (classmethod, staticmethod)):
            fget = func
        else:
            fget = classmethod(func)
        self.fget = fget

    def __get__(self, obj, klass=None) -> GetterReturnType:
        if klass is None:
            klass = type(obj)
        return self.fget.__get__(obj, klass)()

If you need cached_classproperty (refer to the implementation of cached_property):

_NOT_FOUND = object()


class cached_classproperty(Generic[GetterReturnType], classproperty[GetterReturnType]):
    def __init__(self, func: Callable[..., GetterReturnType]):
        super().__init__(func)
        self.lock = RLock()

    def __set_name__(self, owner, name):
        self.attrname = f"__cached_classproperty_{name}"

    def __get__(self, obj, klass=None) -> GetterReturnType:
        val = getattr(klass, self.attrname, _NOT_FOUND)
        if val is _NOT_FOUND:
            with self.lock:
                # check if another thread filled cache while we awaited lock
                val = getattr(klass, self.attrname, _NOT_FOUND)
                if val is _NOT_FOUND:
                    val = super().__get__(obj, klass)
                    setattr(klass, self.attrname, val)

        return val  # pyright: ignore[reportReturnType]

enter image description here

0

I found a method to define a classproperty valid with Python 2 and 3.

from future.utils import with_metaclass

class BuilderMetaClass(type):
    @property
    def load_namespaces(self):
        return (self.__sourcepath__)
            
class BuilderMixin(with_metaclass(BuilderMetaClass, object)):
    __sourcepath__ = 'sp'        

print(BuilderMixin.load_namespaces)
1
-1

Here is my solution that also caches the class property

class class_property(object):
    # this caches the result of the function call for fn with cls input
    # use this as a decorator on function methods that you want converted
    # into cached properties

    def __init__(self, fn):
        self._fn_name = fn.__name__
        if not isinstance(fn, (classmethod, staticmethod)):
            fn = classmethod(fn)
        self._fn = fn

    def __get__(self, obj, cls=None):
        if cls is None:
            cls = type(obj)
        if (
            self._fn_name in vars(cls) and
            type(vars(cls)[self._fn_name]).__name__ != "class_property"
        ):
            return vars(cls)[self._fn_name]
        else:
            value = self._fn.__get__(obj, cls)()
            setattr(cls, self._fn_name, value)
            return value
-49

Here's my suggestion. Don't use class methods.

Seriously.

What's the reason for using class methods in this case? Why not have an ordinary object of an ordinary class?


If you simply want to change the value, a property isn't really very helpful is it? Just set the attribute value and be done with it.

A property should only be used if there's something to conceal -- something that might change in a future implementation.

Maybe your example is way stripped down, and there is some hellish calculation you've left off. But it doesn't look like the property adds significant value.

The Java-influenced "privacy" techniques (in Python, attribute names that begin with _) aren't really very helpful. Private from whom? The point of private is a little nebulous when you have the source (as you do in Python.)

The Java-influenced EJB-style getters and setters (often done as properties in Python) are there to facilitate Java's primitive introspection as well as to pass muster with the static language compiler. All those getters and setters aren't as helpful in Python.

1
  • 21
    Because I need to modify an attribute that in such a way that is seen by all instances of a class, and in the scope from which these class methods are called does not have references to all instances of the class.
    – Mark Roddy
    Commented Sep 24, 2008 at 17:50

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.