2016-11-18 44 views
2

namedtuple创建通常需要输入两次名称,一次提供包含新类的模块级变量的名称,一次设置新类的__name__属性(用于打印输出类对象,我认为主要用于调试,日志记录等)。创建namedtuple时避免输入两次名称

使用函数(new_namedtuple以下)来减少样板代码是否有任何不足之处,除了稍微意外之外?并正在使用globals()是否正确或应该使用exec?当然,当我们想要的类名和变量名就只会工作是相同的:

>>> from collections import namedtuple 
>>> def new_namedtuple(name, *args, **kwargs): 
... globals()[name] = namedtuple(name, *args, **kwargs) 
... 
>>> new_namedtuple('Point', 'x y') 
>>> p = Point(x=1, y=10) 
>>> p 
Point(x=1, y=10) 

编辑:@Moinuddin夸德里指出,进口量将会失败。这不好。因此,让我重述一下我的问题:最好的办法是什么?或者它不值得吗?

+1

突变全局名称空间会导致陷阱和有趣的调试......如果我有一个名为'Point'的类在范围内 - 这会摧毁它 - 并隔离范围,为什么突然停止工作将是有趣的你的'new_namedtuple'甚至可能不会运行,这意味着运行时间行为将变得怪异。 –

+0

@JonClements同意。虽然严格地说,对于'Point = namedtuple('Point','x y')'可以这么说:这行代码有时可能只运行,因此偶尔但并非总是会破坏先前定义的'class Point'那意外地有相同的名字)。 – max

+0

我不喜欢它。避免重复的更好方法是使用元类。 – wim

回答

0

是的,我在这里看到几个缺点。最大的是你不能在其他文件中导入Point()

另一个是其他开发人员查看代码的可读性。最好使用默认语法(可能是基于观点的,但我认为它是缺点)。

+0

可以吗?通过将新名称添加到'new_namedtuple()'内部的模块来修复? – max

1

我能看到的最大缺点是清晰度。

如果我看到这行:

new_namedtuple('Point', 'x y') 

我会感到很惊讶地发现,现在Point是一个变量。

如果有特殊的语法,可能很清楚,但没有。这个变量是以非常隐含的方式进行的。

Explicit is better than implicit

+1

我同意你的观点。这是PEP 359“制造”声明被拒绝的部分原因。 –

1

最大的缺点将是混乱为最终用户(也许的路线,它发生)。人们通常不会期望一个函数像这样改变全局范围。

功能上这没有问题;你可以根据需要修改globals(),它会运行得很好。可以对其进行语义争论;它确实没有理由不输入两次名称。

0

由于namedtuple是一个工厂,你可以使用下面的结构:

>>> class Point(namedtuple('whocares', 'x y')): ... 
>>> p = Point(1, 2) 
>>> p 
Point(x=1, y=2) 

现在是导入的,不重复的类名。基地的嵌套名称有点难看,但如果您可能会感到困扰,它可以用元类进行清理。


如果你打开第三方库,也许你会有兴趣看看fields

>>> from fields import Tuple 
>>> class Point(Tuple.x.y): 
...  pass 
... 
>>> p = Point(3, 7) 
>>> p 
Point(x=3, y=7) 
>>> isinstance(p, tuple) 
True 
>>> issubclass(Point, tuple) 
True 
>>> p.x 
3 
>>> p.y 
7 
>>> tuple(p) 
(3, 7) 
>>> x, y = p 
>>> x 
3 
>>> y 
7 
+0

它会工作,但我不认为它有任何优势。这将使大多数人在查看代码时都会嘲笑他们为什么这样做。简单地使用'Point = namedtuple'('Point','x y')'应该会更好。 –

+0

我同意你的意见。但是使用'Point = namedtuple'('Point','x y')'不能完全回答问题,是吗? :) – wim

+0

问题是*创建namedtuple *时避免键入两次名称,这意味着用户在创建'namedtuple'对象时懒得键入'Point'两次。输入额外的'class'对我来说是一样的。理论上你是对的,你没有在声明:)时写两次'Point' :) –

4

的推荐使用typing.NamedTuple 3.6将满足你的要求的方式,但要求你使用类型提示。

from typing import NamedTuple 

class Point(NamedTuple): 
    x: int 
    y: int 

p = Point(1, 10) 

打字暗示摩擦一些错误的方式。不幸的是,collections.namedtuple不支持这种用法,手指越过这种变化。

+1

哇,这是如何进入python 3.6?我认为类似的方法定义没有赋予'Enum'的属性在最后一分钟被拒绝,因为它看起来很奇怪,会混淆静态分析器。但我猜想,因为它已经在使用该语言了,所以不妨使用它。 – max

+0

是的,我绝对不会这样做,除非我已经计划使用类型提示;类型提示的“成本”只是为了方便语法。 – max

+0

来自python文档: 打字模块已被临时包含在标准库中。如果核心开发人员认为有必要,可能会添加新功能,甚至可能会在次要版本之间改变API。 –