Python 自定义类是dict,但初始化时没有dict副本?
出于易读性的目的,我希望有一个行为与dict完全相同的自定义类(但携带有意义的类型,而不是更一般的dict类型): 现在,有没有一种方法可以以不涉及副本的方式构建此类的新对象?天真的用法Python 自定义类是dict,但初始化时没有dict副本?,python,dictionary,inheritance,Python,Dictionary,Inheritance,出于易读性的目的,我希望有一个行为与dict完全相同的自定义类(但携带有意义的类型,而不是更一般的dict类型): 现在,有没有一种方法可以以不涉及副本的方式构建此类的新对象?天真的用法 derivs = Derivatives({var: 1}) # var is a Python object 事实上,我创建了作为参数传递的字典的副本,出于效率原因,我希望避免这样做 我试图绕过副本,但无法更改dict的类,在CPython中: class Derivatives(dict): d
derivs = Derivatives({var: 1}) # var is a Python object
事实上,我创建了作为参数传递的字典的副本,出于效率原因,我希望避免这样做
我试图绕过副本,但无法更改dict的类,在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 dict)。这在Python中有效吗
PS:用例可能是100000个单键
衍生工具的创建,其中键是一个变量(不是字符串,因此没有关键字初始化)。这实际上并不慢,所以这里的“效率原因”更像是“优雅”:理想情况下,当不需要拷贝时,不需要浪费时间来拷贝。因此,在这种情况下,问题更多的是Python能带来的优雅/清晰,而不是运行速度;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
并在派生类中插入值。这是不可避免的,除非您能够以某种方式将类的内部哈希表设置为dict
的内部哈希表。在纯python中无法做到这一点,因为dict类是用C实现的(如上所述)
请注意,其他人建议使用发电机。这似乎也是一个好主意——比如说,如果你从一个文件中读取导数,或者如果你用一个简单的公式生成它们。它将首先避免创建dict
对象。但是,如果生成器只是list
s(或任何其他可以包含任意值集的数据结构)的包装,那么效率将不会有明显的提高
你最好还是坚持你原来的方法。生成器很好,但它们不能有效地表示任意值集(在您的场景中可能就是这种情况)。在C语言中这样做也不值得
编辑:毕竟,用C语言做这件事是值得的
我对Python C API的细节不太大,但是考虑在C中定义一个类,例如,代码> DerivativesBase < /代码>(从<代码> DICT派生)。您只需在C中为
DerivativeBase
定义一个\uuuu init\uuuu
函数,该函数将dict
作为参数,并将哈希表指针从dict
复制到DerivativeBase
对象中。然后,在python中,您的派生类派生自developerativesbase
,并实现了大部分功能。通过继承,为构造函数参数提供了三种可能性:(突出{}
文本)
这意味着,为了实例化实例,您必须执行以下操作之一:
将变量作为关键字D(x=1)
传递,然后将其打包到中间字典中
创建一个普通字典并将其作为映射传递
传递(键、值)对的可数
因此,在这三种情况下,您都需要创建中间对象来满足dict
构造函数
对于单个对,第三个选项看起来像D((var,1),)
,为了可读性,我强烈建议不要使用它
因此,如果希望类从字典继承,那么使用派生({var:1})
是最有效、最可读的选项
作为个人注意事项,如果您将拥有数千本单对词典,我不确定dict
设置首先是如何最好,您可以重新考虑您的课程基础。为什么不只是derivs=Derivatives(x=1)
?对于普通字典(dict({'x':1})
),同样的复制行为也会发生,不清楚您为什么会有其他期望。这一点很好,但我不能,因为键实际上是Python对象。我将更新这个示例。然后不,没有办法提供您自己的文本。如果你不需要它成为一本字典,你可以让它包含一本字典,然后通过\uu getitem\uuuu
等方式公开它。这个答案,以及关于为什么会这样的解释/指针,将是我可以接受的答案。:)另一点:我没有想到派生词({…})
会复制任何少于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.
class dict(**kwarg)
class dict(mapping, **kwarg)
class dict(iterable, **kwarg)