如何创建一个Python命名空间(argparse.parse_args的值)?

115
为了交互式地测试我的Python脚本,我想创建一个类似于argparse.parse_args()返回的Namespace对象。 显而易见的方法是,
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.parse_args()
Namespace()
>>> parser.parse_args("-a")
usage: [-h]
: error: unrecognized arguments: - a

Process Python exited abnormally with code 2

可能会导致Python repl在一个愚蠢的错误上退出(如上所述)。
那么,创建一个具有给定属性集的Python命名空间最简单的方法是什么?
例如,我可以动态创建一个字典(dict([("a",1),("b","c")])),但我不能将其用作命名空间(Namespace)。
AttributeError: 'dict' object has no attribute 'a'

PS. 新的exit_on_error选项看起来是一个有希望的替代方案,可以避免创建Namespace对象,但显然它被设计得严重破损
6个回答

218

你可以创建一个简单的类:

class Namespace:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

当涉及到属性时,它将与argparse Namespace类完全相同:

>>> args = Namespace(a=1, b='c')
>>> args.a
1
>>> args.b
'c'

或者,只需导入类;可以从argparse模块中获取:

from argparse import Namespace

args = Namespace(a=1, b='c')

从 Python 3.3 开始,还有types.SimpleNamespace,它基本上也可以实现相同的功能:

>>> from types import SimpleNamespace
>>> args = SimpleNamespace(a=1, b='c')
>>> args.a
1
>>> args.b
'c'

这两种类型是不同的;SimpleNamespace 主要用于 sys.implementation 属性和 time.get_clock_info() 的返回值。

进一步比较:

  • 这两个类都支持相等性测试;对于同一类的两个实例,如果它们具有相同的属性和相同的值,则 instance_a == instance_b 为 true。
  • 这两个类都有一个有用的 __repr__ 来显示它们拥有哪些属性。
  • Namespace() 对象支持包含测试;如果命名空间实例具有名为 attrname 的属性,则 'attrname' in instance 为true。 SimpleNamespace则没有此功能。
  • Namespace() 对象具有一个未记录的 ._get_kwargs() 方法,该方法返回该实例的一个按名称排序的 (name, value) 属性列表。您可以使用 sorted(vars(instance).items()) 获取任何一个类的相同内容。
  • 虽然 SimpleNamespace() 是用 C 实现的,而 Namespace() 是用 Python 实现的,但由于两者都使用相同的 __dict__ 存储属性,因此属性访问速度并没有更快。对于 SimpleNamespace() 实例,相等性测试和生成表示稍微快一些。

4
前面提到的简单类实际上指的是 types.SimpleNamespace,请参考 https://docs.python.org/dev/library/types.html#types.SimpleNamespace。 - Ofer
@Ofer:不,SimpleNamespace实际上并没有被argparse使用;argparse.Namespace是该库独有的纯Python类,而types.SimpleNamespace是最初为sys.implementation开发的类,后来也用于time.get_clock_info()。它是用C实现的。 - Martijn Pieters
为了像argparse.Namespace一样实现'attrname' in instance,可以在上面的类定义中添加__contains__方法:def __contains__(self, item): return item in self.__dict__ - alleen1

30

现在建议使用 types 模块中的 SimpleNamespace。它与被接受的答案执行相同的操作,但速度更快,并具有一些更多的内置函数,例如 equals 和 repr。

from types import SimpleNamespace

sn = SimpleNamespace()
sn.a = 'test'
sn.a

# output
'test'

5
argparse.Namespace实现了__repr____eq__SimpleNamespace是用C实现的,但由于这个原因只有等式测试和repr()输出会稍微快一些。由于两者使用完全相同的机制存储和查找属性,因此属性访问同样很快。 argparse.Namespace()还实现了__contains__,因此您可以使用if something in ns_instance,并且有一个未记录的._get_kwargs()方法; SimpleNamespace()没有这两个功能。 - Martijn Pieters

5

首先创建一个字典,然后使用该字典来创建一个命名空间:

from argparse import Namespace
x = {'a': 1, 'b': 2}
ns = Namespace(**x)
print(ns.a) #output 1

0

1
不行,这个参数是严重损坏的 - sds
哦,我不知道,谢谢你。 - mame98

0
如果你喜欢简短的代码,你只需要添加一行额外的代码来定义一个空类:
class dummy:pass
obj = dummy()

在你的程序中,dummy 可以是任何未定义的字符串。然后 obj 将成为一个命名空间。

你还可以在一行中初始化一些命名空间成员:

class dummy:a=1;b=2
obj = dummy()

print(obj.a+' '+obj.b)

Python的一个设计缺陷是没有直接的方法来创建最基本的Python对象(即命名空间对象),就像创建元组、列表、字典等一样。如果我是设计者,我会让a=<>来创建一个空的命名空间a

-5

argparse文档展示了你正在尝试做的各种例子:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-a")
parser.parse_args(['-a 12'])
>>> Namespace(a=' 12')

2
没有好的解决办法,就像我在问题中所解释的那样:只要有一个小错误,REPL 就会“死掉”。 - sds
1
这并不允许您交互式地测试使用“Namespace”实例的代码。 - Martijn Pieters
@sds 我可能在你的问题“repl is dead”的部分上漏掉了什么,但是难道不是因为你使用的ArgumentParser缺少add_argument调用吗?我确实理解你不想在测试中重新创建相同的ArgumentParser,但可以在测试中调用一个单独的方法来创建它以便检索。@MartijnPieters 你不能只是这样创建ArgumentParser,然后将其传递给使用它的方法吗?my_method_using_arg_parse(fake_namespace) - El Bert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接