0

I have been using the argparse for my latest project and although it is extremely helpful, I have realized that most of the time, especially in longer scripts, I try to avoid using the variable with the 'args.' prefix, so lines like this are bloating my code:

parser.add_argument('--learning_rate', '--lr', default=0.0005, type=float, help='defines the learning_rate variable for the model.')
learning_rate = args.learning_rate

is there an option to automatically store the content passed to --learning_rate into learning_rate, avoiding this second line of code?

Thanks for your time and attention.

Lucas Azevedo
  • 1,867
  • 22
  • 39
  • 3
    You can just use `args.learning_rate`. That is automatic. Why copy it to another reference if you don't want it? – OneCricketeer Jan 20 '22 at 18:37
  • 4
    "I try to avoid using the variable with the 'args.' prefix" Why? – Karl Knechtel Jan 20 '22 at 18:38
  • 3
    You can modify the global scope, but you **really shouldn't**, "Namespaces are one honking great idea -- let's do more of those!". You can't do this in local scopes. But again, that's not an issue, because you *really probably shouldn't* – juanpa.arrivillaga Jan 20 '22 at 18:39
  • 1
    I think the key is, you *don't* want to be scattering `args.learning_rate` in *local* scopes. Use `args.learning_rate` in the scope where `args` was defined, but if you need the value in a function, don't use `args` as a global: pass `args.learning_rate` as an *argument* to the function, which will perform an implicit assignment to the function's parameter when you make the call. – chepner Jan 20 '22 at 18:42

2 Answers2

1

When you call a function, an argument is implicitly assigned to a parameter.

In the same scope where args is defined, I would continue using args.learning_rate to emphasize that it is a configuration option. But instead of writing

def foo(x):
    return x * args.learning_rate

y = foo(3)

write

def foo(x, learning_rate):
    return x * learning_rate

y = foo(args.learning_rate)

When reading the definition of foo, you don't care how learning_rate will be set at call time.

chepner
  • 497,756
  • 71
  • 530
  • 681
1

As others have said, removing the line referencing args.learning_rate will lead to others finding your code cryptic or confusing.

But something like this could be used in code golfing, so I will offer a 'cryptic' way of doing this under the assumption that you have several arguments that you do not want to have to reassign line-by-line.

You could acquire the dictionary of args using the vars() built-in function or __dict__ attribute.
Please reference What is the right way to treat Python argparse.Namespace() as a dictionary.

>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> d = vars(args)
>>> d
{'foo': 1, 'bar': [1, 2, 3]}

Afterwards you could convert these keys to variables and assign the values to the variable like so:

for k, v in d.items():
    exec(f'{k} = {v}')
print(foo) # 1
print(bar) # [1, 2, 3]

Please see Using a string variable as a variable name for additional insight to why exec() may be bad practice and setattr may be more appropriate if you resort to this method.

deseuler
  • 408
  • 2
  • 8