Prefer properties. It's what they're there for.

The reason is that all attributes are public in Python. Starting names with an underscore or two is just a warning that the given attribute is an implementation detail that may not stay the same in future versions of the code. It doesn't prevent you from actually getting or setting that attribute. Therefore, standard attribute access is the normal, Pythonic way of, well, accessing attributes.

The advantage of properties is that they are syntactically identical to attribute access, so you can change from one to another without any changes to client code. You could even have one version of a class that uses properties (say, for code-by-contract or debugging) and one that doesn't for production, without changing the code that uses it. At the same time, you don't have to write getters and setters for everything just in case you might need to better control access later.

Answer from kindall on Stack Overflow
🌐
Real Python
realpython.com › python-getter-setter
Getters and Setters: Manage Attributes in Python – Real Python
October 20, 2025 - Getter and setter methods allow you to access and mutate non-public attributes while maintaining encapsulation. In Python, you’ll typically expose attributes as part of your public API and use properties when you need attributes with functional behavior.
Discussions

Having a hard time understanding getters and setters
tl;dr - getters and setters are used so that people creating classes can upgrade classes without breaking the code of those that use them. Getters and setters wrap attributes as methods, allowing you to use functionality only available through methods. In Python, we don’t use getters and setters because we have properties, which allow us to make attributes look like methods after the fact. —- To really understand getters and setters means understanding class design, understanding the potential problems with accessing object attributes, understanding why some languages use getters and setters, and finally understanding why we do not need getters and setters in Python. Imagine designing a class, but for a library, a library that people other than you will use. You might design a class once and then you are done with it, but in all likelihood you’ll want to design it in a way where you can upgrade the functionality of it if you need to, so you need to design it in a futureproof way. The most important thing here is to ensure that, when you do upgrade your class with new functionality, you do it in a way that does not break the code of the people that are using it. This is the concept of backwards compatibility - your new class can be used by people who write code against the older class. The importance of this cannot be understated - if you continuously break people’s code every time you upgrade your own, no one will want to use your code, ever. To ensure backwards compatibility, all the things that the people who write code access in your class access must not change. This means - the library import, the class name, the method names, the method parameters, what the method returns, and the attributes of the class - all these must be unchanged. All these aspects make up what is known as the public API of your class, and changing these means breaking backwards compatibility. Now, consider writing code for such a class. For a complicated method, you may wish to refactor its code into multiple sub-methods. Or, you may need to store attributes on your class for reasons of convenience. Having these as part of your public API would be undesirable, partly because the people using your library class do not need to access these, but mostly because if they end up using them, it’ll hamper your ability to upgrade your class in the future. These end up being private methods and attribute - these are not meant to be accessible by people who use your code and you are free to change them should you need to in the future. Languages like C++ and Java let you label functions and attributes as public and private, and people using your library class will be barred from accessing private elements. In Python, there is no public or private - instead you name your private elements giving it a prefix of a single underscore (i.e. self._bar instead of self.bar), and whilst calling code can access these underscore variables, they understand that they generally must not unless they absolutely have to. An important part of protecting your public API is also related to attributes. Attribute access is very limited - you can get a value from an attribute, set a value onto an attribute, and delete an attribute entirely (get, set and delete) - and that’s about it. The problem is, when you upgrade your class, you may end to wanting to do something more. For example, you may want to add validation - i.e. you may want to raise an exception if someone assigns an incorrect value to an attribute. Or you might want to change one attribute to retrieve data from another place - a classic example is a class that provides temperature for something in both Celsius and Fahrenheit, the attribute for one should just get the attribute for the other and then do the C-to/from-F conversion. These are things that can be done only by methods, not attributes, and if you use an attribute as part of your public API you can’t upgrade your class to use these things without breaking the code of those that use your class, because they are accessing an attribute (self.bar) rather than a method (self.bar()). This is where getters and setters come into play. Languages like Java and C++ use these. The concept is simple - have your attributes as private (i.e. self._bar), and then wrap your private attributes in public methods. This is how it looks in Python: def get_bar(self): return self._bar def set_bar(self, val): self._bar = val The people that use your code then use self.get_bar() and self.set_bar() when interacting with your class. Because they are interacting with a method, you can upgrade your class with the above functionality described, without breaking your public API. Getters and setters solve an important problem, but they are not without problems - the biggest being that they are fugly. The people that use your code have to do self.get_bar() rather than self.bar. But the bigger problem is for you, the creator of the class, who has to litter your code with getters and setters, regardless of whether you need them or not. You may never need to upgrade your class in the future, but you’ll need to use getters and setters if you want to expose an attribute, just on the off chance you’ll need it. Python solves this problem with properties. Properties allow for attributes to be converted into methods, but still be accessed as attributes. You, as the creator of a class, use public attributes when required (self.bar). When you upgrade your class, if and only if you need the functionality of a method, you use a property. You convert your public attribute into a private one (self._bar) and then you use the below syntax: @property def bar(self): return self._bar @bar.setter def bar(self, val): self._bar = val People who call your code still access the same way as an attribute (self.bar) but you now have a method under the hood, so you can use functionality that only methods can do, without breaking your public API for your users. And you only add properties when you need them, so you don’t litter your code like you would with getters and setters. More on reddit.com
🌐 r/learnpython
13
9
May 7, 2022
python - What's the pythonic way to use getters and setters? - Stack Overflow
Copyclass C(object): def ... = c.x # getter called del c.x # deleter called ... @Casey: No. References to ._x (which isn't a property, just a plain attribute) bypass the property wrapping. Only references to .x go through the property. 2019-07-19T10:52:43.487Z+00:00 ... mypy expect those two functions to be together (no other functions inside x and its setter) 2021-02-17T16:01:52.583Z+00:00 ... This is Pythonic if you actually ... More on stackoverflow.com
🌐 stackoverflow.com
what are getters and setters in python? and also @property can anyone please explain.
A class/object (e.g. User) can have multiple fields/attributes/members (e.g. first_name, last_name, login, password, email...). Normally, these fields/attributes/members behave like regular variables (or values in a dict ) - anybody can read- and modify them. But if you want to control access to these attributes in a more sophisticated fashion - e.g., do some validation, logging, unit conversion - then you implement a getter and a setter, which will execute some additional code upon reading/modifying the attribute in question. On the outside, however, it would still look like you were simply accessing e.g. user.login. More on reddit.com
🌐 r/learnpython
20
22
July 4, 2025
@property vs Getter Method
One of the comments in that thread says to just access fields directly whenever possible and migrate to properties if needed (e.g., input validation, read-only access to fields). That's probably the way to go. That is, don't just make getters/setters or properties for the hell of it like in Java. As for computationally-demanding tasks, if your property's getter or setter runs slowly, it probably should really be a method with something like calculate in its name to hint to the user that it's not just getting/setting a pre-existing value. More on reddit.com
🌐 r/learnpython
1
1
October 20, 2020
🌐
GeeksforGeeks
geeksforgeeks.org › python › getter-and-setter-in-python
Getter and Setter in Python - GeeksforGeeks
July 11, 2025 - In this approach, the @property decorator is used for the getter and the @<property_name>.setter decorator is used for the setter.
🌐
Medium
aignishant.medium.com › understanding-python-property-decorators-getters-setters-57d6b535e5d2
Understanding Python Property Decorators: Getters, Setters | by Nishant Gupta | Medium
February 23, 2025 - In this article, we’ll explore Python property decorators in detail, covering: ... The @property decorator is used to define a method that acts as a "getter" for an attribute. When you access the attribute, the method decorated with @property ...
🌐
Python Course
python-course.eu › oop › properties-vs-getters-and-setters.php
3. Properties vs. Getters and Setters | OOP | python-course.eu
Alternatively, we could have used a different syntax without decorators to define the property. As you can see, the code is definitely less elegant and we have to make sure that we use the getter function in the __init__ method again:
🌐
DataCamp
datacamp.com › tutorial › property-getters-setters
Python Property vs. Getters & Setters | DataCamp
December 18, 2018 - class SampleClass: def __init__(self, a): ## private varibale or property in Python self.__a = a ## getter method to get the properties using an object def get_a(self): return self.__a ## setter method to change the value 'a' using an object def set_a(self, a): self.__a = a
🌐
Programiz
programiz.com › python-programming › property
Python @property Decorator (With Examples)
A pythonic way to deal with the above problem is to use the property class. Here is how we can update our code: # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value...") return self._temperature # setter def set_temperature(self, value): print("Setting value...") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature)
Find elsewhere
🌐
Machine Learning Plus
machinelearningplus.com › blog › python @property: getters, setters, and attribute control guide
Python @property: Getters, Setters, and Attribute Control Guide - machinelearningplus
July 15, 2025 - class User: def __init__(self, email, age): self._email = "" self._age = 0 self.email = email # Use setter for validation self.age = age # Use setter for validation @property def email(self): """Getter for email""" return self._email @email.setter def email(self, value): """Setter with validation for email""" if not isinstance(value, str): raise TypeError("Email must be a string") if '@' not in value: raise ValueError("Invalid email format") self._email = value.lower() # Normalize to lowercase @property def age(self): """Getter for age""" return self._age @age.setter def age(self, value): """S
🌐
Mimo
mimo.org › glossary › python › property
Python property(): Syntax, Usage, and Examples
Manages Attribute Access: The primary ... code, like validation or calculation. @property is for Getters: The @property decorator turns a method into a "getter," which allows it to be accessed like an attribute (e.g., obj.my_prop) instead of a method (obj.my_prop())....
🌐
Medium
medium.com › @pijpijani › understanding-property-in-python-getters-and-setters-b65b0eee62f9
Understanding Property in Python: Getters and Setters | by Pikho | Medium
March 2, 2023 - To create a property in Python, you can use the @property decorator to define a getter method for the attribute and the @<attribute>.setter decorator to define a setter method.
🌐
Python Reference
python-reference.readthedocs.io › en › latest › docs › property › getter.html
getter — Python Reference (The Right Way) 0.1 documentation
A property object has getter, setter, and deleter methods usable as decorators that create a copy of the property with the corresponding accessor function set to the decorated function.
🌐
Python Reference
python-reference.readthedocs.io › en › latest › docs › property › setter.html
setter — Python Reference (The Right Way) 0.1 documentation
A property object has getter, setter, and deleter methods usable as decorators that create a copy of the property with the corresponding accessor function set to the decorated function.
🌐
IONOS
ionos.com › digital guide › websites › web development › python property
How to use Python property - IONOS
July 20, 2023 - As you can see, we created a new attribute called “name” in order to call the property() function and assigned the result of the function’s call to it. We did this without an un­der­score because Python property lets us address it from outside. The property() function now has the getter and setter methods as pa­ra­me­ters.
Top answer
1 of 5
11
tl;dr - getters and setters are used so that people creating classes can upgrade classes without breaking the code of those that use them. Getters and setters wrap attributes as methods, allowing you to use functionality only available through methods. In Python, we don’t use getters and setters because we have properties, which allow us to make attributes look like methods after the fact. —- To really understand getters and setters means understanding class design, understanding the potential problems with accessing object attributes, understanding why some languages use getters and setters, and finally understanding why we do not need getters and setters in Python. Imagine designing a class, but for a library, a library that people other than you will use. You might design a class once and then you are done with it, but in all likelihood you’ll want to design it in a way where you can upgrade the functionality of it if you need to, so you need to design it in a futureproof way. The most important thing here is to ensure that, when you do upgrade your class with new functionality, you do it in a way that does not break the code of the people that are using it. This is the concept of backwards compatibility - your new class can be used by people who write code against the older class. The importance of this cannot be understated - if you continuously break people’s code every time you upgrade your own, no one will want to use your code, ever. To ensure backwards compatibility, all the things that the people who write code access in your class access must not change. This means - the library import, the class name, the method names, the method parameters, what the method returns, and the attributes of the class - all these must be unchanged. All these aspects make up what is known as the public API of your class, and changing these means breaking backwards compatibility. Now, consider writing code for such a class. For a complicated method, you may wish to refactor its code into multiple sub-methods. Or, you may need to store attributes on your class for reasons of convenience. Having these as part of your public API would be undesirable, partly because the people using your library class do not need to access these, but mostly because if they end up using them, it’ll hamper your ability to upgrade your class in the future. These end up being private methods and attribute - these are not meant to be accessible by people who use your code and you are free to change them should you need to in the future. Languages like C++ and Java let you label functions and attributes as public and private, and people using your library class will be barred from accessing private elements. In Python, there is no public or private - instead you name your private elements giving it a prefix of a single underscore (i.e. self._bar instead of self.bar), and whilst calling code can access these underscore variables, they understand that they generally must not unless they absolutely have to. An important part of protecting your public API is also related to attributes. Attribute access is very limited - you can get a value from an attribute, set a value onto an attribute, and delete an attribute entirely (get, set and delete) - and that’s about it. The problem is, when you upgrade your class, you may end to wanting to do something more. For example, you may want to add validation - i.e. you may want to raise an exception if someone assigns an incorrect value to an attribute. Or you might want to change one attribute to retrieve data from another place - a classic example is a class that provides temperature for something in both Celsius and Fahrenheit, the attribute for one should just get the attribute for the other and then do the C-to/from-F conversion. These are things that can be done only by methods, not attributes, and if you use an attribute as part of your public API you can’t upgrade your class to use these things without breaking the code of those that use your class, because they are accessing an attribute (self.bar) rather than a method (self.bar()). This is where getters and setters come into play. Languages like Java and C++ use these. The concept is simple - have your attributes as private (i.e. self._bar), and then wrap your private attributes in public methods. This is how it looks in Python: def get_bar(self): return self._bar def set_bar(self, val): self._bar = val The people that use your code then use self.get_bar() and self.set_bar() when interacting with your class. Because they are interacting with a method, you can upgrade your class with the above functionality described, without breaking your public API. Getters and setters solve an important problem, but they are not without problems - the biggest being that they are fugly. The people that use your code have to do self.get_bar() rather than self.bar. But the bigger problem is for you, the creator of the class, who has to litter your code with getters and setters, regardless of whether you need them or not. You may never need to upgrade your class in the future, but you’ll need to use getters and setters if you want to expose an attribute, just on the off chance you’ll need it. Python solves this problem with properties. Properties allow for attributes to be converted into methods, but still be accessed as attributes. You, as the creator of a class, use public attributes when required (self.bar). When you upgrade your class, if and only if you need the functionality of a method, you use a property. You convert your public attribute into a private one (self._bar) and then you use the below syntax: @property def bar(self): return self._bar @bar.setter def bar(self, val): self._bar = val People who call your code still access the same way as an attribute (self.bar) but you now have a method under the hood, so you can use functionality that only methods can do, without breaking your public API for your users. And you only add properties when you need them, so you don’t litter your code like you would with getters and setters.
2 of 5
7
Generally an object doesn't want you directly accessing or changing it's attributes. This is kinda the point of encapsulation. As such, you generally want to have 'getters' (that GET an objects attributes) and 'setters' (that, as you may guess, SET those attributes.) Say you have a Ball class. Ball has an attribute, diameter. Let's now say softball is an instance of object Ball. You COULD use softball.diameter to access this attribute. In programming it's generally frowned upon, though in Python less so. You might have an object method, say, get_diameter() that returns the diameter. So instead of accessing the variable directly through softball.diameter, you use ball.get_diameter() which is an example of a getter. See if you can imagine how a setter would look. This allows you to encapsulate how attributes are set, changed, accessed, etc, inside the method itself, and is generally considered a good thing.
🌐
freeCodeCamp
freecodecamp.org › news › python-property-decorator
The @property Decorator in Python: Its Use Cases, Advantages, and Syntax
December 19, 2019 - Specifically, you can define three methods for a property: A getter - to access the value of the attribute. A setter - to set the value of the attribute. A deleter - to delete the instance attribute.
🌐
Analytics Vidhya
analyticsvidhya.com › home › getter and setter in python
Getter and Setter in Python - Analytics Vidhya
February 17, 2024 - The above example defines a `BankAccount` class with a private attribute `_balance`. We use the `@property` decorator to define a getter method `balance` that returns the value of `_balance`. We also define a setter method, `balance,` that raises an `AttributeError` to prevent direct balance modification.
Top answer
1 of 9
1163

