How to access a private attribute in Python, I created an object from another class that has private attributes as well

Asked

Viewed 299 times

0

I created an object from the class Especificacoes, and by creating a class obejto Geradores, I passed the object created from Especificacoes as a parameter for class Geradores. Note that the attribute self.__especificacoes receives as value an object that has several private attributes. How to access them? I cannot.

class Geradores:
  def __init__(self, nome, especificacoes):
    self.__nome = nome
    self.__especificacoes = especificacoes
    self.__qtd_tanque = 400
    self.__liga_desliga = 'Desligado'


  def get_especificacoes(self):
    return self.__especificacoes


class Especificacoes:
  def __init__(self, potencia, capac_ger_energia, tamanho_tanque):
    self.__potencia = potencia
    self.__capac_ger_energia = capac_ger_energia
    self.__tamanho_tanque = tamanho_tanque


espec_g1 = Especificacoes(95, 8000, 500)
g1 = Geradores('G1', espec_g1)

print(g1.get_especificacoes())
  • Important you [Dit] your post and explain in detail the problem, describing what you tried and where is the current difficulty, preferably with a [mcve]. Studying the post available on this link can make a very positive difference in your use of the site: Stack Overflow Survival Guide in English

  • If you want to access a private attribute, we usually choose to create a getter, so the access is read-only.

  • There are actually no private fields in Python. What happens is that when the name starts with two _, he activates the name mangling, that "internally changes" attribute names. But it is still possible to access them, try to do print(g1.get_especificacoes()._Especificacoes__potencia) - or g1.get_especificacoes()._Especificacoes__potencia = 10 to change the value, see. Create a getter, as suggested above, has more to do with "facilitate" access, but does not prevent the value from being changed... I suggest a read in this answer to learn more

1 answer

2

First, understand that there are actually no private fields in Python. This is said in documentation (my emphasis):

"Private" instance variables that cannot be accessed except from Inside an Object don’t exist in Python.

What exists is a convention to use variable names/methods/etc starting with _ to indicate that they are not part of the public API and therefore should not be accessed directly, as they are internal implementation details that can even change without notice.

But there’s also a mechanism called name mangling for identifiers beginning with two _: any identifier with the name in the form __campo becomes _nomedaclasse__campo. But this does not make the field private, as it is still possible to access it. Ex:

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

t = Teste(10)
print(t._Teste__valor) # 10
t._Teste__valor = 'xyz'; # mudando o valor, mesmo ele sendo "privado"
print(t._Teste__valor) # xyz
# mostra os campos de t
print(vars(t)) # {'_Teste__valor': 'xyz'}


Therefore, in your case it would still be possible to access (and change) the class fields Especificacoes:

espec_g1 = Especificacoes(95, 8000, 500)
g1 = Geradores('G1', espec_g1)

print(g1.get_especificacoes()._Especificacoes__potencia) # 95

g1.get_especificacoes()._Especificacoes__potencia = 10
print(g1.get_especificacoes()._Especificacoes__potencia) # 10

But if you need to access these fields outside the class, maybe they shouldn’t be "private". Already to display the specifications, you can have a method that already returns the formatted string with the desired information:

class Geradores:
    def __init__(self, nome, especificacoes):
        self.nome = nome
        self.especificacoes = especificacoes
        self.qtd_tanque = 400
        self.liga_desliga = 'Desligado'

    # opção 1, ter um método que retorna as especificações já formatadas
    def get_specs(self):
        return f'Potência={self.especificacoes.potencia}, Capacidade={self.especificacoes.capac_ger_energia}, Tanque={self.especificacoes.tamanho_tanque}'

class Especificacoes:
    def __init__(self, potencia, capac_ger_energia, tamanho_tanque):
        self.potencia = potencia
        self.capac_ger_energia = capac_ger_energia
        self.tamanho_tanque = tamanho_tanque

    # opção 2: ao imprimir as especificações, retorna uma string formatada com os dados
    def __repr__(self):
        return f'Potência={self.potencia}, Capacidade={self.capac_ger_energia}, Tanque={self.tamanho_tanque}'

g1 = Geradores('G1', Especificacoes(95, 8000, 500))

# chamar o método get_specs(), que já traz as especificações formatadas
print(g1.get_specs()) # Potência=95, Capacidade=8000, Tanque=500

# imprimir Especificacoes diretamente, é chamado o método __repr__
print(g1.especificacoes) # Potência=95, Capacidade=8000, Tanque=500

Anyway, as in Python there is no concept of private (similar to other languages, such as Java and C#), it may be better to initially leave the fields just like that, without the _ in front of the name. So you can access them directly (ex: g1.especificacoes.potencia). Unless, of course, these fields are internal implementation details that can change without warning in the future - and in this case, maybe it’s best to use a variation of option 2 above: have a method that returns the necessary information (something like get_detalhes_espec() or something like that, which already returns the formatted data to the other classes).

And in the future, if you need any additional logic in these fields, then you can even create properties (read more on the subject here), which is a way to simulate the getter and Setter of other languages (but do not create a getter no need - if it only returns the field and nothing else, there is in fact a gain in creating the Property). And remember that, anyway, if you create just the getter, This does not prevent the field from being modified, as nothing is really private.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.