11

I've mostly used argparse for making command-line scripts in python, and the idiom I generally use is that I assign the arguments as attributes of an object, then parse them individually to a variable that matches their attribute name. This seems a little repetitive. Is there a way to assign them all into the global namespace and cut out the assignment step; or as is often the case when some python behavior seems counter-intuitive to me, can some wise, python expert point out that there a good reason I should not do this or want to do this?

What I have now is this:

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--db",type=str, dest='db', nargs='?', default="test")
    parser.add_argument("--collection",type=str, dest='collection', nargs='?', help="Collection, default is test", default="test")
    args = parser.parse_args()
    db = args.db                   # gross! 
    collection = args.collection   # yuck!
    print(db)
    print(collection)

What I'd like this is:

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--db",type=str, dest='db', nargs='?', default="test")
    parser.add_argument("--collection",type=str, dest='collection', nargs='?', help="Collection, default is test", default="test")
    parser.SUPER_parse_args() # now, db and collection are already in the namespace!
    print(db)
    print(collection)

It doesn't seem like much when I only have 2 arguments, but if I have 10 or so, doubling the assign steps, where I rename into the global namespace the attributes that already exist in the args object, starts to bug me.

Mittenchops
  • 18,633
  • 33
  • 128
  • 246

3 Answers3

11

You can do this using globals:

globals().update(args.__dict__)

however, you really *shouldn't do that. From the zen of python,

Namespaces are one honking great idea -- let's do more of those!

I'll echo what @Martijn said in his comment:

Don't. Just don't. I'd use args directly instead.

Keep things as nicely separated as you can. It makes for more maintainable and easier to understand code.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I see. Cool, thanks. Is the rationale for this just that having variables defined globally, and outside of args, means it's more likely to collide with new things? If so, is updating globals like this really any worse than the simple one-off python script where variables are defined in the global scope anyway? Is the idea that small scripts inevitably turn into big scripts that need smarter namespace management? – Mittenchops Oct 11 '13 at 16:55
  • 2
    @Mittenchops -- That's part of it. It also makes the source a lot easer to read. 3 months later, when you need to update your script and you see yourself using a variable `foo`, but when you try to grep the source, `foo` isn't defined anywhere, then you start to scratch your head and wonder how the whole thing works. – mgilson Oct 11 '13 at 17:22
0

To add onto mgilson's answer:

Consider using locals().update(args.__dict__) instead; this will update the current namespace rather than the global namespace.

You can also do this using locals() or vars(). See this Stack Overflow post for a nice explanation on the difference between the two.

I'm a bit late for Mittonchops, but hopefully this helps others.

Community
  • 1
  • 1
Joe
  • 11
  • Don't use locals. It won't work. :-). updating the locals dict isn't guaranteed to work on any implemention of python and doesn't work on CPython (unless you're in the globals namespace in which case `locals() == globals()` anyway :-) – mgilson Jan 28 '15 at 22:42
0

This is what I did. It helps with variable collision but not maintainability.

parser_args = parser.parse_args()
if parser_args.verbose:
    globals().update(argparse.Namespace(verbose=parser_args.verbose).__dict__)
Zhanwen Chen
  • 1,295
  • 17
  • 21