2016-08-13 69 views
3

为了可读性,我想有一个行为完全像一个字典(但带有一个有意义的类型,而不是更一般的字典式)的自定义类:自定义类是一个字典,但初始化没有字典拷贝?

class Derivatives(dict): 
    "Dictionary that represents the derivatives." 

现在,有建筑的一种方式这个类的新对象的方式不涉及副本?其实天真使用

derivs = Derivatives({var: 1}) # var is a Python object 

创建复制作为参数,这是我想避免的,为了提高效率,通过字典

我试图绕过副本,但随后类字典内无法改变,在CPython的:

class Derivatives(dict): 
    def __new__(cls, init_dict): 
     init_dict.__class__ = cls # Fails with __class__ assignment: only for heap types 
     return init_dict 

我想有两个给一个明确的类名字典的能力的程序操纵构建这种字典的有效方式(而不是被迫复制Python字典)。这在Python中可以有效地实现吗? PS:用例可能是100,000个单键密钥Derivatives,其中密钥是一个变量(不是字符串,所以没有关键字初始化)。这实际上并不慢,所以这里的“效率原因”意味着更多像“优雅”这样的东西:当不需要副本时,理想情况下不需要浪费时间做副本。因此,在这个特殊情况下,问题更多的是Python可以带来的优雅/清晰度,而不是运行速度。

+2

为什么不只是'derivs = Derivatives(x = 1)'?香草词典('dict({'x':1})'')会出现同样的复制行为,但您不清楚为什么您会这么做。 – jonrsharpe

+0

好点,但我不能,因为这些键实际上是Python对象。我会更新这个例子。 – EOL

+0

然后不,没有办法提供你自己的文字。如果你不需要它*是一个字典,你可以让它包含*一个字典,并通过'__getitem__'等公开它 – jonrsharpe

回答

1

TL; DR:有,除非你在C.做它不是通用的方式来做到这一点

龙答: 的dict类是用C实现的。因此,有没有办法来访问它的内部属性 - 最重要的是,它是内部散列表,除非你使用C.

在C中,您可以简单地将表示散列表的指针复制到对象中,而无需遍历dict(键,值)对和将它们插入到您的对象中。 (当然,这比这更复杂一点,请注意我省略了内存管理细节)。

较长的答案:

我不知道为什么你关心效率。

Python将参数作为参考传递。除非你明确地告诉它,否则它很少每个副本。

我读了注释,你不能使用命名参数,因为键是实际的Python对象。这让我明白,你担心复制dict键(也许值)。但是,即使字典键也不被复制,并且通过引用传递!考虑以下代码:

class Test: 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

    def __hash__(self): 
     return self.x 

t = Test(1, 2) 
print(t.y) # prints 2 
d = {t: 1} 
print(d[t]) # prints 1 
keys = list(d.keys()) 
keys[0].y = 10 
print(t.y) # prints 10! No copying was made when inserting object into dictionary. 

因此,关注仅存的面积是通过迭代dict并在Derivatives类插入值。这是不可避免的,除非你能以某种方式将你的类的内部散列表设置为dict的内部散列表。在纯python中没有办法做到这一点,因为dict类在C中实现(如上所述)。

请注意,其他人建议使用生成器。这似乎也是一个好主意 - 比方说,如果你是从文件中读取衍生产品,或者是用简单的公式生成衍生产品。这将避免首先创建dict对象。但是,如果生成器只是围绕list s(或任何其他可包含值集合的数据结构)的包装器,效率将不会有明显的提高。

你最好的选择是坚持你的原始方法。生成器很棒,但它们不能有效地表示一组值(在您的场景中可能是这种情况)。这也是不值得这样做在C。

编辑:它可能毕竟是值得它在C做!我不太在Python C API的细节上,但考虑在C中定义一个类,例如,DerivativesBase(从dict派生)。你所要做的就是在C中为DerivativesBase定义一个__init__函数,它将dict作为参数,并将dict的哈希表指针复制到你的DerivativesBase对象中。然后,在Python中,您的Derivatives类从DerivativesBase派生并实现大部分功能。

+0

这很有趣,也很完整。一个细节:我不认为这是正确的,但是,说“生成器只是列表中的包装器”。例如,一个生成器函数可以返回所有的自然整数:这里没有涉及到列表。类似的,像'(x ** 2 for x in xrange(9))'这样的生成器表达式没有理由首先建立一个列表(这就是为什么'xrange()'存在于第一位)。也就是说,我得出了同样的结论,因为无论如何都是复制的。 – EOL

+0

@EOL我的意思是生成器效率低下,如果它们是列表中的包装器! (例如,[1,2,3]中我没有任何好处) - 列表需要创建并存储!(请注意,我如何说没有明显的效率增益*如果发生器是只是一个包装清单 –

1

通过从dict继承你给出三种可能的构造函数的参数:(裸露的{}字面)

class dict(**kwarg) 
class dict(mapping, **kwarg) 
class dict(iterable, **kwarg) 

这意味着,以实例化您的实例必须执行下列操作之一:

  1. 将变量作为关键字D(x=1)传递,然后将它们打包到中间字典中。
  2. 创建一个普通字典并将其作为mapping传递。

  3. 传递(键,值)对的迭代。

所以在所有这三种情况下,你需要创建中间对象,以满足dict构造。

单个对的第三个选项,它看起来像D(((var,1),)),我强烈建议不要为了可读性。

所以,如果你想让你的课程从字典继承,使用Derivatives({var: 1})是你最有效和最可读的选择。

作为一个个人笔记,如果您将有成千上万的单对字典,我不确定dict设置是如何在首位是最好的,你可能只是重新考虑你的班级的基础。

+0

这是正确的,但我的问题是关于是否可以避免使用'dict()'构造函数(在这个问题中,我展示了一个尝试只改变字典的__class__'例如新的自定义类)。 – EOL