我可以将命名空间对象从可变转换为不可变吗?

时间:2015-12-18 04:31:06

标签: python python-3.x argparse

我从命令行参数接收命名空间对象。 而且我不想修改它。 我可以这样做,还是你有一些想法?

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

2 个答案:

答案 0 :(得分:2)

我最初提出了自定义Namespace类,但我喜欢将args更好地复制到NamedTuple的想法。

Namedtuple

另一种选择是将值从args复制到不可变对象/类。一个命名的元组可能很好地完成这项工作。

创建命名空间

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)

现在定义一个namedtuple

In [1161]: from collections import namedtuple
In [1163]: Foo = namedtuple('Foo',dest)

您还可以从命名空间本身(解析后)获取元组名称

Foo = namedtuple('Foo',vars(args).keys())

使用args

中的值创建这样一个元组
In [1165]: foo=Foo(**vars(args))
In [1166]: foo
Out[1166]: Foo(x=23, y=23)
In [1167]: foo.x
Out[1167]: 23

并且它是不可变的:

In [1168]: foo.x=34
... 
AttributeError: can't set attribute

这样的一个命名空间不能用作命名空间,因为setattr(foo,'x',34)会产生相同的错误。

完成所有这一切的干净方法是将它全部包装在一个函数中:

def do_parse():
   parser = ....
   Foo = namedtuple(...)
   args = parser.parse_args()
   foo = Foo(**vars(args))
   return foo

调用代码永远不会看到可变args,只看到不可变的foo

自定义命名空间类

Ingaz答案为基础,argparse可以使用您自己的Namespace课程。

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)

现在argsanamespace引用相同的MyNamespace对象。只要getattr(anamespace, adest)setattr(anamespace, adest, avalue)有效,argparse就可以使用此命名空间对象。

现在,您可以允许setattr(anamespace, 'string', 'value'),但不允许anamespace.string = value吗?我认为你可以,但需要很好地理解后一种表达方式的工作原理。它可能只需要定制.__setattr__,但我暂时没有研究过Python的这个方面。

通过设计,使用像这样的自定义类,可以使用argparse命名空间'猴子补丁',甚至可以接受。

答案 1 :(得分:1)

您可以在mutable_namespace中重新定义__setattr__

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