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__)
Answer from Martijn Pieters on Stack Overflow
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)
🌐
freeCodeCamp
freecodecamp.org › news › python-property-decorator
The @property Decorator in Python: Its Use Cases, Advantages, and Syntax
December 19, 2019 - The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.
Discussions

Usage of property decorator for private attributes in classes in Python - Software Engineering Stack Exchange
I've started learning Python recently, and there are some topics I cannot really understand. One is the usage of the decorators in user defined objects, or encapsulation more generally. I mean, let's More on softwareengineering.stackexchange.com
🌐 softwareengineering.stackexchange.com
January 7, 2022
What is the @property decorator meant to be used for?
As people have stated the @property decorator makes a method accessible as if it was an attribute. While this can be used for validation it seems to be more commonly used for backwards compatibility. You may write a class that uses attributes and then later need to turn that code into a function, to do validation, or do some computation. The problem is you and anyone using your code may have Class.mything = 'something' throughout their code. If you try to change mything to a method that code is going to break. This is one of the reasons why getters and setters are used in other languages. In python however the @property decorator lets us replace the attribute with a method without changing how it is accessed externally. More on reddit.com
🌐 r/learnpython
5
27
July 27, 2021
python global decorator for class attributes - Stack Overflow
Below is an abstracted piece of code that simplifies an issue that I have. In this example, I have a program that has a login and logout attributes. The login is version-independent and logout is v... More on stackoverflow.com
🌐 stackoverflow.com
Python decorator for attribute and method? - Stack Overflow
Is it possible to have a decorator that makes a method work like an attribute if values are assigned to it using class.something = 2 and work like a method if it is called like class.something(2, T... More on stackoverflow.com
🌐 stackoverflow.com
May 8, 2014
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-property-decorator-property
Python Property Decorator - @property - GeeksforGeeks
July 12, 2025 - @property decorator is a built-in decorator in Python which is helpful in defining the properties effortlessly without manually calling the inbuilt function property(). Which is used to return the property attributes of a class from the stated ...
🌐
StrataScratch
stratascratch.com › blog › how-to-use-python-property-decorator-with-examples
How to Use Python Property Decorator (With Examples) - StrataScratch
November 16, 2023 - The Python property decorator makes attributes in a class act like read-only properties. Essentially, it lets you access methods as if they were attributes, without needing to write parentheses.
🌐
Real Python
realpython.com › python-property
Python's property(): Add Managed Attributes to Your Classes – Real Python
April 17, 2026 - A property in Python is a tool for creating managed attributes in classes. The @property decorator allows you to define getter, setter, and deleter methods for attributes.
🌐
The Teclado Blog
blog.teclado.com › property-decorator-in-python
The @property decorator in Python - The Teclado Blog
May 12, 2022 - The @property decorator works as a shortcut to using the property() function that we used earlier. On top of that, it makes the code a little bit cleaner. In addition, the @property decorator executes first upon class initialization, so there's ...
🌐
LabEx
labex.io › tutorials › python-how-to-implement-python-attribute-decorators-419933
How to implement Python attribute decorators | LabEx
By understanding these basics, ... Attribute decorators in Python are specialized decorators that modify or control attribute access, creation, and manipulation within classes....
Find elsewhere
🌐
Tutorial Teacher
tutorialsteacher.com › python › property-decorator
Python: Property Decorator @property
The @property decorator is a built-in decorator in Python for the property() function.
Top answer
1 of 2
3

Here's a more thorough example of why you would want to have a private attribute: data type validation (as pointed out by the other answers).

Let's say that you've been using the Dog class for a few weeks, and a new bug has come up that happens when the name attribute is (for whatever reason) set to a value that's not a string. You now need to change the code in order to enforce that a Dog's name is indeed a string, by raising an informative Exception. How do you do that?

Your first idea may be to simply check the name's data type after creating a new Dog (using the isinstance builtin function):

my_dog = Dog(name='Max', age=5)
if not isinstance(my_dog.name, str):
    raise ValueError("The Dog's name is not a string")

This works, but there's a clear issue here: you need to 1) change every line of code where a Dog is created, and 2) you need to tell anyone who ever uses your Dog class that, by the way, remember to check for the name's data type. This is clearly a lot of work, and not future-proof by any means - what if you or another user forgets to do this check? What if no one ever reads your documentation on the Dog class?

You may then think to perform this check in the __init__ method:

def class Dog:
    def __init__(self, name, age):
        if not isinstance(name, str):
            raise ValueError("The Dog's name is not a string")
        self.__name = name
        self.age = age

This cuts down on the work, but it doesn't stop someone from modifying the Dog's name after it was instantiated:

my_dog = Dog(name='Max', age=5)
my_dog.name = 10  # oops, wrong value!

What we want to do is to actually move this type-checking functionality to a method that runs every time the name attribute gets set - which is exactly what the setter method (the one decorated with the @name.setter decorator) does:

def class Dog:
    def __init__(self, name, age):
        self.__name = name
        self.age = age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, new_name)
        if not isinstance(new_name, str):
            raise ValueError("The Dog's name is not a string")
        self.__name = new_name

