Search code examples
pythonpython-3.xtypescasting

Type casting (multiple parameters of the same type)


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.


Solution

  • 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