Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance best practice : *args, **kwargs or explicitly specifying parameters

I often find myself overwriting methods of a parent class, and can never decide if I should explicitly list given parameters or just use a blanket *args, **kwargs construct. Is one version better than the other? Is there a best practice? What (dis-)advantages am I missing?

class Parent(object):      def save(self, commit=True):         # ...  class Explicit(Parent):      def save(self, commit=True):         super(Explicit, self).save(commit=commit)         # more logic  class Blanket(Parent):      def save(self, *args, **kwargs):         super(Blanket, self).save(*args, **kwargs)         # more logic 

Perceived benefits of explicit variant

  • More explicit (Zen of Python)
  • easier to grasp
  • function parameters easily accessed

Perceived benefits of blanket variant

  • more DRY
  • parent class is easily interchangeable
  • change of default values in parent method is propagated without touching other code
like image 491
Maik Hoepfel Avatar asked Jan 31 '13 12:01

Maik Hoepfel


People also ask

When should I use Kwargs?

It is often used if you want to pass lots of arguments to another function where you don't necessarily know the options. For instance, specialplot(a,**kwargs) can pass plot options in kwargs to a generic plot function that accepts those options as named parameters.

Can you have Kwargs without args?

Python is pretty flexible in terms of how arguments are passed to a function. The *args and **kwargs make it easier and cleaner to handle arguments. The important parts are “*” and “**”. You can use any word instead of args and kwargs but it is the common practice to use the words args and kwargs.

How do you beat Kwargs argument?

Kwargs allow you to pass keyword arguments to a function. They are used when you are not sure of the number of keyword arguments that will be passed in the function. Kwargs can be used for unpacking dictionary key, value pairs. This is done using the double asterisk notation ( ** ).

Why do we use Kwargs?

The special syntax **kwargs in function definitions in python is used to pass a keyworded, variable-length argument list. We use the name kwargs with the double star. The reason is that the double star allows us to pass through keyword arguments (and any number of them).


2 Answers

My choice would be:

class Child(Parent):      def save(self, commit=True, **kwargs):         super(Child, self).save(commit, **kwargs)         # more logic 

It avoids accessing commit argument from *args and **kwargs and it keeps things safe if the signature of Parent:save changes (for example adding a new default argument).

Update : In this case, having the *args can cause troubles if a new positional argument is added to the parent. I would keep only **kwargs and manage only new arguments with default values. It would avoid errors to propagate.

like image 24
luc Avatar answered Sep 21 '22 08:09

luc


Liskov Substitution Principle

Generally you don't want you method signature to vary in derived types. This can cause problems if you want to swap the use of derived types. This is often referred to as the Liskov Substitution Principle.

Benefits of Explicit Signatures

At the same time I don't think it's correct for all your methods to have a signature of *args, **kwargs. Explicit signatures:

  • help to document the method through good argument names
  • help to document the method by specifying which args are required and which have default values
  • provide implicit validation (missing required args throw obvious exceptions)

Variable Length Arguments and Coupling

Do not mistake variable length arguments for good coupling practice. There should be a certain amount of cohesion between a parent class and derived classes otherwise they wouldn't be related to each other. It is normal for related code to result in coupling that reflects the level of cohesion.

Places To Use Variable Length Arguments

Use of variable length arguments shouldn't be your first option. It should be used when you have a good reason like:

  • Defining a function wrapper (i.e. a decorator).
  • Defining a parametric polymorphic function.
  • When the arguments you can take really are completely variable (e.g. a generalized DB connection function). DB connection functions usually take a connection string in many different forms, both in single arg form, and in multi-arg form. There are also different sets of options for different databases.
  • ...

Are You Doing Something Wrong?

If you find you are often creating methods which take many arguments or derived methods with different signatures you may have a bigger issue in how you're organizing your code.

like image 135
dietbuddha Avatar answered Sep 19 '22 08:09

dietbuddha