I have the following method declaration:
def method(alpha, size=10, logged_in=False, interactions=False):
if not isinstance(logged_in, bool):
logged_in = str(logged_in).lower() in ['true']
if not isinstance(interactions, bool):
interactions = str(interactions).lower() in ['true']
The if
statements are needed because, for example, interactions
would be falsely set to True
if I pass interaction="False"
as a parameter (so, I pass a String). While the above does the trick, I wonder if there is a more elegant way to achieve it (note that I have a lot of boolean parameters, beside logged_in
and interactions
, and I also have some int
and float
parameters. How could decorators be used here? Could one decorator be used for all bool
parameters, and how could I make a general one (i.e., supporting bool
, float
, int
, string
)? I found the answer to this question potentially useful, but still wanted to check if there are better ways.
CodePudding user response:
You could try something like this.
def bool_fixer(func):
table = {"true": True, "false": False}
def wrapper(*args, **kwargs):
for key, val in kwargs.items():
if isinstance(val, str) and val.lower() in table:
kwargs[key] = table[val.lower()]
return func(*args, **kwargs)
return wrapper
@bool_fixer
def method(...):
do something
after the decorator does its magic then all of the string "true" and "false" will turn into their python bool
equivalent and you can skip all the checking in the method
function.
If you want it to cover ints
and floats
too:
import re
def converter(func):
patterns = {r"\d*": int, r"\d*?\.\d*": float}
table = {"true": True, "false": False}
def wrapper(*args, **kwargs):
for key, val in kwargs.items():
if isinstance(val, str):
if val.lower() in table:
kwargs[key] = table[val.lower()]
continue
for pattern in patterns:
if re.match(pattern, val):
kwargs[key] = patterns[pattern](val)
return func(*args, **kwargs)
return wrapper
or
def converter(func):
table = {"true": True, "false": False}
def wrapper(*args, **kwargs):
for key, val in kwargs.items():
if isinstance(val, str):
if val.lower() in table:
kwargs[key] = table[val.lower()]
elif val.isdigit():
kwargs[key] = int(val)
elif re.match(r"\d*?\.\d*", val):
kwargs[key] = float(val)
return func(*args, **kwargs)
return wrapper
CodePudding user response:
This decorator should reliably handle bools, ints and floats:
def cast_arguments(method):
"""decorator"""
def wrapper(*args, **kwargs):
for key, value in kwargs.items():
# change special strings to bools
if isinstance(value, str):
if value.lower() == "true":
kwargs[key] = True
elif value.lower() == "false":
kwargs[key] = False
else:
try:
# change ints to int (int cannot have ".")
kwargs[key] = int(value)
except ValueError:
try:
# change floats to float (can have ".")
kwargs[key] = float(value)
except ValueError:
# this is an actual string
pass
return method(*args, **kwargs)
return wrapper
class Test:
@cast_arguments
def method(self, logged_in=False, interactions=False, other=False):
print("logged_in: ", logged_in, type(logged_in))
print("interactions: ", interactions, type(interactions))
print("other: ", other, type(other))
c = Test()
c.method(logged_in="1", interactions="True", other="3.14")
output:
$ python test.py
logged_in: 1 <class 'int'>
interactions: True <class 'bool'>
other: 3.14 <class 'float'>