Home > Blockchain >  Pythonic Way of Handling Attributes in OOP
Pythonic Way of Handling Attributes in OOP

Time:12-08

I was tasked with creating a program using OOP, reading the documentation they gave me about this task, I have noticed many code examples that use this naming convention:

class User:
    def __init__(self, x, y, z):
        self.__x = x
        self.__y = y
        self.__z = z

    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y

    @property
    def z(self):
        return self.__z

Is this the pythonic way? I'd go instead with plain attributes, like this:

class User:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

I don't have much experience with OOP so maybe I'm missing something, in the first example, you have naming mangling protecting the attributes, but I don't know why it is even necessary, as far as I've seen all the modifications to the class attributes are only made within each class, and not from outside. Any ideas on why would the first example make more sense?

CodePudding user response:

If it's a private property for inner class usage, it's actually the way python is written its own classes and it's called private name mangling :

see The Python Language Reference for more information:

Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam. This transformation is independent of the syntactical context in which the identifier is used. If the transformed name is extremely long (longer than 255 characters), implementation defined truncation may happen. If the class name consists only of underscores, no transformation is done.

CodePudding user response:

This pattern:

class User:
    def __init__(self, x, y, z):
        self.__x = x

    @property
    def x(self):
        return self.__x

Makes more sense if you want to expose x for reading, but don't want people accidentally setting it.

You can of course add a setter:

    @x.setter
    def x(self, value):
        self.__x_was_set = True
        self.__x = value

That makes more sense if you need some side effect of setting a property to occur (I added a trivial one with __x_was_set, but it could be anything and something quite complicated).

If you don't need either, just doing this makes more sense:

class User:
    def __init__(self, x):
        self.x = x

Or you could even use a dataclass:

@dataclass
class User:
    x: str

(or whatever type you need for x of course)

I find that developers coming from languages like Java or C# tend to overuse the getter/setter pattern, because it's what they're more used to. I'd say that just from a Python-perspective simpler and more readable is better. More so because reading and setting user.__x is still possible, so no actually protection is added, it's just adding code.

  • Related