Here's how I would do this:

class ClassPropertyDescriptor(object):

    def __init__(self, fget, fset=None):
        self.fget = fget
        self.fset = fset

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

    def __set__(self, obj, value):
        if not self.fset:
            raise AttributeError("can't set attribute")
        type_ = type(obj)
        return self.fset.__get__(obj, type_)(value)

    def setter(self, func):
        if not isinstance(func, (classmethod, staticmethod)):
            func = classmethod(func)
        self.fset = func
        return self

def classproperty(func):
    if not isinstance(func, (classmethod, staticmethod)):
        func = classmethod(func)

    return ClassPropertyDescriptor(func)


class Bar(object):

    _bar = 1

    @classproperty
    def bar(cls):
        return cls._bar

    @bar.setter
    def bar(cls, value):
        cls._bar = value


# test instance instantiation
foo = Bar()
assert foo.bar == 1

baz = Bar()
assert baz.bar == 1

# test static variable
baz.bar = 5
assert foo.bar == 5

# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50

The setter didn't work at the time we call Bar.bar, because we are calling TypeOfBar.bar.__set__, which is not Bar.bar.__set__.

Adding a metaclass definition solves this:

class ClassPropertyMetaClass(type):
    def __setattr__(self, key, value):
        if key in self.__dict__:
            obj = self.__dict__.get(key)
        if obj and type(obj) is ClassPropertyDescriptor:
            return obj.__set__(self, value)

        return super(ClassPropertyMetaClass, self).__setattr__(key, value)

# and update class define:
#     class Bar(object):
#        __metaclass__ = ClassPropertyMetaClass
#        _bar = 1

# and update ClassPropertyDescriptor.__set__
#    def __set__(self, obj, value):
#       if not self.fset:
#           raise AttributeError("can't set attribute")
#       if inspect.isclass(obj):
#           type_ = obj
#           obj = None
#       else:
#           type_ = type(obj)
#       return self.fset.__get__(obj, type_)(value)

Now all will be fine.

Answer from Mahmoud Abdelkader on Stack Overflow
Top answer
1 of 9
138

Here's how I would do this:

class ClassPropertyDescriptor(object):

    def __init__(self, fget, fset=None):
        self.fget = fget
        self.fset = fset

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

    def __set__(self, obj, value):
        if not self.fset:
            raise AttributeError("can't set attribute")
        type_ = type(obj)
        return self.fset.__get__(obj, type_)(value)

    def setter(self, func):
        if not isinstance(func, (classmethod, staticmethod)):
            func = classmethod(func)
        self.fset = func
        return self

def classproperty(func):
    if not isinstance(func, (classmethod, staticmethod)):
        func = classmethod(func)

    return ClassPropertyDescriptor(func)


class Bar(object):

    _bar = 1

    @classproperty
    def bar(cls):
        return cls._bar

    @bar.setter
    def bar(cls, value):
        cls._bar = value


# test instance instantiation
foo = Bar()
assert foo.bar == 1

baz = Bar()
assert baz.bar == 1

# test static variable
baz.bar = 5
assert foo.bar == 5

# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50

The setter didn't work at the time we call Bar.bar, because we are calling TypeOfBar.bar.__set__, which is not Bar.bar.__set__.

Adding a metaclass definition solves this:

class ClassPropertyMetaClass(type):
    def __setattr__(self, key, value):
        if key in self.__dict__:
            obj = self.__dict__.get(key)
        if obj and type(obj) is ClassPropertyDescriptor:
            return obj.__set__(self, value)

        return super(ClassPropertyMetaClass, self).__setattr__(key, value)

# and update class define:
#     class Bar(object):
#        __metaclass__ = ClassPropertyMetaClass
#        _bar = 1

# and update ClassPropertyDescriptor.__set__
#    def __set__(self, obj, value):
#       if not self.fset:
#           raise AttributeError("can't set attribute")
#       if inspect.isclass(obj):
#           type_ = obj
#           obj = None
#       else:
#           type_ = type(obj)
#       return self.fset.__get__(obj, type_)(value)

Now all will be fine.

2 of 9
73

If you define classproperty as follows, then your example works exactly as you requested.

class classproperty(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, obj, owner):
        return self.f(owner)

The caveat is that you can't use this for writable properties. While e.I = 20 will raise an AttributeError, Example.I = 20 will overwrite the property object itself.

🌐
W3Schools
w3schools.com › python › python_class_properties.asp
Python Class Properties
Properties are variables that belong to a class. They store data for each object created from the class.
🌐
Reddit
reddit.com › r/learnpython › when to use a property (rather than a method) in a class?
r/learnpython on Reddit: When to use a property (rather than a method) in a class?
December 15, 2023 -

