Can I convert a namespace object from mutable to immutable?

自古美人都是妖i 提交于 2019-12-02 17:04:01

问题


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 -*-

import argparse

def parse_args():
    parser = argparse.ArgumentParser(description='This script is ...')
    parser.add_argument('--confdir', type=str, required=True)
    parser.add_argument('--outdir', type=str, required=True)
    return parser.parse_args()

if __name__ == '__main__':
    mutable_namespace = parse_args()

    # I want to prevent overwrite like that.
    mutable_namespace.confdir = "xxx"

回答1:


I initially proposed the custom Namespace class, but I like this idea of copying args to a NamedTuple better.

Namedtuple

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.

Custom Namespace class

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.




回答2:


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


来源:https://stackoverflow.com/questions/34348568/can-i-convert-a-namespace-object-from-mutable-to-immutable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!