I know that functions are just descriptors, like this:
def func(self):
print(self.name)
class C:
def __init__(self, name):
self.name = name
C.func = func
c = C("foo")
c.func()
I thought at first that c.func equals C.func.__get__(c),yes,C.func.__get__(c) return a bound method. But when I set the __get__ of func to None, c.func still returns a bound method.
def func(self):
print(self.name)
class C:
def __init__(self, name):
self.name = name
func.__get__ = None
C.func = func
c = C("foo")
c.func
output:
<bound method func of <__main__.C object at 0x0000027EB23BF088>>
So I'm confused. Moreover, I found that when calling a function from an instance, Python actually calls the class's ___getAttribute__ method, which returns a bound method.
def func(self):
print(self.name)
func.__get__ = None
class C:
def __getattribute__(self, name):
r = super().__getattribute__(name)
print(r) # r is a bound method already
return r
def __init__(self, name):
self.name = name
C.func = func
c = C("foo")
c.func
output:
<bound method func of <__main__.C object at 0x0000027EB243D1C8>>
func.__get__ doesn't seem to have any effect. So, What happended in __getattribute__? How does Python turn a function into a method? I've Googled and done some research, but I still can't find the answer.
Maybe I'm making things complicated, In my understanding, function is itself a descriptor, but just like the code below, I set the func to None, it works normally:
class C:
def func(self):
print('hello world')
func.__get__ = None
c = C()
c.func()
but if it's a descriptor, it will raise TypeError:
class C:
class D:
def __get__(self, inst, cls):
if inst is None:
return self
return 'hello world'
D.__get__ = None
func = D()
c = C()
c.func
Well, if I understand correctly from what I found. (Since I didn't know the descriptors, that's exactly why I like to help, still learning)
First, let's look at __getattr__ and __getattribute__.
Let's have an empty class A
class A:
pass
If I initialize an object and try to call a property, because there is none at the moment, we get AttributeError.
a = A()
a.some_property
The following occurs:
Simple check of flow:
class FlowDemo:
def __init__(self):
self.inited_property = True
def __getattribute__(self, item):
if item in ('__class__', '__len__') : # For less spam of getting this attribute, if you want, you can remove condition.
print('Get Attribute', item)
# Call default behavior
return super().__getattribute__(item)
def __getattr__(self, item):
print('Get Attr', item)
if item == 'some_magic_name':
return "It's magic!"
raise AttributeError
fd = FlowDemo()
fd.inited_property
# Get Attribute inited_property
# True
fd.some_magic_property
# Get Attribute some_magic_name
# Get Attr some_magic_name
# "It's magic!"
fd.some_property
# Get Attribute some_property
# Get Attr some_property
# Traceback (most recent call last):
# File "<input>", line 1, in <module>
# File "stack-class-property-and-descriptors.py", line 67, in # __getattr__
# raise AttributeError
# AttributeError
This is probably understandable, including the use. But to be sure, I'll give an example. This logic is used as a dynamic representation of the result from the databases (mapping of attributes to ordinary dict, list, etc.).
But it can also be just logic for accessing an attribute (property), such as an access counter or validation (but this applies to __setattr__ and __setattribute__)
And what about descriptors?
First let's look at data-descriptors, they are easier for me to understand.
This is a class or decoder that has __get__ and one or both of __set__ and __delete__.
Once this is defined, python, when used in the property definition with it and then does not return a class but the value it obtains through __get__, does not overwrite an already declared class when declaring a value, but uses its __set__.
Example:
class WeekDayDescriptor:
def __init__(self):
self.__week_day = 0
def __get__(self, instance, owner=None):
return self.__week_day
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Value must be int')
if not (0 <= value < 6):
raise ValueError('Value must be in range 0 - 6')
self.__week_day = value
class Calendar:
week_day = WeekDayDescriptor()
def __init__(self, week_day):
self.week_day = week_day
Demo:
c = Calendar(9)
# ValueError: Value must be in range 0-6
c = Calendar('6')
# TypeError: Value must be int
c = Calendar(3)
c.week_day = 6
c.week_day = 10
# ValueError: Value must be in range 0-6
c.week_day = 'monday'
# TypeError: Value must be int
Decorator descriptor:
class Calendar:
#property
def week_day(self):
return self.__week_day
#week_day.setter
def week_day(self, week_day):
if not isinstance(week_day, int):
raise TypeError('Value must be int')
if not (0 <= week_day < 6):
raise ValueError('Value must be in range 0 - 6')
self.__week_day = week_day
def __init__(self, week_day):
self.week_day = week_day
pass
And now for non-data descriptors...
A non-data descriptor is one that has only __get__.
As I understand it, each method automatically has its own descriptor, thanks to which the functions get references to the object - self.
We can write our own descriptor for a function / method, but it's not that straightforward, we have to help ourselves and get around it a bit.
def function_as_method(self, value):
print(self, value)
class HelperDescriptor:
def __get__(self, instance, owner):
def wrapper(*args, **kwargs):
return function_as_method(instance, *args, **kwargs)
return wrapper
class Foo:
baz = HelperDescriptor()
>>> bar = Foo()
>>> bar.baz(1)
<__main__.Foo object at 0x7f64f7768b70> 1
Source of last code block, but in czech lang.
And finally, your mentioned problem, when we set __get__ to None and you still get a reference to the function.
It's simple, python doesn't directly distinguish between primitive data types and functions, it's all a variable (or attribute / property) that has a value. Whether it's just value or it's callable is a different matter.
def f(): return True
print(type(f), f())
# <class 'function'> True
f = 123
print(type(f), f)
# <class 'int'> 123
Therefore, when we ask for the obj.func method or call it obj.func() directly, the first two changed magic is called first - __getattribute__ and __getattr__.
And if we call a method, it is called only after we get a reference to a function in memory.
Again a simple example:
def func(self, value):
print('Printing:', value)
class PrintDescriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
def wrapper(*args, **kwargs):
print(f"Calling '{self.name}' method")
return func(instance, *args, **kwargs)
return wrapper
class B:
foo = PrintDescriptor('foo')
bar = PrintDescriptor('bar')
def __getattribute__(self, item):
if item not in ('__len__', '__class__', '__dict__'):
print('Get Attribute', item)
return super().__getattribute__(item)
Demo:
b = B()
b.foo
# Get Attribute foo
# <function PrintDescriptor.__get__.<locals>.wrapper at 0x7f774a782ee0>
b.foo(2)
# Get Attribute foo
# Calling 'foo' method
# Printing: 2
b.bar(4)
# Get Attribute bar
# Calling 'bar' method
# Printing: 4
Sources:
https://www.datacamp.com/community/tutorials/python-descriptors#above1
https://blog.milde.cz/post/319-pokrocile-techniky-v-pythonu-deskriptory/
Python Doc, __get__
Python Docs, __getattribute__
Python Docs, __getattr__
I am trying to implement a metaclass that initializes class variables when a first its instance is being created. I want to keep a new magic method __load__ that should be called as a classmethod (like __new__). So I implemented it like this:
class StaticLoad(type):
__loaded_classes = set()
def __call__(cls, *args, **kwargs):
if cls not in cls.__loaded_classes:
if hasattr(cls, '__load__'):
cls.__load__()
cls.__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
class BaseClass(metaclass=StaticLoad):
s = 0
class MyClass(BaseClass):
#classmethod
def __load__(cls):
print("Loading", cls.__name__, "...")
cls.s += 1
obj1 = MyClass()
obj2 = MyClass()
print(MyClass.s)
It works fine and gives the correct result:
Loading MyClass ...
1
Now I want to implement the method __load__ as a classmethod by default like __new__ (without the need to type #classmethod above each time). I tried this:
class StaticLoad(type):
__loaded_classes = set()
def __call__(cls, *args, **kwargs):
if cls not in cls.__loaded_classes:
if hasattr(cls, '__load__'):
# I try to apply classmethod routine to make
# cls.__load__ a classmethod
classmethod(cls.__load__)()
cls.__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
class BaseClass(metaclass=StaticLoad):
s = 0
class MyClass(BaseClass):
# #classmethod line was deleted
def __load__(cls):
print("Loading", cls.__name__, "...")
cls.s += 1
obj1 = MyClass()
obj2 = MyClass()
print(MyClass.s)
I got the error:
Traceback (most recent call last):
File "example.py", line 22, in <module>
obj1 = MyClass()
File "example.py", line 7, in __call__
classmethod(cls.__load__)()
TypeError: 'classmethod' object is not callable
It looks like classmethod routine is correctly available only inside a class definition.
How should I improve my metaclass to make it work fine? I would like to keep the content of classes BaseClass and MyClass as I wrote above, placing all magic into StaticLoad.
With the help of #AnttiHaapala the solution is simple. Instead of calling
classmethod(cls.__load__)()
I had to call
cls.__load__(cls)
If you want to perform transforms on the certain methods and attributes of a class creation, you do that on the metaclass' __new__ function.
Since yu already have a metaclass, all you have to do is to implement its __new__ method to convert any __load__ methods in a classmethod:
class StaticLoad(type):
__loaded_classes = set()
def __new__(metacls, name, bases, namespace):
if "__load__" in namespace and not isinstance(namespace["__load__"], classmethod):
namespace["__load__"] = classmethod(namespace["load"])
return super().__new__(metacls, name, bases, namespace)
def __call__(cls, *args, **kwargs):
if cls not in cls.__class__.__loaded_classes:
if hasattr(cls, '__load__'):
cls.__load__()
type(cls).__loaded_classes.add(cls)
return super().__call__(*args, **kwargs)
(the other change I made was to make explict that "__loaded_classes" should be accessed on the metaclass, not on the class itself).
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?
3.8 < Python < 3.11
Can use both decorators together. See this answer.
Python < 3.9
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
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 recommended to use it.
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.
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
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
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.
Python 3!
See #Amit Portnoy's answer for an even cleaner method in python >= 3.9
Old question, lots of views, sorely in need of a one-true Python 3 way.
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!'
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.
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
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
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
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.
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.
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.
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)
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
After searching different places, 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)
Hope this can help somebody :)
A code completion friendly solution for Python < 3.9
from typing import (
Callable,
Generic,
TypeVar,
)
T = TypeVar('T')
class classproperty(Generic[T]):
"""Converts a method to a class property.
"""
def __init__(self, f: Callable[..., T]):
self.fget = f
def __get__(self, instance, owner) -> T:
return self.fget(owner)
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
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.
I'm trying to create a class which must be superclass of others, tracing their attribute requests. I thought of using "getattribute" which gets all attribute requests, but it generates recursion:
class Mixin(object):
def __getattribute__ (self, attr):
print self, "getting", attr
return self.__dict__[attr]
I know why I get recursion: it's for the self.dict call which recalls getattribute recursively. I've tryied to change last line in "return object.__getattribute__(self,attr)" like suggested in other posts but recursion is recalled.
Try this:
class Mixin(object):
def __getattribute__ (self, attr):
print self, "getting", attr
return object.__getattribute__(self, attr)
If you are still getting recursion problems, it is caused by code you haven't shown us
>>> class Mixin(object):
... def __getattribute__ (self, attr):
... print self, "getting", attr
... return object.__getattribute__(self, attr)
...
>>> Mixin().__str__
<__main__.Mixin object at 0x00B47870> getting __str__
<method-wrapper '__str__' of Mixin object at 0x00B47870>
>>> Mixin().foobar
<__main__.Mixin object at 0x00B47670> getting foobar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getattribute__
AttributeError: 'Mixin' object has no attribute 'foobar'
>>>
And here is the result when combined with Bob's Mylist
>>> class Mylist(Mixin):
... def __init__ (self, lista):
... if not type (lista) == type (""):
... self.value = lista[:]
... def __add__ (self,some):
... return self.value + some
... def __getitem__ (self,item):
... return self.value[item]
... def __getslice__ (self, beg, end):
... return self.value[beg:end]
...
>>> a=Mylist([1,2])
>>> a.value
<__main__.Mylist object at 0x00B47A90> getting value
[1, 2]
This is the code:
from Es123 import Mixin
class Mylist(Mixin):
def __init__ (self, lista):
if not type (lista) == type (""):
self.value = lista[:]
def __add__ (self,some):
return self.value + some
def __getitem__ (self,item):
return self.value[item]
def __getslice__ (self, beg, end):
return self.value[beg:end]
a = Mylist ([1,2])
a.value
Then python returns "RuntimeError: maximum recursion depth exceeded"