2

I want to check all of my argument values so I can convert the value to None if it's an empty string.

I figured out how to get the list of arguments, but I can't figure out how to get the value of the argument.

What I have now is this:

def __init__(self, first, middle, last):
    # convert blank attributes to None
    import inspect
    args = inspect.getargspec(self.__init__)[0]
    for arg in args:
        print 'arg: ' + str(arg)
        if arg == '':
            arg = None

This gets the argument names, but not their values. How can I get a hold of the values?

SecretAgentMan
  • 2,856
  • 7
  • 21
  • 41
ems
  • 722
  • 1
  • 9
  • 16

2 Answers2

3

With only 3 arguments, don't bother with inspecting, just refer to them directly, or use a catch-all argument *args:

def __init__(self, *args):
    args = [argument or None for argument in args]

You can access function locals through the locals() dictionary, but it is meant to be read-only, you generally should not use that to set mere defaults.

If you do want a generic approach, I'd use a decorator:

from functools import wraps

def reset_to_none(func):
    @wraps(func)
    def wrapper(*args, **kw):
        args = [arg if not isinstance(arg, str) or arg else None for arg in args]
        kw = {k: arg if not isinstance(arg, str) or arg else None for k, arg in kw.items()}
        return func(*args, **kw)
    return wrapper

then apply that to your __init__ method:

class SomeClass(object):
    @reset_to_none
    def __init__(self, first, middle, last):
        # any of `first`, `middle` or `last` being empty strings will be set to `None`
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • There are more than three arguments, I just took out the others to make the post neater. I kind of like giving explicit arguments (rather than *args) because then I get notified if I'm passing the wrong number of arguments to the function elsewhere. – ems Jan 05 '14 at 18:33
  • Dang, I just wanted to post an answer with decorators myself. +1 – tobias_k Jan 05 '14 at 18:34
  • @glglgl: that won't work for instances that have a `__bool__` or `__nonempty__` or `__len__` method returning `False`. The first argument to methods is `self`, so `self.__len__()` could be consulted, replacing `self` with `None`. Not pretty. – Martijn Pieters Jan 05 '14 at 18:43
  • I meant it for the very first piece of code. For the next ones it is not so easy and your solution is the best. Sorry for being unclear. – glglgl Jan 05 '14 at 18:46
  • @glglgl: ah, yes, that is better there. – Martijn Pieters Jan 05 '14 at 18:47
  • Thanks. I didn't know about decorators. Could you help me understand what `args = [arg if not isinstance(arg, str) or arg else None for arg in args]` is doing? Also, is there any reason to include the kw stuff if I'm not using keyword arguments (which maybe I should be...). – ems Jan 05 '14 at 19:01
  • 2
    The first is a list comprehension replacing the original `args` sequence with one where empty strings are replaced with `None` but leaving other values untouched. You can *call* your function using keyword parameters even when the function itself only has positional parameters, and supporting it here makes the decorator useful to more than just your sample function. :-) – Martijn Pieters Jan 05 '14 at 19:19
2

You could write a decorator that converts all empty string arguments to None:

from functools import wraps

def emptyToNoneParameters (f):
    @wraps(f)
    def inner (*args, **kwargs):
        newArgs = [None if a == '' else a for a in args]
        newKwargs = {k: None if v == '' else v for k, v in kwargs.items()}
        return f(*newArgs, **kwargs)
    return inner

class MyType:
    @emptyToNoneParameters
    def __init__ (self, first, middle, last):
        print(first, middle, last)

x = MyType('a', '', '')
# prints: 'a', None, None
Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602