Suppose I had the class `vehicle` which represents a motor vehicle. Suppose the horsepower of the vehicle was not passed as an inputs but, with some detailed calculation, could be calculated from the other properties of the vehicle class. Would it be better to add `horsepower` as a property of the `vehicle` class, or as a method?

As a property, this might look something like this:

class Vehicle:

    def __init__(self, args):
        # Set args
        self._horsepower = None
    
    @property
    def horsepower(self):
        if self._horsepower is None:
            self._horsepower = calculate_horsepower()
        return self._horsepower

As a method, it may look like this:

class Vehicle:

    def __init__(self, args):
        # Set args

    def calculate_horsepower(self):
        # Calculate horsepower of instance vehicle

Which of the above is preferable?

In reality, horsepower is a property of a vehicle. However, if significant processing is required to calculate it then I'm not sure if it feels right to have it as a property of the `vehicle` class.

🌐
Python documentation
docs.python.org › 3 › tutorial › classes.html
9. Classes — Python 3.14.8 documentation
1 week ago - Python classes provide all the standard features of Object Oriented Programming: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method ...
🌐
The Python Coding Stack
thepythoncodingstack.com › the python coding stack › the properties of python's `property`
The Properties of Python's `property`
July 15, 2025 - To do this, we can change .athlete_id from a data attribute into a property: ... We renamed the data attribute defined in the .__init__() method to ._athlete_id, with a leading underscore. The leading underscore identifies this attribute as non-public. It's not really a private attribute that cannot be accessed from outside the class – Python doesn't have private attributes – but it clearly shows the programmer's intent to any user of this class: this attribute is not meant to be accessed from outside the class.
🌐
YouTube
youtube.com › watch
Learn Python @property in 7 minutes! ⚙️ - YouTube
# @property = Decorator used to define a method as a property (it can be accessed like an attribute)# Benefit:...
Published: June 15, 2024
Find elsewhere
🌐
Medium
elfi-y.medium.com › python-properties-and-class-methods-a6c7ad69b0f1
Python Properties and Class Methods | by E.Y. | Medium
January 10, 2021 - Simply put, the property() method provides an interface to instance attributes, aka, getter , setter and deleter . ... Well, it is common to allow dynamically setting and getting an instance attribute when you build a class, e.g.
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-property-function
Python property() function - GeeksforGeeks
July 11, 2025 - property() function in Python is a built-in function that returns an object of the property class. It allows developers to create properties within a class, providing a way to control access to an attribute by defining getter, setter and deleter ...
🌐
Real Python
realpython.com › python-property
Python's property(): Add Managed Attributes to Your Classes – Real Python
April 17, 2026 - The @property decorator simplifies the management of attributes in your Python classes. It allows you to control attribute access, enabling features such as data validation, lazy evaluation, and the creation of backward-compatible APIs without modifying the class’s public interface.
🌐
Reddit
reddit.com › r/learnpython › what is the purpose of @property ?
r/learnpython on Reddit: What is the purpose of @property ?
November 17, 2017 -

Hi guys, I'm new to Python and I was curious if I define a class, what is the fuss with getattr, setattr and @property? I've read couple topics on stackexchange, but neither did motivate why I should use them. I mean I can define own functions to set something, and just use car.wheels to get the amount of wheels. No need to define an extra function car.getattr(wheels). What am I missing.

Top answer
1 of 5
21
Properties solve several problems. The main one is that they allow you to substitute a method call for an attribute access without changing the public API. That is important if you're dealing with large software projects where you can't break backwards compatibility. The method can contain arbitrary logic. For example, suppose that you wrote a class that stores a temperature. You initially wrote it to store temperate in Fahrenheit as a simple attribute, and you have lots of existing code that depends on that. But at some point you decide that you want it internally stored as Celsius. By using a computed property, you can change the internal representation to Celsius, while pretending to external users that it's still stored as Fahrenheit, by writing a getter method that dynamically computes the conversion. This is intentionally silly, but you can imagine more complex examples where you want to change the internal representation or other internal implementation details without affecting the public API. Another problem it solves is of validation. With a simple attribute, users can set any value. Again going back to the temperature example, someone might set the attribute to an invalid temperature. By using a setter of a property, you can intercept that attribute access and execute arbitrary logic to perform validation, raising an exception if they do something wrong. Arguably, the getting and setting in this case should have been written as methods in the first place, rather than attributes, but again you don't always have that kind of foresight, and often design decisions change and you need to make the change without breaking the API. Properties allow that.
2 of 5
2
Since a good explanation is given for properties, perhaps I can tackle getattr (and setattr by extension). Let's use your example - you have an object car with the attribute wheels. There are two ways to access this: car.wheels getattr(car, 'wheels') So why would you use the second form? Well, look at the second parameter - wheels is a string. Because the second parameter is a string, you can get attributes dynamically from an object. Imagine you wanted to have user input to allow the user to grab an attribute from car and print out that attribute. (A contrived example, I know.) You could do this: attribute = input("Type car attribute") print(getattr(car, attribute)) If the user types in wheels, then this script prints out car.wheels. But if the user types in doors, then this script prints out car.doors. I hope you can see - because we are using a string to access the object's attribute, you can do more dynamic things with this functionality.
Top answer
1 of 15
1350

