In this case, the important thing is to understand when each of the calls is made, and how everything is assembled together.
Don’t try to use __
for private variables
Before addressing the central point however, I draw attention to one thing - the use of __
, two underscores, as a prefix of a name in a class, cause a "name mangling" of the name, but this is not the same as a "private variable". Older Python documentation (things older than 10 years) tend to say that the use of two underscores is the same as a "private" variable as exists in Java or other languages - this is not true - in Python there is no concept of private variables, except that an attribute should not be modified by users of the class - and this is simply a convention. Two __
activate a variable name modification mechanism at compile time (yes, Python code is compiled despite this step being transparent to the developer). In that specific case, it will only cause your variable self.__name
in the inherited class is different of the variable self.__name
in the mother class - if you kept your code and changed only the Setter, the getter would try to read a variable that does not exist.
What is an object returned by property
In Python, the built-in function "Property" returns an object that is a Descriptor. This is an object that implements a method among __get__
, __set__
or __delete__
- and it is these methods on an object that is a class attribute that cause the manipulation of the same attribute in the instance follow rules other than those of normal attributes. In practice, Property is just a "tidy" way of dynamically creating a Descriptor - what matters is that it exists as a class attribute.
So even if it were possible to change the fget
of a Property object (and it’s not - it exists as a "read only" attribute in the object), if you did that in the daughter class, it would change the Property in the parent class - it’s the same object - and the behavior would be changed for all instances both of the parent class, how much of the son, how much of other subclasses of the father.
1st form - Modifying properties in inheritance - hard-coding the getters and setters
Perhaps the simplest way to change a legacy Property is simply to create a new, zeroed Property - it will remain a separate, independent class attribute in the daughter class.
The way you are doing it has the advantage that your getter and Setter methods are "standard" methods. (If you use the Property in Decorator form, these methods are "hidden" and you could not use them).
The problem is that you can’t just put a line like:
name = property(fget=get_name, fset=set_name)
In the body of the daughter class if the get_name
is not set in the daughter class either. And as you noticed, it is not possible to call super()
in the class name.
This would work here - Python will find and create the Property:
class Employee(Person):
...
def set_name(self, value):
...
name = property(fget=Person.get_name, fset=set_name)
In Python 3, normal methods (which are used in the instance), are functions without anything special when retrieved as class attributes. "Property" on the other hand wants exactly this as parameters: normal functions - it will take charge of inserting the parameterself
when these functions are called.
The only drawback of this method is that you are required to fix the name of the class where the original getter is - if you are using a multiple heritage architecture, you may have problems there.
2nd form - Modifying properties in inheritance - __init_subclass__
A more elegant way may be to use this functionality introduced in Python 3.6: a class when inherited will have the method __init_subclass__
called with the newly created class as parameter. This special method is a class method that is only called once for each new inherited class.
So it’s possible Recreate the Property within the method __init_subclass__
- in this case, the normal class attribute access rules will find the methods desired for your getter and Setter - and you don’t have to worry about marking them explicitly as part of a Property in each new class created.
Your root class would look like this:
class person(...):
@abstractmethod
def __init__(self, name, telephone):
self.name = name
self.telephone = telephone
def __init_subclass__(cls, **kw):
cls.name = property(cls.get_name, cls.set_name)
super().__init_subclass__(**kw)
def get_name(self) -> str:
return self._name
def set_name(self, value: str):
self._name = value
name = property(fget=get_name, fset=set_name)
And then, any daughter class you override get_name
either of set_name
will get a new property and override rules will work normally for access to Property name
- he will be recreated with the visible method in that descending class.
In the case of the 2nd Way, to override the
get_name
or aset_name
in the daughter class, would it be enough to recreate the function without the need to clarify the creation of the new property? That is, in the daughter class would no longer need the linename = property(fget=Person.get_name, fset=set_name)
, simply recreate the functionset_nome
.– Matheus Saraiva
Exactmistress - the function
__init_subclass__
will be automatically called by the language, and will receive the child class as a parameter. When referring tocls.get_name
, normal inheritance attribute access mechanisms will apply, and the most derived methods, even in a complicated hierarchy, will be used automatically.– jsbueno
Hello John. Sorry for the delay in feedback. I performed a test following the instructions you left on "2nd form". The first mistake I had was that
super().__init_subclass()
does not receive arguments. I confirmed this in doc. That’s why I’m making the mistake "Typeerror: init_subclass() takes in Arguments (1 Given)", I tried to follow the example of docsuper().__init_subclass__(**kw)
, then I have no error but the function is not overwritten. Follow the code– Matheus Saraiva
Find the problem. Is that at the initialization of my object, ie in
__init__
the assignment is not going through the proprerty. For this to happen just change the first line of the__init__
ofPerson
ofself._name = name
forself.name = name
. With respect to functionsuper().__init_subclass__
, really she gets no arguments.– Matheus Saraiva
Ah yes - indeed, there was a
cls
remaining in the call tosuper()
of__init_subclass__
. It is automatically added bysuper()
.– jsbueno