让 namedtuple 接受 kwargs
Make namedtuple accept kwargs
如果我class喜欢:
class Person(object):
def __init__(self, name, **kwargs):
self.name = name
p = Person(name='joe', age=25) # age is ignored
忽略额外的参数。但是如果我有一个namedtuple
,我会得到`unexpected keyword argument:
from collections import namedtuple
Person = namedtuple('Person', 'name')
p = Person(name='joe', age=25)
# Traceback (most recent call last):
# File "python", line 1, in <module>
# TypeError: __new__() got an unexpected keyword argument 'age'
如何让 namedtuple
接受 kwargs
以便我可以安全地传递额外的参数?
不好看:
p = Person(*(dict(name='joe', age=25)[k] for k in Person._fields))
可以包装 Person
class 构造函数以忽略未定义为 Person
命名元组字段的参数:
from collections import namedtuple
Person = namedtuple('Person', 'name')
def make_person(*args, **kwargs):
person_args = {}
# process positional args
if len(args) > len(Person._fields):
msg = "Person() takes %d positional arguments but %d were given" % (len(Person._fields), len(args))
raise TypeError(msg)
for arg_name, arg_value in zip(Person._fields, args):
person_args[arg_name] = arg_value
# process keyword args
for arg_name, arg_value in kwargs.items():
try:
i = Person._fields.index(arg_name)
except ValueError:
pass # ignore arguments not defined as Person fields
else:
if arg_name in person_args:
msg = "make_person() got multiple values for argument " + repr(arg_name)
raise TypeError(msg)
person_args[arg_name] = arg_value
if len(person_args) != len(Person._fields):
msg = "Person() requires additional arguments: "
msg += ", ".join([repr(x) for x in Person._fields if x not in person_args])
raise TypeError(msg)
return Person(*[person_args[x] for x in Person._fields])
鉴于以上:
>>> make_person('a')
Person(name='a')
>>> make_person('a', b='b')
Person(name='a')
>>> make_person('a', name='b')
TypeError: make_person() got multiple values for argument 'name'
>>> make_person(b='b')
TypeError: Person() requires additional arguments: 'name'
>>> make_person(1, 2)
TypeError: Person() takes 1 positional arguments but 2 were given
解释器中的以下会话显示了解决问题的一种可能解决方案:
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 02:27:37) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import collections
>>> class Person(collections.namedtuple('base', 'name')):
__slots__ = ()
def __new__(cls, *args, **kwargs):
for key in tuple(kwargs):
if key not in cls._fields:
del kwargs[key]
return super().__new__(cls, *args, **kwargs)
>>> p = Person(name='joe', age=25)
>>> p
Person(name='joe')
>>>
选择:
由于您宁愿有一个更简单的解决方案,您可能会发现下一个程序更符合您的喜好:
#! /usr/bin/env python3
import collections
def main():
Person = namedtuple('Person', 'name')
p = Person(name='joe', age=25)
print(p)
def namedtuple(typename, field_names, verbose=False, rename=False):
base = collections.namedtuple('Base', field_names, verbose, rename)
return type(typename, (base,), {
'__slots__': (),
'__new__': lambda cls, *args, **kwargs: base.__new__(cls, *args, **{
key: value for key, value in kwargs.items()
if key in base._fields})})
if __name__ == '__main__':
main()
使用工厂方法对@paulmcg 的回答稍作改动:
_Person = namedtuple('Person', 'name')
class Person(_Person):
@staticmethod
def from_dict(args):
args = {k: v for k, v in args.items() if k in _Person._fields}
return Person(**args)
p = Person.from_dict(dict(name='joe', age=25))
print(p)
简单明了,只需给 namedtuple
一个 kwargs
字段并将其默认值设置为空字典即可。然后可以在该 kwargs
字段下添加和访问任何 key-value 对,请参阅下面的示例代码。
from collections import namedtuple
Person = namedtuple('Person', 'name, kwargs', defaults=['', {}])
p = Person(name='joe', kwargs={'age': 25})
print(p)
print(p.kwargs['age'])
p2 = Person()
print(p2)
输出:
Person(name='joe', kwargs={'age': 25})
25
Person(name='', kwargs={})
如果我class喜欢:
class Person(object):
def __init__(self, name, **kwargs):
self.name = name
p = Person(name='joe', age=25) # age is ignored
忽略额外的参数。但是如果我有一个namedtuple
,我会得到`unexpected keyword argument:
from collections import namedtuple
Person = namedtuple('Person', 'name')
p = Person(name='joe', age=25)
# Traceback (most recent call last):
# File "python", line 1, in <module>
# TypeError: __new__() got an unexpected keyword argument 'age'
如何让 namedtuple
接受 kwargs
以便我可以安全地传递额外的参数?
不好看:
p = Person(*(dict(name='joe', age=25)[k] for k in Person._fields))
可以包装 Person
class 构造函数以忽略未定义为 Person
命名元组字段的参数:
from collections import namedtuple
Person = namedtuple('Person', 'name')
def make_person(*args, **kwargs):
person_args = {}
# process positional args
if len(args) > len(Person._fields):
msg = "Person() takes %d positional arguments but %d were given" % (len(Person._fields), len(args))
raise TypeError(msg)
for arg_name, arg_value in zip(Person._fields, args):
person_args[arg_name] = arg_value
# process keyword args
for arg_name, arg_value in kwargs.items():
try:
i = Person._fields.index(arg_name)
except ValueError:
pass # ignore arguments not defined as Person fields
else:
if arg_name in person_args:
msg = "make_person() got multiple values for argument " + repr(arg_name)
raise TypeError(msg)
person_args[arg_name] = arg_value
if len(person_args) != len(Person._fields):
msg = "Person() requires additional arguments: "
msg += ", ".join([repr(x) for x in Person._fields if x not in person_args])
raise TypeError(msg)
return Person(*[person_args[x] for x in Person._fields])
鉴于以上:
>>> make_person('a')
Person(name='a')
>>> make_person('a', b='b')
Person(name='a')
>>> make_person('a', name='b')
TypeError: make_person() got multiple values for argument 'name'
>>> make_person(b='b')
TypeError: Person() requires additional arguments: 'name'
>>> make_person(1, 2)
TypeError: Person() takes 1 positional arguments but 2 were given
解释器中的以下会话显示了解决问题的一种可能解决方案:
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 02:27:37) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import collections
>>> class Person(collections.namedtuple('base', 'name')):
__slots__ = ()
def __new__(cls, *args, **kwargs):
for key in tuple(kwargs):
if key not in cls._fields:
del kwargs[key]
return super().__new__(cls, *args, **kwargs)
>>> p = Person(name='joe', age=25)
>>> p
Person(name='joe')
>>>
选择:
由于您宁愿有一个更简单的解决方案,您可能会发现下一个程序更符合您的喜好:
#! /usr/bin/env python3
import collections
def main():
Person = namedtuple('Person', 'name')
p = Person(name='joe', age=25)
print(p)
def namedtuple(typename, field_names, verbose=False, rename=False):
base = collections.namedtuple('Base', field_names, verbose, rename)
return type(typename, (base,), {
'__slots__': (),
'__new__': lambda cls, *args, **kwargs: base.__new__(cls, *args, **{
key: value for key, value in kwargs.items()
if key in base._fields})})
if __name__ == '__main__':
main()
使用工厂方法对@paulmcg 的回答稍作改动:
_Person = namedtuple('Person', 'name')
class Person(_Person):
@staticmethod
def from_dict(args):
args = {k: v for k, v in args.items() if k in _Person._fields}
return Person(**args)
p = Person.from_dict(dict(name='joe', age=25))
print(p)
简单明了,只需给 namedtuple
一个 kwargs
字段并将其默认值设置为空字典即可。然后可以在该 kwargs
字段下添加和访问任何 key-value 对,请参阅下面的示例代码。
from collections import namedtuple
Person = namedtuple('Person', 'name, kwargs', defaults=['', {}])
p = Person(name='joe', kwargs={'age': 25})
print(p)
print(p.kwargs['age'])
p2 = Person()
print(p2)
输出:
Person(name='joe', kwargs={'age': 25})
25
Person(name='', kwargs={})