让namedtuple接受kwargs

nor*_*tpy 8 python kwargs namedtuple python-2.7

如果我有一个类:

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

p = Person(name='joe', age=25) # age is ignored
Run Code Online (Sandbox Code Playgroud)

额外的参数被忽略了.但如果我有一个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'
Run Code Online (Sandbox Code Playgroud)

我怎样才能namedtuple接受kwargs所以我可以安全地传递额外的参数?

Pau*_*McG 12

它不漂亮:

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


Noc*_*wer 8

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

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')
>>> 
Run Code Online (Sandbox Code Playgroud)

替代方案:

由于您更倾向于使用更简单的解决方案,因此您可能会根据自己的喜好找到下一个程序:

#! /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()
Run Code Online (Sandbox Code Playgroud)