Pythonic way of defining setters and getters

10

Searching the internet I see that it is highly recommended not to use Setter and Getters in Python classes, however, I can not determine a way to access private values without using them. There are several recommendations on using properties, but I can not understand the advantage because I do not see the possibility of determining a method for all the attributes, and yet I would have to determine a method for each one (or I'm not understanding its use). Example:

class Pessoa(object):
    def __init__(self, nome, idade, salario)
        self.__nome = nome
        self.__idade = idade
        self.__salario = salario

    def nomeGet(self):
        return self.__nome

    def nomeSet(self, nome):
        self.__nome = nome

    def idadeGet(self):
        return self.__idade

    def idadeSet(self, idade):
        self.__idade = idade

    def salarioGet(self):
        return self.__salario

    def salarioSet(self, salario):
        self.__salario = salario

I know that it is not considered good practice in Python to define so many access methods, but as I mentioned before I have no idea how to avoid this, the image I have using property would be the same way, the difference would be instantiating the property for each attribute. Could someone explain to me the best way to work with more than one attribute, or why would it be better to use properties?

    
asked by anonymous 28.02.2017 / 05:32

3 answers

12

Let's break it down:

When "the whole internet" says one thing and you do not want to do the same thing - who do you think is going against it?

In Python there is no custom to use getters and setters, because you have to think differently. When you speak of "private attributes" - are they private to "who"?

The idea of encapsulation in OO is that people using their class and pubic methods do not need to worry about private states, nor should they try to manipulate them directly. This makes it easy for you when you develop the class you do not have to worry about "what if someone leaves this attribute inconsistent" between two method calls, and whoever is using it does not have to worry about "if I change that value, something in the functioning of the object "?

The purpose of private attributes is NOT to prevent someone using your class from reading some value that you consider private for security reasons to protect yourself from something like a "malicious programmer" making use of your class.

Because languages such as Java and C ++ are defined, it gives the impression that private attributes and methods can provide security against a malicious programmer who is using your class. This security is not real - in both languages it is possible to access these private attributes - sometimes taking many turns.

In Python and other dynamic languages, the use of introspection makes it very easy to find and use any attribute marked as private (even those prefixed with __ )

In short: if someone is writing code that will run in the same process as your class with private attributes - it can and should be able to access the data. It is different from the case of a programmer on a system other than yours that will access your data by an API. In this case, private attributes are simply not exposed in the API. In Python, instead of trying to force unreachable private attributes, it is customary to say that language is used by "consenting adults."

So, the practice is to prefix private attributes and methods with a single _ : so whoever uses the class knows that it does not have to tinker with those attributes.

Now, Python has a very powerful mechanism for attribute access, which we call the "descriptor protocol" - this is what is used internally by property to allow you to create methods - it goes a long way in addition to what proeprt allows (basically you can define attributes in a class that automatically have custom getters and setters - just create a special class for these attributes with the methods __get__ , __set__ or __del__ - the property does this) .

That said, the property is a facilitator for you, when you want to read or write an attribute, run some custom code that can transform or validate the data of that attribute when it is accessed (for example, read the value of a database format, validate the attribute type, etc ...).

Using the property (or getters and setters) to simply save the value as it came and return it as it came, does not make sense - unless, for example, you want the class to be thread safe and use this code to use locks and semaphores in changing the attributes. And it is by "making no sense" that the internet recommends not to do it - simply because your program will be exactly even with or without getters and setters -

Without the property:

class Teste:
    def __init__(self, valor):
        self.valor = valor

With the property:

class Teste:
    def __init__(self, nome):
         self.nome = nome

    @property
    def nome(self):
         # Este código é executado quando alguém for
         # ler o valor de self.nome
         return self._nome

    @nome.setter
    def nome(self, value):
         # este código é executado sempre que alguém fizer 
         # self.nome = value
         self._nome = value

And then - who will use your code, will not use a chaos to the "setter" - the magic of Python is that if you need to put custom logic in the getter and setter, this is completely transparent to anyone who uses your class and its attributes. If no extra logic is required for a given attribute, you do not have to create a property for it.

    
01.03.2017 / 16:41
7

There is exactly this question in the SO EN .

If you really need setters and getters, you should use them through property , eg:

class Pessoa(object):
    def __init__(self, nome, idade, salario):
        self._nome = nome
        self._idade = idade
        self._salario = salario

    @property
    def nome(self):
        print('get do nome')
        return self._nome

    @nome.setter
    def nome(self, nome):
        print('set do nome', nome)
        self._nome = nome

    @property
    def idade(self):
        print('get da idade')
        return self._idade

    @idade.setter
    def idade(self, idade):
        print('set da idade', idade)
        self._idade = idade

    @property
    def salario(self):
        print('get do salario')
        return self._salario

    @salario.setter
    def salario(self, salario):
        print('set do salario', salario)
        self._salario = salario


pessoa = Pessoa('Miguel', 30, 50)
print(pessoa.__dict__) # valores iniciais
pessoa.nome = 'Afonso'
pessoa.idade = 20
pessoa.salario = 500
print(pessoa.nome)
print(pessoa.idade)
print(pessoa.salario)

This is useful if you want some code to be executed (such as, for example, the prints above that are inside the methods) when you do set, get, del on some property.

Looking at further searches and articles written by people who know more than I fully agree.

  

...

...

What translated is:

  

...

...

...

...

...

(my example above)

And here is an example, given your code, of what I would do (if I did not need anything extra to run in a get / set / del ), try to keep as simple as possible :

class Pessoa(object):
    def __init__(self, nome, idade, salario):
        self.nome = nome
        self.idade = idade
        self.salario = salario

pessoa = Pessoa('Miguel', 30, 50)
print(pessoa.__dict__) # valores iniciais
pessoa.nome = 'Afonso'
pessoa.idade = 20
pessoa.salario = 500
print(pessoa.__dict__) # novos valores
print(pessoa.nome)
print(pessoa.idade)
print(pessoa.salario)

In this last example I might add only the methods for return, (def get_name, ...) for the sake of readability, but it is not at all necessary / convention.

"Beauty of Python Simplicity, Do not Waste It"

    
28.02.2017 / 10:13
0
class Pessoa(object):

    def __init__(self, nome, idade, salario):
        self.__nome = nome
        self.__idade = idade
        self.__salario = salario

    def nomeGet(self):
        return self.__nome

    def nomeSet(self, nome):
        self.__nome = nome

    def idadeGet(self):
        return self.__idade

    def idadeSet(self, idade):
        self.__idade = idade

    def salarioGet(self):
        return self.__salario

    def salarioSet(self, salario):
        self.__salario = salario
print(Pessoa)        
    
21.10.2018 / 17:37