2013-07-12 26 views
1

我拉行作为字典(使用SSDictCursor)一的参数集,并做一些处理,使用下面的方法一个namedtuple对象:创建仅利用MySQL数据库通过

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 < N.

我的第一个想法是尝试编写一个我可以应用于每个类的装饰器,它将检查类以查看它期望的字段,并将适当的参数传递给新对象。但是我在过去几天刚刚开始阅读装修工,而且我对他们还没有那么自信。

所以我的问题是两个部分:

  1. 这是可以做到用一个单一的装饰,将找出哪些字段由特定类正在装修需要的?
  2. 是否有替代具有相同的功能,将更容易使用,修改和理解?

我有太多的表和字段的潜在排列,每个结果集有数百万行,只写一个通用的namedtuple子类来处理每个不同的任务。查询时间和可用内存已被证明是限制因素。

如果需要:

>>> sys.version 
'2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)]' 

回答

2

首先,你必须以定制namedtuple创建覆盖__new__,因为namedtuple__new__方法检查它的参数之前,你甚至到达__init__。其次,如果您的目标是接受并过滤关键字参数,则需要采取**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替换字典的理解,但我每次使用itemgetter有多个按键的时候,没有人明白这意味着什么,所以我不情愿地停止使用它。


您还可以覆盖__init__如果你有充分的理由这样做,因为这样会尽快__new__返回一个Foo实例调用。

但是你不需要这样做,因为namedtuple的__init__没有任何参数或做任何事情;值已经在__new__中设置(就像tuple和其他不可变类型一样)。它看起来像CPython 2.7,你实际上可以super(Foo, self).__init__(*args, **kwargs)它只会被忽略,但与PyPy 1.9和CPython 3.3,你会得到一个TypeError。无论如何,没有理由通过它们,并且没有任何说明它应该起作用,所以即使在CPython 2.7中也不要这样做。

请注意,您__init__将得到未过滤kwargs。如果你想改变这一点,你可以在__new__内部就地变异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 

注意这不必使用准私人/半记录_fields类属性的优势,因为我们已经有fields作为参数。

另外,虽然我们在上面,但我添加了一条线来抛弃任何多余的位置参数,正如评论中所建议的那样。


现在,你只需要使用它,你会用namedtuple,它会自动忽略任何多余的参数:

class Foo(LenientNamedTuple('Foo', ['id', 'name', 'age'])): 
    pass 

print(Foo(id=1, name=2, age=3, spam=4)) 

打印(美孚(1,2,3,4,5)) 打印(美孚(1,年龄= 3,名字= 2,鸡蛋= 4))


我上传a test,替换用的dict()字典理解上的genexpr 2.6 compatibil ity(2.6是namedtuple的最早版本),但没有args截断。它适用于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。

+0

谢谢,我很欣赏细节。然而,当我们在'Wrapper'类中硬编码时,我没有看到如何用一组字段和'Bar'来声明'Foo'和另一组字段? – Air

+0

经过一番尝试,似乎可以与'class Wrapper(namedtuple(name,fields))'一起工作。不能可靠地采取args-only或混合args/kwargs,但这对我来说不是问题。如果该行只是一个混乱,请编辑,我会接受。 – Air

+0

@AirThomas:哪里不能采用独立或混合参数?我只是在每一个我拥有的Python上进行了测试,在每个我能想到的参数组合中都能够测试,并且它总能按预期工作。请参阅编辑答案中的链接。 – abarnert

2

namedtuple类型具有属性_fields其是在对象中的字段的名称的一个元组。您可以使用它从数据库记录中挖出必需的字段。