Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a read-only class property in Python? [duplicate]

Essentially I want to do something like this:

class foo:     x = 4     @property     @classmethod     def number(cls):         return x 

Then I would like the following to work:

>>> foo.number 4 

Unfortunately, the above doesn't work. Instead of given me 4 it gives me <property object at 0x101786c58>. Is there any way to achieve the above?

like image 730
Björn Pollex Avatar asked Jul 08 '10 12:07

Björn Pollex


People also ask

How do you make a Python property read-only?

If you need to make a read-only attribute in Python, you can turn your attribute into a property that delegates to an attribute with almost the same name, but with an underscore prefixed before the its name to note that it's private convention.

What is a read-only property in Python?

To define a readonly property, you need to create a property with only the getter. However, it is not truly read-only because you can always access the underlying attribute and change it. The read-only properties are useful in some cases such as for computed properties.

How do you create a class property in Python?

Python property() function returns the object of the property class and it is used to create property of a class. Parameters: fget() – used to get the value of attribute. fset() – used to set the value of attribute.

What is @property in Python class?

The @property Decorator In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is: property(fget=None, fset=None, fdel=None, doc=None)


2 Answers

This will make Foo.number a read-only property:

class MetaFoo(type):     @property     def number(cls):         return cls.x  class Foo(object, metaclass=MetaFoo):     x = 4  print(Foo.number) # 4  Foo.number = 6 # AttributeError: can't set attribute 

Explanation: The usual scenario when using @property looks like this:

class Foo(object):     @property     def number(self):         ... foo = Foo() 

A property defined in Foo is read-only with respect to its instances. That is, foo.number = 6 would raise an AttributeError.

Analogously, if you want Foo.number to raise an AttributeError you would need to setup a property defined in type(Foo). Hence the need for a metaclass.


Note that this read-onlyness is not immune from hackers. The property can be made writable by changing Foo's class:

class Base(type): pass Foo.__class__ = Base  # makes Foo.number a normal class attribute Foo.number = 6    print(Foo.number) 

prints

6 

or, if you wish to make Foo.number a settable property,

class WritableMetaFoo(type):      @property     def number(cls):         return cls.x     @number.setter     def number(cls, value):         cls.x = value Foo.__class__ = WritableMetaFoo  # Now the assignment modifies `Foo.x` Foo.number = 6    print(Foo.number) 

also prints

6 
like image 139
unutbu Avatar answered Sep 24 '22 19:09

unutbu


The property descriptor always returns itself when accessed from a class (ie. when instance is None in its __get__ method).

If that's not what you want, you can write a new descriptor that always uses the class object (owner) instead of the instance:

>>> class classproperty(object): ...     def __init__(self, getter): ...         self.getter= getter ...     def __get__(self, instance, owner): ...         return self.getter(owner) ...  >>> class Foo(object): ...     x= 4 ...     @classproperty ...     def number(cls): ...         return cls.x ...  >>> Foo().number 4 >>> Foo.number 4 
like image 33
bobince Avatar answered Sep 24 '22 19:09

bobince