Try this: Python Property

The sample code is:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        print("getter of x called")
        return self._x

    @x.setter
    def x(self, value):
        print("setter of x called")
        self._x = value

    @x.deleter
    def x(self):
        print("deleter of x called")
        del self._x


c = C()
c.x = 'foo'  # setter called
foo = c.x    # getter called
del c.x      # deleter called
2 of 9
629

What's the pythonic way to use getters and setters?

The "Pythonic" way is not to use "getters" and "setters", but to use plain attributes, like the question demonstrates, and del for deleting (but the names are changed to protect the innocent... builtins):

value = 'something'

obj.attribute = value  
value = obj.attribute
del obj.attribute

If later, you want to modify the setting and getting, you can do so without having to alter user code, by using the property decorator:

class Obj:
    """property demo"""
    #
    @property            # first decorate the getter method
    def attribute(self): # This getter method name is *the* name
        return self._attribute
    #
    @attribute.setter    # the property decorates with `.setter` now
    def attribute(self, value):   # name, e.g. "attribute", is the same
        self._attribute = value   # the "value" name isn't special
    #
    @attribute.deleter     # decorate with `.deleter`
    def attribute(self):   # again, the method name is the same
        del self._attribute

(Each decorator usage copies and updates the prior property object, so note that you should use the same name for each set, get, and delete function/method.)

