我可以将命名空间对象从可变的转换为不可变的吗?
Can I convert a namespace object from mutable to immutable?
我从命令行参数中收到一个命名空间对象。
我不想修改它。
我可以这样做吗?或者你有什么想法吗?
# -*- 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"
您可以在 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
我最初提出自定义命名空间 class,但我更喜欢将 args
复制到 NamedTuple 的想法。
命名元组
另一种选择是将值从 args
复制到不可变的 object/class。命名元组可能会很好地完成这项工作。
创建命名空间
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
这样的 namedtuple 不能用作命名空间,因为 setattr(foo,'x',34)
会产生相同的错误。
完成所有这一切的一种简洁方法是将其全部包装在一个函数中:
def do_parse():
parser = ....
Foo = namedtuple(...)
args = parser.parse_args()
foo = Foo(**vars(args))
return foo
调用代码永远不会看到可变的 args
,只会看到不可变的 foo
。
自定义命名空间class
要基于 Ingaz
答案,argparse
可以使用您自己的 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)
现在 args
和 anamespace
引用同一个 MyNamespace
对象。只要 getattr(anamespace, adest)
和 setattr(anamespace, adest, avalue)
工作,argparse
就可以使用这个命名空间对象。
现在,您可以允许 setattr(anamespace, 'string', 'value')
,但不允许 anamespace.string = value
吗?我认为你可以,但需要很好地理解后一种表达方式的工作原理。可能只是需要定制.__setattr__
,不过我好久没研究Python这方面了
根据设计,'monkey patch' argparse
命名空间是可能的,甚至是可接受的 - 使用像这样的自定义 class。
我从命令行参数中收到一个命名空间对象。 我不想修改它。 我可以这样做吗?或者你有什么想法吗?
# -*- 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"
您可以在 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
我最初提出自定义命名空间 class,但我更喜欢将 args
复制到 NamedTuple 的想法。
命名元组
另一种选择是将值从 args
复制到不可变的 object/class。命名元组可能会很好地完成这项工作。
创建命名空间
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
这样的 namedtuple 不能用作命名空间,因为 setattr(foo,'x',34)
会产生相同的错误。
完成所有这一切的一种简洁方法是将其全部包装在一个函数中:
def do_parse():
parser = ....
Foo = namedtuple(...)
args = parser.parse_args()
foo = Foo(**vars(args))
return foo
调用代码永远不会看到可变的 args
,只会看到不可变的 foo
。
自定义命名空间class
要基于 Ingaz
答案,argparse
可以使用您自己的 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)
现在 args
和 anamespace
引用同一个 MyNamespace
对象。只要 getattr(anamespace, adest)
和 setattr(anamespace, adest, avalue)
工作,argparse
就可以使用这个命名空间对象。
现在,您可以允许 setattr(anamespace, 'string', 'value')
,但不允许 anamespace.string = value
吗?我认为你可以,但需要很好地理解后一种表达方式的工作原理。可能只是需要定制.__setattr__
,不过我好久没研究Python这方面了
根据设计,'monkey patch' argparse
命名空间是可能的,甚至是可接受的 - 使用像这样的自定义 class。