I think the title gives the general idea of what I am looking for, but to be more specific I will give an example with code.
So let's say I have a Python class with a few required position variables that also takes an arbitrary number of keyword arguments. The class has many data members, and some of them will be defined by the required position variables, but most are keyword argument variables where the program has default values for these variables, but if the user uses a keyword argument this will override the defaults. I am looking for the most "pythonic" way to initialize a class of this type. I have two ideas for how to do this, but each of them feels unsatisfying and like there is a more pythonic way I am missing.
#First Option
class SampleOne:
def __init__(pos1, pos2, **kwargs):
def do_defaults():
self.kwarg1 = default_kwarg1
self.kwarg2 = default_kwarg2
self.kwarg3 = default_kwarg3
def do_given():
for variable, value in kwargs.items():
self.variable = value
self.pos1 = pos1
self.pos2 = pos2
do_defaults()
do_given()
Or
#Second Option
class SampleTwo:
def __init__(pos1, pos2, **kwargs):
self.pos1 = pos1
self.pos2 = pos2
self.kwarg1 = kwargs[kwarg1] if kwarg1 in kwargs else default_kwarg1
self.kwarg2 = kwargs[kwarg2] if kwarg2 in kwargs else default_kwarg2
self.kwarg3 = kwargs[kwarg3] if kwarg3 in kwargs else default_kwarg3
I don't love the first option because it seems wasteful to set a bunch of default data members if a bunch are going to be changed, especially if there are many data members.
I don't love the second option because it looks unnecessarily busy and less readable in my opinion - I like the separation of the default values from the user-defined values and think it will make my code easier to read and change.
Also, I am using **kwargs instead of keyword arguments with default values because I am still in the early phase of the development of this codebase so the member variables needed are subject to change, but also because there is going to be a lot of member variables and it will make the function signature very ugly to have all of those parameters.
Apologies if my question is a bit long-winded, this is one of my first times asking questions on StackOverflow and I wanted to make sure I gave enough detail.
Also if it makes a difference my code needs to work in Python 3.8 and later.
CodePudding user response:
Here's one possible solution:
class Solution:
def __init__(self, pos1, pos2, **kwargs):
self.pos1 = pos1
self.pos2 = pos2
for key, value in kwargs.items():
if key not in self.__dict__:
self.__dict__[key] = value
else:
raise ValueError(f"{key} already exists in Solution")
You will need to be careful not to overwrite methods or existing variables in the class so I made it throw an exception in case you are trying to do that.
Example usage:
>>> solution = Solution(1, 2, kwarg1=1, kwarg2=2, kwarg3=3, kwarg4=4)
>>> print(solution.__dict__)
{'pos1': 1, 'pos2': 2, 'kwarg1': 1, 'kwarg2': 2, 'kwarg3': 3, 'kwarg4': 4}
>>> solution.kwarg4
4
CodePudding user response:
Specify the defaults you want to support and take arbitrary ones that don't require a default. This keeps your classes readable and the implementation (relatively) clear:
class Sample:
def __init__(
self,
pos1,
pos2,
kwarg1='default_1',
kwarg2='default_2',
kwarg3='default_3',
**kwargs
):
self.pos1 = pos1
self.pos2 = pos2
self.kwarg1 = kwarg1
self.kwarg2 = kwarg2
self.kwarg3 = kwarg3
for k, v in kwargs.items():
setattr(self, k, v)