Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 仅使用传递的参数子集创建namedtuple对象_Python_Arguments_Decorator_Namedtuple - Fatal编程技术网

Python 仅使用传递的参数子集创建namedtuple对象

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

我从MySQL数据库中提取行作为字典(使用SSDictCursor),并使用以下方法进行一些处理:

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
定义所期望的名称相对应的特定子集M 我的第一个想法是尝试编写一个可以应用于我的每个类的修饰符,它将检查类以查看它需要哪些字段,并且只将适当的参数传递给新对象。但在过去的几天里,我刚刚开始阅读有关装饰师的文章,我对他们还没有那么大的信心

因此,我的问题分为两部分:

  • 这是否可以通过一个装饰器来实现,该装饰器将确定被装饰的特定类需要哪些字段
  • 是否有具有相同功能且更易于使用、修改和理解的替代方案
  • 我有太多的表和字段的潜在排列,每个结果集中有数百万行,而仅仅编写一个通用的
    namedtuple
    子类来处理每个不同的任务。查询时间和可用内存已被证明是限制因素

    如有需要:

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