【问题标题】:Can I convert a namespace object from mutable to immutable?我可以将命名空间对象从可变转换为不可变吗?
【发布时间】:2016-03-24 17:55:05
【问题描述】:

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

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

【问题讨论】:

  • 在 Python 中锁定一个变量?
  • 只是...不要修改它? Python 中没有像 const 这样的东西。通常的哲学是,如果你不想做某事,你就是不去做。这就是为什么我们也没有private
  • 感谢您的建议。我明白了,我的想法在 Python 中不合适。我非常了解。我会换个方式。非常感谢。

标签: python python-3.x argparse


【解决方案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

【讨论】:

  • 如果您对argparseNamespace 执行此操作,那么您将破坏argparse 以获取将来可能使用它的代码中的任何其他内容。以为我会给你一个提示。 Monkeypatching 核心类来禁用它们是一个不行。
  • @ShadowRanger:OP 的想法从一开始就很奇怪。所以它有同样奇怪的答案。
  • 这不是一个古怪的想法。您可以定义自己的namespace 类。只要允许解析器使用getattrsetattr 获取和设置属性,它就可以工作。
【解决方案2】:

我最初提出了自定义 Namespace 类,但我更喜欢将 args 复制到 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)

现在定义一个命名元组

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 命名空间是可能的,甚至可以接受 - 使用这样的自定义类。

【讨论】:

  • 只要你要解决所有这些麻烦,你也可以继承 argparse.ArgumentParser 来包装它的 parse_args 来做一个不可变命名空间的重新包装;与为每个用例创建特殊的do_parse 相比,更容易做到这一点并使您的工作可以轻松地重用(用户只需更改解析器的初始化行,否则就可以正常使用它)。
  • 这取决于您是否需要重复做这种事情,或者只需一次或两次。子类ArgumentParser 如果您需要重复重用某些新功能。您仍然可以将解析器定义/使用包装在一个函数中,以将解析与使用完全分开。
  • 非常感谢。我知道没有什么像“const”这样的,而且我的方式在 Python 中是不合适的。我认为这是另一种语言的方式。我将遵循 Python 的方式。谢谢你的回答,我学到了很多。
  • @ShadowRanger:一个更可重用的解决方案是创建一个函数,将Bunch-like 对象such as argparse.Namespace() 转换为命名元组(它应该很简单,创建一个浅的、非递归的版本)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
  • 2012-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多