2016-08-13 26 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