Doubt about the __dict__ method in Python

Asked

Viewed 715 times

1

Well, I’m a beginner in Python programming and started studying object orientation.

I came across the following code, which is very simple.

class Pessoa:
    def __init__(self, nome, idade, altura):

        """Método construtor da classe Pessoa"""

        self.__nome = nome
        self.__idade = idade
        self.__altura = altura

pessoa1 = Pessoa('Fulano', 32, 1.86)

print(pessoa1.__dict__)

I had the following return:

{'_Pessoa__nome': 'Fulano', '_Pessoa__idade': 32, '_Pessoa__altura': 1.86}

Is there any way to make this output stay formatted as below?

{'Nome': 'Fulano', 'Idade': 32, Altura': 1.86}

I speak this for visibility issues for the user.

  • Got, reading the dictionary and printing the way you want.

  • Wouldn’t it be better to use the method __str__ than using the dictionary with __dict__?

1 answer

3

__dict__ is not a method - is an attribute - and is the way implemented, officially, in the language of storing instance attributes in objects.

It is a common dictionary, and you can use any code that would operate in a common dictionary with the __dict__ one-instance.

Your use to directly access the attributes content of the instance is not encouraged - even if it has no side effects. As a rule, you can say: if you don’t need it, don’t use it directly .__dict__. (There are other ways to store attribute values in instances - and how this is done can be customized with the use of property or by implementing __setattr__ and __getattribute__ or with the use of __slots__, for example.

Now, your example has another thing that draws attention - is the use of the two __ to prefix your attributes. Rather older Python material (and perhaps some more recent material from those who have studied this ancient material), usually says that "using two '__' is the Python equivalent of having a private attribute, as it exists in other object-oriented languages". That statement, or variations of it, are essentially wrong.

The use of two __ It does fire an automatic rename effect of attributes and methods, but that most likely will cause you problems than help you. Python simply does not have the concept of private attributes supported by language itself - what exists is the convention that attributes and methods begin with a single _ are private, in the sense that they should not be used except for the same project that implements the class (and not for other projects that make use of that class).

In other words: the use of __ for "private attributes" is more a functionality created by someone who missed private things for more than 25 years, and ended up in a dead end that is hardly used in serious projects.

what the use of "__" does: (I write more below to not stay before more important things) I suggest heavily that you aggregate more language learning sources and don’t try to artificially use private attributes until you understand well how language works.

Customizing the representation of a class

That said, I recommend you focus on really cool language features and features - for example, if you create the special method __repr__ he will be called to convert the instances of your class for a string for viewing purposes.

In your case, it could be so:

class Pessoa:

    def __init__(self, nome, idade, altura):
        """Método construtor da classe Pessoa"""
        self.nome = nome
        self.idade = idade
        self.altura = altura

    def __repr__(self):
        return f"Pessoa(nome={self.nome}, idade={self.idade}, altura={self.altura})"

With that, simply print(pessoa) will give a very readable output, as the Voce wants.

If you don’t want to keep repeating this for each class and each attribute, you can create a base class with these features - something like:

class AutoAttributes:
    attrs = ()

    def __init__(self, **kwargs):
        """Método construtor genérico"""
        for attr in self.attrs:
            if attr in kwargs:
                setattr(attr, kwargs[attr])


    def __repr__(self):
        body = [f"{attr}={getattr(self, attr, None)}" for attr in self.attrs]
        return f"{self.__class__.__name__}({', '.join(body)})"


class Pessoa(AutoAttributes):

    attrs = ['nome', 'idade', 'altura']

And then yes, you’ll be studying Python, and the good parts of object orientation - seeing how "inheritance" helps.

But after learning and understanding, Python 3.7 add a feature that does what I do in the Base class above, and goes well beyond, with several other facilities for automatic attributes - see the module documentation dataclasses (exists only from Python 3.7)

What "__" really does:

(remembering: as much as it is a "legal" mechanism, I do not personally recommend its use)

This triggers a "name mangling" mechanism that is "name mess" of attributes and methods. This is a mechanism that happens at compile time of the file ". py" (yes, Python is compiled for a special bytecode, just like Java and C# - only this happens in a transparent step for all users. In Java and C# it is necessary to call an explicit compilation step, which nowadays is hidden by Ides that do everything automatically). The purpose of emulating private attributes with this "name mangling" has never been to "not allow users outside the class to access attributes" - as it is taught briefly in O.O. classes - this gives no security for any code - is only so that an intermediate layer of a complex class hierarchy can have attributes that will not be over-written unintentionally by subclasses who inherit the same.

Let’s say I have a really big project, and in the middle, I have a class that has a validation layer with a method validate which will validate the connection to the database available to the instance. At another point in the class hierarchy, I’ll have another class to use, which will have a method validate to validate the same attributes (let’s assume that there were to check rules for the attributes "name", "age" of your person). In a single project, with non-cooperative methods, the validate of this other subclass would overlap with the first validate that would never be called. If the convention in that project would always use the __, then the method would have the real name of _NetworkMixin__validate and the other could have ended up with the name of _AttributeMixn_validate and the two methods could be called independently.

In practice, it was noted that it is preferable (1) not to have hierarchies of such complex classes, with hundreds of methods competing with each other within the same object - and yes, simpler and generic objects that can be passed as parameters to others who do things as distinct as "validate the network connection". (2) using collaborative inheritance always using the super() to call the methods of all superclasses - all methods that have the same name are called if this is well done.

  • Well, it’s not long since I started to study Python, this code is not mine, it’s from a material I’m studying and in this I was, "playing" with the code.

  • 1

    as I said - some material or outdated, or written by those who learned from an unofficial source and did not update themselves.

Browser other questions tagged

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