I took a look at this question but it doesn't exactly answer my question. As an example, I've taken a simple method to print my name.
def call_me_by_name(first_name):
print("Your name is {}".format(first_name))
Later on, I realized that optionally, I would also like to be able to print the middle name and last name. I made the following changes to accommodate that using **kwargs fearing that in the future, I might be made to add more fields for the name itself (such as a 3rd, 4th, 5th name etc.)
I decided to use **kwargs
def call_me_by_name(first_name,**kwargs):
middle_name = kwargs['middle_name'] if kwargs.get('middle_name') else ""
last_name = kwargs['last_name'] if kwargs.get('last_name') else ""
print("Your name is {} {} {}".format(first_name,middle_name,last_name))
My only concern here is that as I continue to implement support for more names, I end up writing one line of code for every single keyword argument that may or may not come my way. I'd like to find a solution that is as pythonic as possible. Is there a better way to achieve this ?
EDIT 1
I want to use keyword arguments since this is just an example program. The actual use case is to parse through a file. The keyword arguments as of now would support parsing a file from
1) A particular byte in the file.
2) A particular line number in the file.
Only one of these two conditions can be set at any given point in time (since it's not possible to read from a particular byte offset in the file and from a line number at the same time.) but there could be more such conditions in the future such as parse a file from the first occurrence of a character etc. There could be 10-20 different such conditions my method should support BUT only one of those conditions would ever be set at any time by the caller. I don't want to have 20-30 different IF conditions unless there's no other option.
You have two separate questions with two separate pythonic ways of answering those questions.
1- Your first concern was that you don't want to keep adding new lines the more arguments you start supporting when formatting a string. The way to work around that is using a defaultdict
so you're able to return an empty string when you don't provide a specific keyword argument and str.format_map
that accepts a dict as a way to input keyword arguments to format. This way, you only have to update your string and what keyword arguments you want to print:
from collections import defaultdict
def call_me_by_name(**kwargs):
default_kwargs = defaultdict(str, kwargs)
print("Your name is {first_name} {second_name} {third_name}".format_map(default_kwargs))
2- If, on the other hand and answering your second question, you want to provide different behavior depending on the keyword arguments, like changing the way a string looks or providing different file lookup functionalities, without using if statements, you have to add different functions/methods and call them from this common function/method. Here are two ways of doing that:
OOP:
class FileLookup:
def parse(self, **kwargs):
return getattr(self, next(iter(kwargs)))(**kwargs)
def line_number(self, line_number):
print('parsing with a line number: {}'.format(line_number))
def byte_position(self, byte_position):
print('parsing with a byte position: {}'.format(byte_position))
fl = FileLookup()
fl.parse(byte_position=10)
fl.parse(line_number=10)
Module:
def line_number(line_number):
print('parsing with a line number: {}'.format(line_number))
def byte_position(byte_position):
print('parsing with a byte position: {}'.format(byte_position))
def parse(**kwargs):
return globals()[next(iter(kwargs))](**kwargs)
parse(byte_position=29)
parse(line_number=29)