After defining the above, the original setting, getting, and deleting code is the same:

obj = Obj()
obj.attribute = value  
the_value = obj.attribute
del obj.attribute

You should avoid this:

def set_property(property,value):  
def get_property(property):  

Firstly, the above doesn't work, because you don't provide an argument for the instance that the property would be set to (usually self), which would be:

class Obj:

    def set_property(self, property, value): # don't do this
        ...
    def get_property(self, property):        # don't do this either
        ...

Secondly, this duplicates the purpose of two special methods, __setattr__ and __getattr__.

Thirdly, we also have the setattr and getattr builtin functions.

setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value)  # default is optional

The @property decorator is for creating getters and setters.

For example, we could modify the setting behavior to place restrictions the value being set:

class Protective(object):

    @property
    def protected_value(self):
        return self._protected_value

    @protected_value.setter
    def protected_value(self, value):
        if acceptable(value): # e.g. type or range check
            self._protected_value = value

In general, we want to avoid using property and just use direct attributes.

This is what is expected by users of Python. Following the rule of least-surprise, you should try to give your users what they expect unless you have a very compelling reason to the contrary.

Demonstration

For example, say we needed our object's protected attribute to be an integer between 0 and 100 inclusive, and prevent its deletion, with appropriate messages to inform the user of its proper usage:

