Python 仅使用传递的参数子集创建namedtuple对象
我从MySQL数据库中提取行作为字典(使用SSDictCursor),并使用以下方法进行一些处理:Python 仅使用传递的参数子集创建namedtuple对象,python,arguments,decorator,namedtuple,Python,Arguments,Decorator,Namedtuple,我从MySQL数据库中提取行作为字典(使用SSDictCursor),并使用以下方法进行一些处理: from collections import namedtuple class Foo(namedtuple('Foo', ['id', 'name', 'age'])): __slots__ = () def __init__(self, *args): super(Foo, self).__init__(self, *args) # ...som
from collections import namedtuple
class Foo(namedtuple('Foo', ['id', 'name', 'age'])):
__slots__ = ()
def __init__(self, *args):
super(Foo, self).__init__(self, *args)
# ...some class methods below here
class Bar(namedtuple('Bar', ['id', 'address', 'city', 'state']):
__slots__ = ()
def __init__(self, *args):
super(Bar, self).__init__(self, *args)
# some class methods here...
# more classes for distinct processing tasks...
要使用namedtuple
,我必须事先准确地知道我想要的字段,这很好。但是,我希望允许用户向我的程序中输入一个简单的SELECT*
语句,然后该语句将遍历结果集的行,使用这些不同的类执行多个任务。为了实现这一点,我的类必须以某种方式检查从游标输入的N个字段,并仅获取与namedtuple
定义所期望的名称相对应的特定子集Mnamedtuple
子类来处理每个不同的任务。查询时间和可用内存已被证明是限制因素
如有需要:
>>> sys.version
'2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]'
namedtuple
类型有一个属性\u fields
,它是对象中字段名称的元组。您可以使用它从数据库记录中挖掘出所需的字段。首先,您必须重写\uuuu new\uuuu
,以便自定义命名对的创建,因为命名对的\uu new\uuuuuuu
方法在您进入\uuu init\uuuuu>之前会检查其参数
其次,如果您的目标是接受和过滤关键字参数,那么您需要使用**kwargs
并过滤和传递这些参数,而不仅仅是*args
所以,把它放在一起:
class Foo(namedtuple('Foo', ['id', 'name', 'age'])):
__slots__ = ()
def __new__(cls, *args, **kwargs):
kwargs = {k: v for k, v in kwargs.items() if k in cls._fields}
return super(Foo, cls).__new__(cls, *args, **kwargs)
你可以用itemgetter
来代替dict理解,但是每次我使用多个键的itemgetter时,没有人理解它的意思,所以我不情愿地停止使用它
如果您有理由这样做,您也可以重写\uuuuuu init\uuuuuuuuuu
,因为只要\uuuuuuu new\uuuuuuu
返回一个Foo
实例,就会调用它
但是你不需要仅仅为了这个,因为namedtuple的\uuuu init\uuu
不接受任何参数或做任何事情;这些值已经在\uuuu new\uuuu
中设置好了(就像tuple
和其他不可变类型一样)。看起来,使用CPython 2.7,您实际上可以super(Foo,self)。\uuuu init\uuu(*args,**kwargs)
并且它将被忽略,但是使用PyPy 1.9和CPython 3.3,您会得到一个类型错误。无论如何,没有理由通过它们,也没有任何理由说它应该有效,所以即使在CPython2.7中也不要这样做
请注意,您将获得未经过滤的kwargs
。如果你想改变这一点,你可以在\uuuuu new\uuuuu
中适当地修改kwargs
,而不是制作一本新字典。但我相信这仍然不能保证做任何事情;它只是让实现定义您是获得过滤的参数还是未过滤的参数,而不是保证未过滤的参数
那么,你能把这个包起来吗?当然
def LenientNamedTuple(name, fields):
class Wrapper(namedtuple(name, fields)):
__slots__ = ()
def __new__(cls, *args, **kwargs):
args = args[:len(fields)]
kwargs = {k: v for k, v in kwargs.items() if k in fields}
return super(Wrapper, cls).__new__(cls, *args, **kwargs)
return Wrapper
请注意,这样做的优点是不必使用准私有/半文档化的\u fields
类属性,因为我们已经将fields
作为参数
另外,在我们讨论这个问题时,我添加了一行内容,以消除多余的位置参数,正如在一篇评论中所建议的那样
现在您只需像使用namedtuple
一样使用它,它会自动忽略任何多余的参数:
class Foo(LenientNamedTuple('Foo', ['id', 'name', 'age'])):
pass
print(Foo(id=1, name=2, age=3, spam=4))
印刷(Foo(1,2,3,4,5))
打印(Foo(1,年龄=3,姓名=2,鸡蛋=4))
我已经上传了,在genexpr上用dict()
替换dict理解,以实现2.6兼容性(2.6是最早的版本,带有namedtuple
),但没有截断参数。在CPython 2.6.7、2.7.2、2.7.5、3.2.3、3.3.0和3.3.1、PyPy 1.9.0和2.0b1以及Jython 2.7b中,它可以处理位置、关键字和混合参数,包括无序关键字。这些答案似乎都过于复杂。你真的想要新的类和重载,而不是仅仅写一行代码或一个助手函数来实例化你想要的标准数据类型吗
Foo = namedtuple('Foo', ['id', 'name', 'age'], defaults=(None,) * 3)
Bar = namedtuple('Bar', ['id', 'address', 'city', 'state'], defaults=(None,) * 4)
poo = {'id': 1, 'age': 'Orz', 'city': 'Tucson', 'weight': True}
ooh = {'id': 2, 'name': 'Spathi', 'state': 'Iowa', 'children': '25'}
塔达阿
或者做个小帮手
# nt should have defaults
def nt_from_kwargs(nt, **kwargs):
return nt(**dict(i for i in kwargs.items() if i[0] in nt._fields))
每个人都喜欢字典
def nt_from_dict(nt, d):
return nt(*[d[k] if k in d else None for k in nt._fields])
谢谢,我很感激你的细节。然而,当它们在Wrapper
类中硬编码时,我不知道如何用一组字段声明Foo
,用另一组字段声明Bar
?经过一些实验,它似乎可以用类包装器(namedtuple(name,fields))
工作。无法再可靠地只接受args或混合args/kwargs,但这对我来说不是问题。如果那行只是一个傻瓜,请编辑,我会接受。@AirThomas:哪里不能只接受args或混合args?我只是在我拥有的每一条Python上,在我能想到的每一个参数组合中测试了它,它总是按照预期工作。查看编辑后的答案中的链接。仅当len(args)!=len(字段)
。我想我弄错了,这似乎很好。我猜现在发生的是,声明任何关键字都会提示在kwargs中隐式解析非关键字参数。@AirThomas:使用一个简单函数进行实验:def foo(*args,**kwargs):print(args,
>>> nt_from_kwargs(Foo, id=1, age='Orz', city='Tucson', weight=True)
Foo(id=1, name=None, age='Orz')
>>> nt_from_kwargs(Bar, **poo)
Bar(id=1, address=None, city='Tucson', state=None)
>>> nt_from_kwargs(Bar, **{**poo, **ooh})
Bar(id=2, address=None, city='Tucson', state='Iowa')
def nt_from_dict(nt, d):
return nt(*[d[k] if k in d else None for k in nt._fields])
>>> nt_from_dict(Foo, poo)
Foo(id=1, name=None, age='Orz')
>>> nt_from_dict(Bar, poo)
Bar(id=1, address=None, city='Tucson', state=None)
>>> nt_from_dict(Bar, {**poo, **ooh})
Bar(id=2, address=None, city='Tucson', state='Iowa')