关于ZAKER 融媒体解决方案 合作 加入

python – 仅使用传递的参数子集创建一个 n.

CocoaChina 10-21

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

from collections import namedtupleclass Foo ( namedtuple ( 'Foo', [ 'id', 'name', 'age' ] ) ) : __slots__ = ( ) def __init__ ( self, *args ) : super ( Foo, self ) .__init__ ( self, *args ) # ...some class methods below hereclass 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 个字段并仅采用特定的子集 M< N 对应于 namedtuple 定义所期望的名称 . 我的第一个想法是尝试编写一个我可以应用于每个类的装饰器 , 它将检查类以查看它所期望的字段 , 并仅将适当的参数传递给新对象 . 但是我刚刚开始阅读过去几天关于装饰器的内容 , 而且我对它们并不那么自信 . 所以我的问题分为两部分:

> 这可能与单个装饰器有关 , 它将确定要装饰的特定类需要哪些字段?

> 是否有一种具有相同功能的替代方案 , 更易于使用 , 修改和理解?

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

如果需要的话:

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

首先 , 你必须覆盖 __new__ 以自定义 namedtuple 创建 , 因为 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 替换那个 dict 理解 , 但每次我使用带有多个键的 itemgetter 时 , 没有人理解它的意思 , 所以我不情愿地停止使用它 .

如果你有理由这样做 , 你也可以覆盖 __init__, 因为只要 __new__ 返回一个 Foo 实例就会调用它 .

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

请注意 ,__ init__ 将获得未经过滤的 kwargs. 如果你想改变它 , 你可以在 __new__ 中就地改变 kwargs, 而不是制作一个新词典 . 但我相信仍然无法保证做任何事情 ; 它只是使实现定义是否获得过滤的 args 或未过滤的 args, 而不是保证未过滤的 .

所以 , 你可以把它包起来吗?当然!

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 类属性 , 因为我们已经将字段作为参数 .

而且 , 正如我们所说的那样 , 我添加了一条线来抛弃任何多余的位置参数 , 如评论中所建议的那样 .

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

class Foo ( LenientNamedTuple ( 'Foo', [ 'id', 'name', 'age' ] ) ) : passprint ( Foo ( id=1, name=2, age=3, spam=4 ) )

打印 ( Foo ( 1,2,3,4,5 ) )

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

我上传了a test, 在 genexpr 上用 dict ( ) 替换 dict 理解兼容性 2.6 ( 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 中的位置 , 关键字和混合 args, 包括无序关键字 . 和 2.0b1, 以及 Jython 2.7b.

以上内容由"CocoaChina"上传发布 查看原文
相关标签 参数子类

觉得文章不错,微信扫描分享好友

扫码分享