Yes -
The easiest way to do it is not perfect - but it's enough for most cases -
This is to make use of the mechanism that the language has to write functions that receive an arbitrary number of named parameters - for this, just precede the name of a parameter with two asterisks - **
(in almost all code Python you look at, this parameter will be called kwargs
- short for "Keyword arguments" - or shortened to kw
- however this is just a naming convention - the important thing is two **
.)
To shorten, your derived class above might look like this:
from Testes import POO
class Fisica(POO.Cliente):
def __init__(self,sexo, **kwargs):
super().__init__(**kwargs)
self.sexo = sexo
Ready - what this means: The parameter sexo
is required for the __init__
method of this subclass, and it does not mind receiving any other named parameters.
The kwargs
parameter within this method will be a Python dictionary - normal, containing all the other arguments passed. You can inspect it, change it, etc ... (try putting print(kwargs)
, for example).
And then, in the call to super()...
the same kwargs is used in the reverse mechanism: when we put **
in the prefix of an argument in the call of a function or method, it assumes that the variable after **
is a mapping, a dictionary is a specific type of map) - and unfolds the contents chave: valor
of that map as parameters and arguments to the called function.
In this case, if the Physical class was called with Fisica(sexo='F', nome='Simone', idade=35)
, kwargs would be a dictionary containing {'nome': 'Simone', 'idade': 35}
, and super().__init__(**kwargs)
would be equivalent to super().__init__(nome='Simone', idade=35)
It is important to note that this syntax with two "**" requires arguments to be passed with a name - so Python can create a dictionary with par and value keys: ideally the call must be Fisica(sexo='F', nome='Simone', idade=35)
. if it is desired to work with positional parameters. i.e.% with%, only one "*" should be used, and the parameter received with the other arguments in the order they were passed is a tuple, not a dictionary. Just because the signature with all possible / desired parameters is not explicit in the subclass, it is most recommended if working with named arguments, so no one needs to "guess" the send order, and calls can be in arbitrary order.
Note that this mechanism is very flexible - if you prefer, you can simply put Fisica('F', "Simone', 35)
and no more parameters in the method definition, and extract the data used by each subclass of the dictionary itself, using the **kwargs
method, for example (which retrieves a value from the dictionary and removes the corresponding key / value pair):
class Fisica(POO.Cliente):
def __init__(self,sexo, **kwargs):
self.sexo = kwargs.pop('sexo', '-')
super().__init__(**kwargs)
(The second parameter for pop is the default value - in case, if the person did not pass "sex" as an argument, the string '-' would be used).
Why did I say that this is not perfect?
Just because someone who inspects the .pop()
method of the subclass can not see the names and specifications of the parameters in the superclass - only "sex" and "kwargs" would appear. So an IDE that relies on this to help you with the auto-complete, for example, will get lost.
A good recommendation in these cases is to document the extra parameters you know to be needed in the doc-string of the class.
Python's introspection mechanisms would allow the creation of a decorator for the class, for example, that could change the signature of the methods with kwargs within a class to reflect the parameters of the super-class - but that would be something very advanced, and complex. There may even be some external module that already does this.