Now the name's data type is always checked for, both during instantiation (in the __init__ method) and afterwards.

Data type validation is simply an example. The point of using private attributes and their getters/setters is to combine attribute access with some custom functionality. If you desire no functionality whatsoever when accessing/modifying the attribute, you're correct in your observation that you don't need to bother making it private and writing the getter/setter methods in the first place.

2 of 2
5

The advantage of properties is that from the viewpoint of the user of your class, it's syntactically the same as plain attribute access. So you can add logic (validation etc.) at any time without changing the class's interface and breaking the code that uses it. You can even swap in different versions of the class (e.g. with properties that do logging during development, without properties for production) and the caller won't even notice.

Contrast this to a language like Java where best practice is to always write getter/setter methods even if you don't need them, because you might need them later, but can't add them later without changing the API and thereby breaking code that uses the class.

BTW, there are no truly private attributes in Python. The leading __ is intended to keep names from conflicting among subclasses of the base class, where that might be a problem.

🌐
LinkedIn
linkedin.com › pulse › understanding-property-decorator-python-nuno-bispo-m7aee
Understanding the @property Decorator in Python
February 7, 2024 - This can be particularly useful ... properties. The @property decorator is a built-in Python decorator that allows you to turn class methods into properties in a way that's both elegant and user-friendly....
🌐
Programiz
programiz.com › python-programming › property
Python @property Decorator (With Examples)
Python programming provides us with a built-in @property decorator which makes usage of getters and setters much easier in Object-Oriented Programming.
🌐
Reddit
reddit.com › r/learnpython › what is the @property decorator meant to be used for?
r/learnpython on Reddit: What is the @property decorator meant to be used for?
July 27, 2021 -

I am learning Python from a C++ background. If I have this code:

class Foo():
    def __init(self):
        self.bar = 1

// Getting a property
fooObject = Foo()
print(fooObject.bar) // 1

// Setting a property
fooObject.bar = 2
print(fooObject.bar) // 2

When I tested this, it works just like it would in C++/Java etc etc.

Why would I need the @property decorator?

Is it for static properties perhaps? Or does it act similarly to Ruby's attr_accessor method maybe?

Would really appreciate if someone could clean up my confusion about this!

🌐
Readthedocs
tango-controls.readthedocs.io › projects › pytango › en › stable › versions › migration › to-9.4 › attr-decorators.html
New attribute decorators — PyTango 10.3.1 documentation
from tango import AttReqType, DevState from tango.server import Device, attribute class Test(Device): _simulated_voltage = 0.0 voltage = attribute(dtype=float) @voltage.getter def voltage(self): return self._simulated_voltage @voltage.setter def voltage(self, value): self._simulated_voltage = value @voltage.is_allowed def voltage_can_be_changed(self, req_type): if req_type == AttReqType.WRITE_REQ: return self.get_state() == DevState.ON else: return True · There are many variations possible when using these. See more in Writing TANGO servers in Python.
🌐
Python Tutorial
pythontutorial.net › home › python oop › python property decorator
Python Property Decorator
March 31, 2025 - To summarize, you can use decorators to create a property using the following pattern: class MyClass: def __init__(self, attr): self.prop = attr @property def prop(self): return self.__attr @prop.setter def prop(self, value): self.__attr = valueCode language: Python (python) In this pattern, the __attr is the private attribute and prop is the property name.
🌐
Medium
medium.com › techtofreedom › understand-the-property-decorator-of-python-classes-a6e75011cde2
Understand the Property Decorator of Python Classes | by Yang Zhou | TechToFreedom | Medium
June 13, 2020 - Understand the Property Decorator of Python Classes Introduction In object-oriented programming, each attribute of a class may have three basic methods: A getter method to get its value A setter …
🌐
AskPython
askpython.com › python › built-in-methods › python-property-decorator
How to Use Python Property Decorator? - AskPython
June 21, 2021 - The Property decorator is based on the in-built property() function. This function returns a special property object. You can call this in your Python Interpreter and take a look:
Top answer
1 of 2
9

Manual Solution

Looks like a use case for a property decorator:

