Is there a “pythonic” approach to required properties (OOP)?

I am trying to find a "pythonic" approach to the following class organization:

I have a base class with properties initialized in its constructor, for example:

class Animal(object): def __init__(self, class_, species, is_domesticated): self.class_ = class_ self.species = species self.is_domesticated = is_domesticated 

Then, when I subclass, I would like to “hard code” one or more of these properties, for example:

 class Mammal(Animal): def __init__(self, species, is_domesticated): Animal.__init__(self, 'Mammal', species, is_domesticated) 
Thus, a mammal is created in this way:
 monkey = Mammal('Primate', false) 

The problem is that I would like to use * args to leave only derived classes when changing the definition of the base class. Thus, the definition of a Mammal is as follows:

 class Mammal(Animal): def __init__(self, *args): Animal.__init(self, *(args + (class_='Mammal',))) 

Which (needless to say) looks awful. Some tips will be appreciated =)

+4
source share
2 answers

If you only have a fixed set of arguments in the base class, there is no need to worry about variable arguments. Just do what you did in your first example, and everything is fine. If you want to be able to randomly add arguments to the base class, but add them as positional arguments and without by default, there is no hope; you cannot just change the willy-nilly base class and expect all derived classes to continue to work.

However, there is a fairly common intermediate case where you can have a large set of attributes, various combinations of which can be passed to any class in the hierarchy. You might want to add new arguments to the base class, but they will have default values ​​so that derived classes do not need them explicitly; they simply gracefully degrade to the base class by default. In this case, it is usually better to use **kwargs rather than *args .

 class Animal(object): def __init__(self, **kwargs): self.class_ = kwargs['class_'] self.species = kwargs['species'] # etc. class Mammal(Animal): def __init__(self, **kwargs): Animal.__init__(self, class_="Mammal", **kwargs) 

This requires keyword arguments:

 >>> Animal(class_='Fish', species='barracuda', is_domesticated=False) 4: <__main__.Animal object at 0x0177ABF0> >>> Mammal(species="monkey", is_domesticated=False) 5: <__main__.Mammal object at 0x0177AFB0> 

., but it’s better if there are a lot of them, because no one will remember what order to transfer them to, if you have 10 different things going through the positions. It also means that you can easily add new arguments; no one should know where there are new ones on the list, they can simply add them by keyword.

In Python 2, you need to manually extract kwargs, as I did above. In Python 3, you can use keyword-only arguments to make this even easier.

+6
source

OK, why don't you just do what you said, what you want? Make Mammal.__init__() accept the *args argument, and then use it. Here is the code:

 class Animal(object): def __init__(self, class_, species, is_domesticated): self.class_ = class_ self.species = species self.is_domesticated = is_domesticated def __str__(self): s_dom = "dom" if self.is_domesticated else "wild" return ("Animal(%s, %s, %s)" % (self.class_, self.species, s_dom)) class Mammal(Animal): def __init__(self, *args): Animal.__init__(self, 'Mammal', *args) cat = Mammal("cat", True) print(cat) lion = Mammal("lion", False) print(lion) 

Output:

 Animal(Mammal, cat, dom) Animal(Mammal, lion, wild) 
0
source

Source: https://habr.com/ru/post/1433651/


All Articles