655

I'm doing it like:

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

or

object.property = value  
value = object.property

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

starball
  • 20,030
  • 7
  • 43
  • 238
Jorge Guberte
  • 10,464
  • 8
  • 37
  • 56

9 Answers9

1073

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
rayryeng
  • 102,964
  • 22
  • 184
  • 193
Grissiom
  • 11,355
  • 3
  • 18
  • 23
  • 5
    Is the setter for x called in the initializer when instantiating _x? – Casey Jul 03 '19 at 15:12
  • 18
    @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`. – ShadowRanger Jul 19 '19 at 10:52
  • mypy expect those two functions to be together (no other functions inside x and its setter) – Pierre.Sassoulas Feb 17 '21 at 16:01
  • 10
    This is Pythonic if you actually need to perform some action/manipulation in the getter or setter other than just retrieving a private property. For this simple case, even though it is considered best practice to use getters/setters in other languages, in Python just create a public property. Simple, Zen, and to the point – Mason3k Mar 20 '21 at 16:23
571

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.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
  • Besides duplicating available functionality, why should writing your own setters and getters be avoided? I understand it might not be the Pythonic way, but are there really serious issues one might encounter otherwise? – user1350191 Jul 27 '18 at 13:56
  • 1
    @user1350191 I'm at work, I'll get back to this a little later and expand my answer - and I'll let you know when I do. – Russia Must Remove Putin Jul 27 '18 at 14:20
  • 11
    In your demo, the `__init__` method refers to `self.protected_value` but the getter and setters refer to `self._protected_value`. Could you please explain how this works? I tested your code and it works as is - so this is not a typo. – codeforester Jul 28 '18 at 00:36
  • 3
    @codeforester I was hoping to respond in my answer earlier, but until I can, this comment should suffice. I hope you can see that it uses the property through the public api, ensuring it is "protected". It wouldn't make sense to "protect" it with a property and then use the non-public api instead in the `__init__` would it? – Russia Must Remove Putin Aug 01 '18 at 19:37
  • 5
    Yes, @AaronHall got it now. I didn't realize `self.protected_value = start_protected_value` is actually calling the setter function; I thought it was an assignment. – codeforester Aug 01 '18 at 20:42
  • 4
    imho this should be the accepted answer, if I understood correctly python takes just the opposite point compared to eg java. Instead of making everything private by default and writing some extra code when it is needed publicly in python you can make everything public and add privacy later on – 463035818_is_not_an_ai Aug 29 '18 at 10:27
  • I'm actually confused about how this `self.protected_value = start_protected_value` is not an assigment but a function call. – elelias Oct 03 '18 at 20:10
  • @elelias the `property` decorator uses the descriptor protocol to manage the dotted lookup. I have an answer that describes how they work here: https://stackoverflow.com/a/34554353/541136 – Russia Must Remove Putin Oct 03 '18 at 20:20
  • This is probably a dumb question, but does the setter method have to have `value` as the input? I always see it used in examples, but idk if that's just because it's an easy substitution or a required parameter name. – nos codemos May 11 '20 at 11:20
  • @noscodemos No, you don't need to use `value`, but it's the convention, like `self` for an object instance. See for example the [`property` documentation](https://docs.python.org/3/library/functions.html#property), and compare the signatures of [`object.__setattr__`](https://docs.python.org/3/reference/datamodel.html#object.__setattr__) and [`object.__setitem__`](https://docs.python.org/3/reference/datamodel.html#object.__setitem__) – wjandrea May 31 '20 at 17:40
  • 1
    Saying the Pythonic way is not to use any setters & getters is a strong statement. Can you add documentation to back your claim? – XValidated Mar 12 '21 at 20:17
  • 3
    @XValidated [PEP 8 says](https://www.python.org/dev/peps/pep-0008/#designing-for-inheritance): *"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."* – wjandrea Mar 29 '21 at 17:45
  • what if you refactor some code inside class and think that the properties are used extensively in class but name doesn't make sense, so what will you do? change the property name used everywhere outside class? or you will make duplicate properties inside getter setter attribute method and will keep duplicate properties? – Hafiz Sep 24 '21 at 11:01
  • @Hafiz If you're changing the interface, then it depends on the users of your code and whether you can know that the users have fully migrated or not, but until they migrate you should maintain backward compatibility even if your documentation says the old naming is sad and bad and they should *feel* sad and bad for using it at this point. If you're changing implementation details (names inside properties), supporting old implementation details to support people who should have known better would be very generous of you, especially if you're a small, independent open-source project. – Russia Must Remove Putin Sep 24 '21 at 13:03
  • 1
    @Hafiz If you do duplicate a property, you just need to alias it: `new_name = old_name`. So your question isn't specific to properties; they're the same as class attributes in that respect. – wjandrea Sep 24 '21 at 17:04
  • @wjandrea yeah I think that makes sense. If you have refactored the class, you can definitely do this at-least. – Hafiz Sep 25 '21 at 19:52
  • AaronHall thanks for the comment but I was only talking about inside class and my point was that inner things of class shouldn't be effecting the interface and how the change of property name couldn't effect outside , so @wjandrea have addressed my concern well. – Hafiz Sep 25 '21 at 19:55
  • I know this is as old as dirt but it really is a horrible answer that fundamentally misunderstands OOP. Class based objects are not Dictionaries that allow the use of dot notation! OOP is about tightly coupling data with functions (methods). It is not an attribute datastore. If all you want is a box for data python already offers data structures for that. – Tarynn Jan 12 '22 at 22:45
  • @Tarynn How does it imply that class-based objects are just data stores? How would you implement a property on a dict? I think actually, you've fundamentally misunderstood this answer, so if there's anything unclear, your feedback could help improve it. – wjandrea Jan 14 '22 at 23:48
  • @wjandrea Direct access to members should be considered a code smell. Object instances are a type of state machine where property values hold the instance state and methods control changes to that state. If an object isn't functionally changed when a value changes the value shouldn't be part of the class, if an object is functionally changed when a value is updated access to it should be controlled through accessor methods. Access to attribute values because you might make it a property someday is premature optimization. – Tarynn Jan 18 '22 at 04:26
  • @Tarynn It sounds like you learned OOP in another language, maybe Java? Python takes a different approach to attribute access. Check out the [PEP 8 excerpt I posted above](/q/2627002#comment118184879_36943813). – wjandrea Jan 18 '22 at 04:38
  • @wjandrea I learned OOP using C++, C#, Java and others. Nothing I stated is language specific. Making pass through getters and setters in Java or public members in C# and C++ is the same as doing direct attribute access in python. No matter what language it is in it is a code smell. I suggest you read: Design Patterns: Elements of Reusable Object-Oriented Software, Refactoring: Improving the Design of Existing Code, and Clean Code: A Handbook of Agile Software Craftsmanship All are great primers on OOP and language agnostic. – Tarynn Jan 19 '22 at 05:24
  • @Tarynn Did you read the PEP 8 excerpt? And are you familiar with ["We're all responsible users"](https://docs.python-guide.org/writing/style/#we-are-all-responsible-users)? – wjandrea Jan 19 '22 at 05:55
  • @wjandrea those questions are two seperate things so let me address the first one. I have no issue with the excerpt. If you have to allow direct access to an instance member wrapping it in a property is overly burdensome. This goes for using the public modifier in C# and C++ as well. My issue is that one should avoid having "simple public data attributes" in the first place. Excepting DTO's a member variable that is easily changed in isolation means that the attribute shouldn't be there in the first place. This is especially true for a dynamically typed language. – Tarynn Jan 20 '22 at 07:14
  • @wjandrea Its beyond the scope of the question but since you asked I am aware of the python communities flawed reasoning for breaking encapsulation/abstraction. The fact true encapsulation is undesired implies a community wide desire to potentially violate it. Enforced abstraction has nothing to do with being "defensive" or paternalistic. It is about maintaining the integrity of the code not just immediately but across all future versions. It should be assumed that changes to abstracted code will be non-breaking. Python drastically increases any classes api by refusing to enforce encapsulation – Tarynn Jan 20 '22 at 07:34
  • I'd to like add that you conclusion advocating to start with 'simple attributes', contradicts the reason provided from the following website. The website linked to argues that private variables make code debugging much more easier to deal with, especially when parent and child class (inheritance) are involved. Overall your explanation was very helpful. https://towardsdatascience.com/private-protected-attributes-in-python-demystified-once-and-for-all-9456d4e56414 – gimmegimme Dec 08 '22 at 16:00
34
In [1]: class test(object):
    def __init__(self):
        self.pants = 'pants'
    @property
    def p(self):
        return self.pants
    @p.setter
    def p(self, value):
        self.pants = value * 2
   ....: 
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20
Autoplectic
  • 7,566
  • 30
  • 30
27

Using @property and @attribute.setter helps you to not only use the "pythonic" way but also to check the validity of attributes both while creating the object and when altering it.

class Person(object):
    def __init__(self, p_name=None):
        self.name = p_name

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

    @name.setter
    def name(self, new_name):
        if type(new_name) == str: #type checking for name property
            self._name = new_name
        else:
            raise Exception("Invalid value for name")

By this, you actually 'hide' _name attribute from client developers and also perform checks on name property type. Note that by following this approach even during the initiation the setter gets called. So:

p = Person(12)

Will lead to:

Exception: Invalid value for name

But:

>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
Farzad Vertigo
  • 2,458
  • 1
  • 29
  • 32
  • I think `self.name` should read `self._name` and `p = person('Mike')` should read `p = Person('Mike')`. Edit-queue is full... Cheers! – lcnittl Mar 09 '21 at 23:27
  • If we change `self.name` to `self._name` in the `__init__` method we will lose validation upon object instantiation. With that `Person(12)` can be created (although cannot be updated to have another number as a name afterwards). – Farzad Vertigo Jun 07 '21 at 23:47
16

This is an old question but the topic is very important and always current. In case anyone wants to go beyond simple getters/setters i have wrote an article about superpowered properties in python with support for slots, observability and reduced boilerplate code.

from objects import properties, self_properties


class Car:
    with properties(locals(), 'meta') as meta:

        @meta.prop(read_only=True)
        def brand(self) -> str:
            """Brand"""

        @meta.prop(read_only=True)
        def max_speed(self) -> float:
            """Maximum car speed"""

        @meta.prop(listener='_on_acceleration')
        def speed(self) -> float:
            """Speed of the car"""
            return 0  # Default stopped

        @meta.prop(listener='_on_off_listener')
        def on(self) -> bool:
            """Engine state"""
            return False

    def __init__(self, brand: str, max_speed: float = 200):
        self_properties(self, locals())

    def _on_off_listener(self, prop, old, on):
        if on:
            print(f"{self.brand} Turned on, Runnnnnn")
        else:
            self._speed = 0
            print(f"{self.brand} Turned off.")

    def _on_acceleration(self, prop, old, speed):
        if self.on:
            if speed > self.max_speed:
                print(f"{self.brand} {speed}km/h Bang! Engine exploded!")
                self.on = False
            else:
                print(f"{self.brand} New speed: {speed}km/h")
        else:
            print(f"{self.brand} Car is off, no speed change")

This class can be used like this:

mycar = Car('Ford')

# Car is turned off
for speed in range(0, 300, 50):
    mycar.speed = speed

# Car is turned on
mycar.on = True
for speed in range(0, 350, 50):
    mycar.speed = speed

This code will produce the following output:

Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Turned on, Runnnnnn
Ford New speed: 0km/h
Ford New speed: 50km/h
Ford New speed: 100km/h
Ford New speed: 150km/h
Ford New speed: 200km/h
Ford 250km/h Bang! Engine exploded!
Ford Turned off.
Ford Car is off, no speed change

More info about how and why here: https://mnesarco.github.io/blog/2020/07/23/python-metaprogramming-properties-on-steroids

mnesarco
  • 2,619
  • 23
  • 31
11

Properties are pretty useful since you can use them with assignment but then can include validation as well. You can see this code where you use the decorator @property and also @<property_name>.setter to create the methods:

# Python program displaying the use of @property 
class AgeSet:
    def __init__(self):
        self._age = 0

    # using property decorator a getter function
    @property
    def age(self):
        print("getter method called")
        return self._age

    # a setter function
    @age.setter
    def age(self, a):
        if(a < 18):
            raise ValueError("Sorry your age is below eligibility criteria")
        print("setter method called")
        self._age = a

pkj = AgeSet()

pkj.age = int(input("set the age using setter: "))

print(pkj.age)

There are more details in this post I wrote about this as well: https://pythonhowtoprogram.com/how-to-create-getter-setter-class-properties-in-python-3/

charlesw001
  • 144
  • 1
  • 3
6

You can use accessors/mutators (i.e. @attr.setter and @property) or not, but the most important thing is to be consistent!

If you're using @property to simply access an attribute, e.g.

class myClass:
    def __init__(a):
        self._a = a

    @property
    def a(self):
        return self._a

use it to access every* attribute! It would be a bad practice to access some attributes using @property and leave some other properties public (i.e. name without an underscore) without an accessor, e.g. do not do

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b

    @property
    def a(self):
        return self.a

Note that self.b does not have an explicit accessor here even though it's public.

Similarly with setters (or mutators), feel free to use @attribute.setter but be consistent! When you do e.g.

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b 

    @a.setter
    def a(self, value):
        return self.a = value

It's hard for me to guess your intention. On one hand you're saying that both a and b are public (no leading underscore in their names) so I should theoretically be allowed to access/mutate (get/set) both. But then you specify an explicit mutator only for a, which tells me that maybe I should not be able to set b. Since you've provided an explicit mutator I am not sure if the lack of explicit accessor (@property) means I should not be able to access either of those variables or you were simply being frugal in using @property.

*The exception is when you explicitly want to make some variables accessible or mutable but not both or you want to perform some additional logic when accessing or mutating an attribute. This is when I am personally using @property and @attribute.setter (otherwise no explicit acessors/mutators for public attributes).

Lastly, PEP8 and Google Style Guide suggestions:

PEP8, Designing for Inheritance says:

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.

On the other hand, according to Google Style Guide Python Language Rules/Properties the recommendation is to:

Use properties in new code to access or set data where you would normally have used simple, lightweight accessor or setter methods. Properties should be created with the @property decorator.

The pros of this approach:

Readability is increased by eliminating explicit get and set method calls for simple attribute access. Allows calculations to be lazy. Considered the Pythonic way to maintain the interface of a class. In terms of performance, allowing properties bypasses needing trivial accessor methods when a direct variable access is reasonable. This also allows accessor methods to be added in the future without breaking the interface.

and cons:

Must inherit from object in Python 2. Can hide side-effects much like operator overloading. Can be confusing for subclasses.

Tomasz Bartkowiak
  • 12,154
  • 4
  • 57
  • 62
  • 2
    I strongly disagree. If I have 15 attributes on my object, and I want one to be computed with `@property`, making the rest also use `@property` seems like a poor decision. – Quelklef Jan 13 '20 at 17:15
  • Agree but only if there is something specific about this particular attribute that you need `@property` for (e.g. executing some special logic before returning an attribute). Otherwise why would you decorate one attribute with `@propery` and not others? – Tomasz Bartkowiak Jan 13 '20 at 18:51
  • @Quelklef see the sidenote in the post (marked with asterisk). – Tomasz Bartkowiak Jan 13 '20 at 19:00
  • Well... If you're not doing one of the things mentioned by the sidenote, then you shouldn't be using `@property` to begin with, right? If your getter is `return this._x` and your setter is `this._x = new_x`, then using `@property` at all is kinda silly. – Quelklef Jan 13 '20 at 23:39
  • Agree, and I'm completely with you. However some people still do use `@property` even in the example you've mentioned and all I am saying is that it is still fine as long as you're being consistent. – Tomasz Bartkowiak Jan 14 '20 at 00:27
  • 1
    Hmm, perhaps. I personally would say it's not fine---it's entirely superfluous. But I can see where you're coming from. I guess I just read your post as saying that "the most important thing when using `@property` is being consistent." – Quelklef Jan 14 '20 at 01:12
  • @TomaszBartkowiak no, *the entire point* of the `property` decorator is *to not have to write boilerplate getters and setters*. Python is not Java. Also, looking at the example provided in the Google recommendations, it seems they are talking about situations where your getter actually *does* something, not simply `return self._some_var`. So that would not be inconsitent with PEP8 – juanpa.arrivillaga Apr 02 '20 at 11:10
  • Absolutely NO! What makes Python accessors great is that you don't need to be consistent. I never had one class with all @property, but I almost always need to sprinkle a few here and there – stenci Jan 11 '22 at 14:00
  • getter is a method which can be passed as argument to other func, for example , `valid_if_not=self.is_finalized)` . In case of shit property you have to lambdify it every time. Also explicit set can be combined in sequence, for example: `.set_name('asd').set_value(3).` in case of shit property you cannot do this. So don't use properties. – iperov Dec 09 '22 at 07:54
3

You can use the magic methods __getattribute__ and __setattr__.

class MyClass:
    def __init__(self, attrvalue):
        self.myattr = attrvalue
    def __getattribute__(self, attr):
        if attr == "myattr":
            #Getter for myattr
    def __setattr__(self, attr):
        if attr == "myattr":
            #Setter for myattr

Be aware that __getattr__ and __getattribute__ are not the same. __getattr__ is only invoked when the attribute is not found.

Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
0
class ChangingPassword(object):
    def __init__(self, username, password):
        """use _ for change to read only type(protected)."""
        self.username = username
        self._password = password

    def username(self):
        return self.username

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, new_password: int):
        if isinstance(new_password, int):
            if self._password != new_password:
                self._password = new_password
            else:
                raise ValueError('Enter different value!')


user01 = ChangingPassword('Herment', 1321)
print(user01.password)
user01.password = 6301
print(user01.password)
toyota Supra
  • 3,181
  • 4
  • 15
  • 19
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Diego Borba Aug 25 '23 at 01:50