class Protective(object):
    """protected property demo"""
    #
    def __init__(self, start_protected_value=0):
        self.protected_value = start_protected_value
    # 
    @property
    def protected_value(self):
        return self._protected_value
    #
    @protected_value.setter
    def protected_value(self, value):
        if value != int(value):
            raise TypeError("protected_value must be an integer")
        if 0 <= value <= 100:
            self._protected_value = int(value)
        else:
            raise ValueError("protected_value must be " +
                             "between 0 and 100 inclusive")
    #
    @protected_value.deleter
    def protected_value(self):
        raise AttributeError("do not delete, protected_value can be set to 0")

(Note that __init__ refers to self.protected_value but the property methods refer to self._protected_value. This is so that __init__ uses the property through the public API, ensuring it is "protected".)

And usage:

>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0

Do the names matter?

Yes they do. .setter and .deleter make copies of the original property. This allows subclasses to properly modify behavior without altering the behavior in the parent.

class Obj:
    """property demo"""
    #
    @property
    def get_only(self):
        return self._attribute
    #
    @get_only.setter
    def get_or_set(self, value):
        self._attribute = value
    #
    @get_or_set.deleter
    def get_set_or_delete(self):
        del self._attribute

Now for this to work, you have to use the respective names:

obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'  
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error

I'm not sure where this would be useful, but the use-case is if you want a get, set, and/or delete-only property. Probably best to stick to semantically same property having the same name.

Conclusion

Start with simple attributes.

If you later need functionality around the setting, getting, and deleting, you can add it with the property decorator.

Avoid functions named set_... and get_... - that's what properties are for.

🌐
TechVidvan
techvidvan.com › tutorials › python-property-class-getters-setters
Python Property Class | Getters & Setters - TechVidvan
May 18, 2021 - Setters:- They help in changing/renaming the already defined internal libraries in python. ... Class latestclass: def __ini__(self, a): self.__b= b ## getter method to get the properties def get_b(self): return self.__b ## setter method to change the value 'b' def set_b(self, b): self.__b = b