使namedtuple接受kwargs

问题描述 投票:0回答:6

如果我有这样的课程:

class Person(object):
    def __init__(self, name, **kwargs):
        self.name = name

p = Person(name='joe', age=25) # age is ignored

额外的参数将被忽略。但如果我有一个

namedtuple
,我会得到`意外的关键字参数:

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
这样我就可以安全地传递额外的参数?

python python-2.7 keyword-argument namedtuple
6个回答
14
投票

不太漂亮:

p = Person(*(dict(name='joe', age=25)[k] for k in Person._fields))

11
投票

解释器中的以下会话显示了解决您的问题的一种可能的解决方案:

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()

4
投票

使用工厂方法对 @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)

3
投票

可以包装

Person
类构造函数来忽略未定义为
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

0
投票

如果你愿意拥有这个有用的小库,我最近处理了类似的情况,如下所示:

import funcy
data = {"lots": "of", "keys": "here"}
trip = Trip(**funcy.project(data, Trip._fields))

funcy
图书馆:https://github.com/Suor/funcy#readme
project
功能:https://funcy.readthedocs.io/en/stable/colls.html#project


-1
投票

很简单,只需为您的

namedtuple
提供
kwargs
字段并将其默认值设置为空字典即可。然后可以在该
kwargs
字段下添加和访问任何键值对,请参阅下面的示例代码。

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)

输出:

人(姓名='乔',kwargs={'年龄':25})

25

人(名称='',kwargs={})

© www.soinside.com 2019 - 2024. All rights reserved.