The property() function returns a special descriptor object:

>>> property()
<property object at 0x10ff07940>

It is this object that has extra methods:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

These act as decorators too. They return a new property object:

>>> property().getter(None)
<property object at 0x10ff079f0>

that is a copy of the old object, but with one of the functions replaced.

Remember, that the @decorator syntax is just syntactic sugar; the syntax:

@property
def foo(self): return self._foo

really means the same thing as

def foo(self): return self._foo
foo = property(foo)

so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use @foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.

The following sequence also creates a full-on property, by using those decorator methods.

First we create some functions:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 

Then, we create a property object with only a getter:

>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Next we use the .setter() method to add a setter:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Last we add a deleter with the .deleter() method:

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

The Descriptor Howto includes a pure Python sample implementation of the property() type:

class Property:
    "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__)
2 of 15
403

The documentation says it's just a shortcut for creating read-only properties. So

@property
def x(self):
    return self._x

is equivalent to

def getx(self):
    return self._x
x = property(getx)
🌐
Python Basics
python-basics-tutorial.readthedocs.io › en › latest › oop › property.html
@property decorator - Python Basics
1 month ago - In Python, you can access instance variables directly, without additional getter and setter methods that are often used in Java and other object-oriented languages. This makes writing Python classe...
🌐
Mimo
mimo.org › glossary › python › property
Python property(): Syntax, Usage, and Examples
A Python property is a special kind of attribute that lets you run code whenever it is accessed, set, or deleted. It allows you to expose what looks like a simple attribute to the user, while hiding the internal logic for getting or setting its value (getters and setters).
🌐
Programiz
programiz.com › python-programming › property
Python @property Decorator (With Examples)
Note: The actual temperature value is stored in the private _temperature variable. The temperature attribute is a property object which provides an interface to this private variable. In Python, property() is a built-in function that creates and returns a property object.
🌐
GeeksforGeeks
geeksforgeeks.org › python › difference-between-attributes-and-properties-in-python
Difference between attributes and properties in Python - GeeksforGeeks
July 15, 2025 - We can apply the property function by using @property decorator. This is one of the built-in decorators. A decorator is simply a function that takes another function as an argument and adding to its behavior by wrapping it. ... # create a class class byDeco: # constructor def __init__(self, value): self._value = value # getting the values @property def value(self): print('Getting value') return self._value # setting the values @value.setter def value(self, value): print('Setting value to ' + value) self._value = value # deleting the values @value.deleter def value(self): print('Deleting value') del self._value # create an object of class x = byDeco('happy Coding') print(x.value) x.value = 'Hey Coder!'
🌐
Medium
serdaralkancode.medium.com › various-styles-for-managing-or-accessing-your-attributes-properties-of-your-class-in-python-6d76d9b85af9
Various styles for managing or accessing your attributes/properties of your class in Python | by Serdar ALKAN | Medium
February 23, 2025 - If you remove setter methods or throw exception for setter method, only use @property methods. You make object read-only ... class Point: def __init__(self, x, y): self._x = x self._y = y @property def x(self): """The x property.""" print("Get x") return self._x @property def y(self): """The y property.""" print("Get y") return self._y
🌐
Toptal
toptal.com › developers › python › python-class-attributes-an-overly-thorough-guide
Python Class Attributes: An Overly Thorough Guide | Toptal®
January 16, 2026 - A Python namespace is a mapping from names to objects, with the property that there is zero relation between names in different namespaces. Namespaces are usually implemented as Python dictionaries, although this is abstracted away. In Python, a class method is a method that is invoked with the class as the context.
🌐
Medium
gokulapriyan.medium.com › understanding-python-class-components-attributes-methods-and-properties-explained-1b83402098ed
🔍 Understanding Python Class Components: Attributes, Methods, and Properties Explained” | by Gokulapriyan | Medium
September 26, 2024 - Properties are defined using the @property decorator and allow you to expose an instance method like an attribute. ... class Person: def __init__(self, name): self._name = name @property def name(self): # Getter method, allows access to _name ...
🌐
W3Schools
w3schools.com › python › python_classes.asp
Python Classes
Almost everything in Python is an object, with its properties and methods. A Class is like an object constructor, or a "blueprint" for creating objects.
🌐
Python
peps.python.org › pep-0008
PEP 8 – Style Guide for Python Code | peps.python.org
Note 1: See the argument name recommendation above for class methods. For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.