I receive a namespace object from command line arguments. And I don\'t want to modify it. Can I do that or do you have some ideas?
# -*- coding: utf-8 -*-
impo
I initially proposed the custom Namespace class, but I like this idea of copying args
to a NamedTuple better.
Another option is to copy the values from args
to an immutable object/class. A named tuple might do the job nicely.
Create a namespace
In [1157]: dest=['x','y']
In [1158]: args=argparse.Namespace()
In [1159]: for name in dest:
......: setattr(args, name, 23)
......:
In [1160]: args
Out[1160]: Namespace(x=23, y=23)
now define a namedtuple
In [1161]: from collections import namedtuple
In [1163]: Foo = namedtuple('Foo',dest)
You could also get the tuple names from the Namespace itself (after parsing)
Foo = namedtuple('Foo',vars(args).keys())
create such a tuple with values from args
:
In [1165]: foo=Foo(**vars(args))
In [1166]: foo
Out[1166]: Foo(x=23, y=23)
In [1167]: foo.x
Out[1167]: 23
and it is immutable:
In [1168]: foo.x=34
...
AttributeError: can't set attribute
Such a namedtuple cannot be used as a Namespace, since setattr(foo,'x',34)
produces the same error.
A clean way to do all of this is to wrap it all in a function:
def do_parse():
parser = ....
Foo = namedtuple(...)
args = parser.parse_args()
foo = Foo(**vars(args))
return foo
The calling code never sees the mutable args
, just the immutable foo
.
To build on Ingaz
answer, argparse
can use your own Namespace
class.
https://docs.python.org/3/library/argparse.html#the-namespace-object
class MyNamespace(argparse.Namespace):
pass
<customize one or more methods>
anamespace = MyNamespace()
args = parser.parse_args(namespace=anamespace)
Now args
and anamespace
reference the same MyNamespace
object. As long as getattr(anamespace, adest)
and setattr(anamespace, adest, avalue)
work, argparse
can use this namespace object.
Now, can you allow setattr(anamespace, 'string', 'value')
, but disallow anamespace.string = value
? I think you can, but it will require a good understanding of how the latter expression works. It may just require customizing .__setattr__
, but I haven't studied this aspect of Python in a while.
By design it is possible, and even acceptable to 'monkey patch' the argparse
namespace - with a custom class like this.
You can redefine __setattr__
in your mutable_namespace:
class NotMutableException(Exception):pass
class SomeObject(object):
def init(self):
self.x = 10
self.y = 20
some_obj = SomeObject()
some_obj.z = 30
def not_setattr(self, name, value):
raise NotMutableException
type(some_obj).__setattr__ = not_setattr
some_obj.a = 1000