Home > OS >  Type casting (multiple parameters of the same type)
Type casting (multiple parameters of the same type)

Time:05-05

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'>
  • Related