class A(object):
    def __init__(self):
        self.version = "1.0"

        self.login = "logged in"
        self.login_message = "hello logger"

    @property    
    def logout(self):
        return {"1.0": "logged out", "2.0": "logged out 2.0"}[self.version]

    @property    
    def logout_message(self):
        return {"1.0": "goodbye logger", "2.0": "goodbye logger 2.0"}[self.version]

Now:

>>> a = A()
>>> a.login
'logged in'
>>> a.logout
'logged out'
>>> a.version = '2.0'
>>> a.logout
'logged out 2.0'     

Automated Solution 1

If you have lot such properties, you can automate a bit:

class A(object):
    def __init__(self):
        self.version = '1.0'
        self.login = 'logged in'
        self.login_message = 'hello logger'
        property_attrs = {'logout': {'1.0': 'logged out', 
                                     '2.0': 'logged out 2.0'},
                          'logout_message': {'1.0': 'goodbye logger',
                                             '2.0': 'goodbye logger 2.0'}}
        for name, value in property_attrs.items():
            setattr(self.__class__, name, property(lambda x: value[x.version]))

Now:

>>> a = A()
>>> a.login_message
'hello logger'
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'

Automated Solution 2

The "Automated Solution 1" redefines the properties every time you make a new instance of A. This solutions avoids this but is bit more involved. It makes use of a class decorator.

property_attrs = {'logout': {'1.0': 'logged out', '2.0': 'logged out 2.0'},
                  'logout_message': {'1.0': 'goodbye logger', '2.0': 'goodbye logger 2.0'}}

def add_properties(property_attrs):
    def decorate(cls):
        for name, value in property_attrs.items():
            setattr(cls, name, property(lambda self: value[self.version]))
        return cls
    return decorate

@add_properties(property_attrs)
class A(object):
    def __init__(self):
        self.version = '1.0'
        self.login = 'logged in'
        self.login_message = 'hello logger'

Now:

>>> a = A()
>>> a.logout
'goodbye logger'
>>> a.version = '2.0'
>>> a.logout
'goodbye logger 2.0'
2 of 2
0

You say that "self.login and self.logout should not be accessed differently. The code below keeps the self.logout dictionary but renames it to self.logouts so that we can access it as a property. Similar remarks apply to self.logout_message.

This code runs on Python 2 or 3.

from __future__ import print_function

class Executor(object):
    def do(self, s):
        print('Executing %r' % s)


class A(object):
    def __init__(self, version="1.0"):
        self.version = version

        self.login = "logged in"
        self.login_message = "hello logger"
        self.logouts = {
            "1.0": "logged out",
            "2.0": "logged out 2.0",
        }
        self.logout_messages = {
            "1.0": "goodbye logger",
            "2.0": "goodbye logger 2.0",
        }

    @property
    def logout(self):
        return self.logouts[self.version]

    @property
    def logout_message(self):
        return self.logout_messages[self.version]

    def perform(self, executor):
        executor.do(self.login)
        executor.do(self.logout)

executor = Executor()
executor.do('Tests')

#Test

a = A()
a.perform(executor)
print('msg', a.logout)
a.version = "2.0"
a.perform(executor)
print('msg', a.logout)
print()

b = A("2.0")
b.perform(executor)
print('msg', b.logout)
b.version = "3.0"
b.perform(executor)

output

Executing 'Tests'
Executing 'logged in'
Executing 'logged out'
msg logged out
Executing 'logged in'
Executing 'logged out 2.0'
msg logged out 2.0

Executing 'logged in'
Executing 'logged out 2.0'
msg logged out 2.0
Executing 'logged in'
Traceback (most recent call last):
  File "./qtest.py", line 69, in <module>
    b.perform(executor)
  File "./qtest.py", line 50, in perform
    executor.do(self.logout)
  File "./qtest.py", line 42, in logout
    return self.logouts[self.version]
KeyError: '3.0'
🌐
Python 101
python101.pythonlibrary.org › chapter25_decorators.html
Chapter 25 - Decorators — Python 101 1.0 documentation
Python has a neat little concept called a property that can do several useful things. We will be looking into how to do the following: ... One of the simplest ways to use a property is to use it as a decorator of a method. This allows you to turn a class method into a class attribute.
🌐
Medium
medium.com › @christopher.kelly1997 › python-decorators-and-dynamic-properties-55402a2e1aff
Python Decorators and Dynamic Properties | by Christopher Hugh Kelly | Medium
April 23, 2025 - Also the _ denotes a private attribute here in Python. That is also why you need the _ in the set too. So you don’t accidentally overwrite the property and cause access hell. We call the one with the _ in front of the name the private name. The one without is the public name. ... One other thing to note about the property method that you do not see here, technically the property decorator applies the access restrictions to a class attribute that is accessed via an object.