2016-02-09 6 views
8
In [30]: import numpy as np 

In [31]: d = np.dtype(np.float64) 

In [32]: d 
Out[32]: dtype('float64') 

In [33]: d == np.float64 
Out[33]: True 

In [34]: hash(np.float64) 
Out[34]: -9223372036575774449 

In [35]: hash(d) 
Out[35]: 880835502155208439 

इन डाइट्स बराबर की तुलना क्यों करते हैं लेकिन हैश अलग हैं?ये dtypes बराबर तुलना क्यों करते हैं लेकिन हैश अलग है?

नोट अजगर वादा करता हूँ कि करता है कि:

केवल आवश्यक संपत्ति है कि एक ही हैश मान है जिन वस्तुओं पर बराबर की तुलना है ...

इस समस्या के लिए मेरे workaround पर np.dtype कॉल करने के लिए है सबकुछ, जिसके बाद हैश मान और तुलना सुसंगत हैं।

+0

दरअसल 'प्रकार (डी) == प्रकार (np.float64)' 'गलत है '। और पायथन कहते हैं कि केवल "संख्यात्मक मान जो बराबर तुलना करते हैं, वही हैश मान"। – AndyG

+0

यदि आप इसके बजाय 'd = np.float64' कहते हैं, तो हैश समकक्ष हैं। – AndyG

+0

@AndyG निश्चित है, लेकिन ऐसा इसलिए है क्योंकि वे वही वस्तु हैं। पाइथन वादा करता है कि यदि वे बराबर की तुलना करते हैं तो उन्हें हैश बराबर होना चाहिए। –

उत्तर

1

वे इस तरह से नहीं व्यवहार करना चाहिए, लेकिन __eq__ और __hash__numpy.dtype के लिए वस्तुओं अनिवार्य रूप से एक unfixable डिजाइन स्तर पर टूट रहे हैं। मैं इस जवाब के लिए dtype-related bug report पर njsmith की टिप्पणियों से भारी खींच रहा हूं।

np.float64 वास्तव में एक प्रकार का नहीं है। यह पाइथन प्रकार प्रणाली की सामान्य समझ में एक प्रकार है। विशेष रूप से, यदि आप float64 dtype की सरणी से स्केलर पुनर्प्राप्त करते हैं, तो np.float64 परिणामी स्केलर का प्रकार है।

np.dtype(np.float64) एक प्रकार है, numpy.dtype का एक उदाहरण है। dtypes हैं कैसे NumPy एक NumPy सरणी की सामग्री की संरचना रिकॉर्ड करता है। वे structured arrays के लिए विशेष रूप से महत्वपूर्ण हैं, जिनमें बहुत ही जटिल प्रकार हो सकते हैं। जबकि सामान्य पाइथन प्रकारों ने विविध प्रकार की भूमिका भरी हो सकती है, नए संरचित सरणी के लिए फ्लाई पर नए प्रकार बनाना बहुत अजीब होगा, और यह टाइप-क्लास एकीकरण से पहले के दिनों में असंभव होगा।

numpy.dtype औजार __eq__ मूल रूप से इस तरह:

def __eq__(self, other): 
    if isinstance(other, numpy.dtype): 
     return regular_comparison(self, other) 
    return self == numpy.dtype(other) 

जो बहुत टूट गया है। अन्य समस्याओं के अलावा, यह सकर्मक नहीं है, यह TypeError को जन्म देती है, जब यह NotImplemented लौटना चाहिए, और इसके उत्पादन वास्तव में समय पर कैसे dtype बलात्कार काम करता है की वजह से विचित्र है:

>>> x = numpy.dtype(numpy.float64) 
>>> x == None 
True 

numpy.dtype.__hash__ किसी भी बेहतर नहीं है।यह __hash__ अन्य सभी प्रकार के तरीकों numpy.dtype.__eq__ स्वीकार करता है (और इतने सारे असंगत प्रकारों के साथ निपटने के लिए, यह कैसे हो सकता है?) के साथ संगत होने का कोई प्रयास नहीं करता है। बिल्ली, यह भी अस्तित्व में नहीं होना चाहिए, क्योंकि dtype वस्तुओं mutable हैं! मॉड्यूल या फ़ाइल ऑब्जेक्ट्स जैसे बस म्यूटेबल नहीं, जहां यह ठीक है क्योंकि __eq__ और __hash__ पहचान द्वारा काम करते हैं। dtype वस्तुओं तरीके कि वास्तव में उनके हैश मान बदल जाएगा में परिवर्तनशील हैं:

>>> x = numpy.dtype([('f1', float)]) 
>>> hash(x) 
-405377605 
>>> x.names = ['f2'] 
>>> hash(x) 
1908240630 

जब आप d == np.float64, d.__eq__np.float64 के बाहर एक dtype बनाता है और पता चलता है कि यह सच है d == np.dtype(np.float64) है तुलना करने के लिए प्रयास करें। जब आप अपने हैंश लेते हैं, हालांकि, np.float64 टाइप ऑब्जेक्ट्स के लिए नियमित (पहचान-आधारित) हैश का उपयोग करता है और d टाइप प्रकार ऑब्जेक्ट्स के लिए हैश का उपयोग करता है। आम तौर पर, विभिन्न प्रकार की समान वस्तुओं के बराबर हैश होनी चाहिए, लेकिन डाइट कार्यान्वयन की परवाह नहीं है।

दुर्भाग्यवश, एपीआई लोगों को तोड़ने के बिना dtype __eq__ और __hash__ के साथ समस्याओं को ठीक करना असंभव है। लोग x.dtype == 'float64' या x.dtype == np.float64 जैसी चीज़ों पर भरोसा कर रहे हैं, और फिक्सिंग डाइटप्स इसे तोड़ देंगे।

+0

आपके उत्तर के लिए धन्यवाद। हालांकि, इन समस्याओं को ठीक करना असंभव नहीं है। वे कुछ खराब डिजाइन विकल्पों को कम कर सकते हैं, और उसके बाद कुछ वर्षों के बाद तंत्र को प्रतिस्थापित कर सकते हैं। उदाहरण के लिए, 'x.dtype == 'float64' का समर्थन करना एक अच्छा विचार नहीं था। –

1

वे एक ही बात नहीं कर रहे हैं, जबकि np.float64 एक type है, d एक उदाहरण numpy.dtype की है, इसलिए वे विभिन्न मूल्यों के लिए हैश, लेकिन d के सभी उदाहरणों उसी तरह एक ही मूल्य के लिए हैश जाएगा बनाया क्योंकि वे समान हैं (निश्चित रूप से इसका मतलब यह नहीं है कि वे एक ही स्मृति स्थान को इंगित करते हैं)।

संपादित करें:

को देखते हुए आप ऊपर अपने कोड निम्न का प्रयास कर सकते हैं:

In [72]: type(d) 
Out[72]: numpy.dtype 

In [74]: type(np.float64) 
Out[74]: type 

जो आपको पता चलता है कि दो अलग अलग प्रकार के होते हैं और इसलिए विभिन्न मूल्यों के लिए हैश होगा। दिखा रहा है कि numpy.dtype के विभिन्न उदाहरणों निम्न उदाहरण से दिखाया जा सकता है:

In [77]: import copy 
In [78]: dd = copy.deepcopy(d) # Try copying 

In [79]: dd 
Out[79]: dtype('float64') 

In [80]: hash(dd) 
Out[80]: -6584369718629170405 

In [81]: hash(d) # original d 
Out[81]: -6584369718629170405 

In [82]: ddd = np.dtype(np.float64) # new instance 
In [83]: hash(ddd) 
Out[83]: -6584369718629170405 

# If using CPython, id returns the address in memory (see: https://docs.python.org/3/library/functions.html#id) 
In [84]: id(ddd) 
Out[84]: 4376165768 

In [85]: id(dd) 
Out[85]: 4459249168 

In [86]: id(d) 
Out[86]: 4376165768 

इसके देखने के लिए कि ddd, और d ही स्मृति में एक ही वस्तु का हिस्सा (उदाहरण के d के रूप में एक ही तरीके से बनाई गई) अच्छा है, लेकिन dd (कॉपी ऑब्जेक्ट) एक अलग पते का उपयोग करता है।

समानता चेकों का मूल्यांकन के रूप में आप उम्मीद करेंगे, ऊपर हैश दिया:

In [87]: dd == np.float64 
Out[87]: True 
In [88]: d == np.float64 
Out[88]: True 
In [89]: ddd == np.float64 
Out[89]: True 
In [90]: d == dd 
Out[90]: True 
In [91]: d == ddd 
Out[91]: True 
In [92]: dd == ddd 
Out[92]: True 
+0

यह सही उत्तर की तरह दिखता है, लेकिन क्या आप इसे थोड़ा सा विस्तार कर सकते हैं? –

+0

@NeilG मैंने अपना जवाब अपडेट कर लिया है, मुझे उम्मीद है कि आप जो खोज रहे थे;) – tttthomasssss

0

यह क्योंकि आप एक dtype वस्तु के खिलाफ एक type hashing कर रहे हैं।

हालांकि मूल्यों बराबर (d == np.float64 से तुलना सबूत के रूप में, उनके प्रकार अलग हैं:

print type(d) 
print type(np.float64) 

उत्पादन

< प्रकार 'numpy.dtype' >

< प्रकार 'प्रकार '>

Python docs के अनुसार:

hash (वस्तु)

वापसी वस्तु के हैश मान (यदि उसमें कोई है)।हैश मान पूर्णांक हैं। वे एक शब्दकोश लुकअप के दौरान शब्दकोश कुंजी की तुलना करने के लिए जल्दी से उपयोग किया जाता है। बराबर तुलना करने वाले संख्यात्मक मानों में समान हैश मान होता है (भले ही वे विभिन्न प्रकार के होते हैं, जैसा कि 1 और 1.0 के मामले में है)।

और एक dtype के बाद से एक अंकीय प्रकार, कोई गारंटी नहीं है कि इस तरह के और वस्तु एक type कि बराबर तुलना के रूप में ही हैश में परिणाम होगा नहीं है नहीं है।


संपादित करें: Python 3.5 docs से:

object.__hash__(self)

में निर्मित समारोह हैश द्वारा कहा जाता है() और सेट, frozenset, और dict सहित टुकड़ों में बंटी संग्रह के सदस्यों पर कार्रवाई के लिए। हैश() को एक पूर्णांक वापस करना चाहिए। एकमात्र आवश्यक संपत्ति यह है कि बराबर की तुलना करने वाली वस्तुओं में समान हैश मान होता है; यह किसी भी तरह से ऑब्जेक्ट के घटकों के लिए हैश मानों को एक साथ मिलाकर (उदाहरण के लिए अनन्य या) का उपयोग करने की सलाह दी जाती है जो वस्तुओं की तुलना में एक हिस्सा भी बजाती है।

जो दर्शाता है कि hash(d) == hash(np.float64) को आपके मामले में True वापस करना चाहिए।

मैं नोटिस किया था एक नोट है कि वहाँ कि सही होने के बाद राज्यों:

hash() एक वस्तु के कस्टम हैश() विधि से एक Py_ssize_t के आकार के दिए गए मान ट्रंकेटस। यह आम तौर पर 64-बिट बिल्डों पर 8 बाइट्स और 32-बिट बिल्डों पर 4 बाइट्स है।

हालांकि, मैं यह निर्धारित करने में सक्षम नहीं था कि हैश कार्यों से लौटाई गई वस्तुओं का आकार वास्तव में अलग था; वे के लिए np.float64 और d अलग हैं ही (मैं sys.getsizeof इस्तेमाल किया)

+0

कृपया मेरा अपडेट किया गया प्रश्न देखें। मैंने दस्तावेज़ों का एक और हिस्सा उद्धृत किया। –

4

tttthomasssss नोटों के रूप में, type (वर्ग) दिखाई देते हैं। वे चीजों के विभिन्न प्रकार हैं:

In [435]: type(np.float64) 
Out[435]: type 

प्रकार type साधन (आमतौर पर) है कि यह एक समारोह है, तो यह रूप में इस्तेमाल किया जा सकता है:

In [436]: np.float64(0) 
Out[436]: 0.0 

In [437]: type(_) 
Out[437]: numpy.float64 

एक अंकीय वस्तु का निर्माण। असल में यह एक वर्ग परिभाषा की तरह दिखता है। लेकिन चूंकि numpy बहुत सारे संकलित कोड का उपयोग करता है, और इसके ndarray अपने स्वयं के __new__ का उपयोग करता है, तो मुझे आश्चर्य नहीं होगा कि यह लाइन को परेशान करता है।

In [438]: np.float64.__hash__?? 
Type:  wrapper_descriptor 
String Form:<slot wrapper '__hash__' of 'float' objects> 
Docstring: x.__hash__() <==> hash(x) 

मैं इस होगा hash(np.float64) सोच रहा था, लेकिन यह वास्तव में उस प्रकार का एक उद्देश्य है, उदा के लिए हैश हो सकता है hash(np.float64(0))। उस स्थिति में hash(np.float64) बस डिफ़ॉल्ट type.__hash__ विधि का उपयोग करता है।

dtype को आगे बढ़ते:

In [441]: d(0) 
... 
TypeError: 'numpy.dtype' object is not callable 

In [442]: d.__hash__?? 
Type:  method-wrapper 
String Form:<method-wrapper '__hash__' of numpy.dtype object at 0xb60f8a60> 
Docstring: x.__hash__() <==> hash(x) 

np.dtype ऐसा लगता है कि किसी विशेष __hash__ विधि को परिभाषित नहीं करता, यह सिर्फ object से विरासत:

In [439]: d=np.dtype(np.float64) 

In [440]: type(d) 
Out[440]: numpy.dtype 

d एक समारोह या वर्ग नहीं है।

इसके अलावा float64 और d के बीच अंतर बताने वाला वर्ग विरासत ढेर

In [443]: np.float64.__mro__ 
Out[443]: 
(numpy.float64, 
numpy.floating, 
numpy.inexact, 
numpy.number, 
numpy.generic, 
float, 
object) 

In [444]: d.__mro__ 
... 
AttributeError: 'numpy.dtype' object has no attribute '__mro__' 

In [445]: np.dtype.__mro__ 
Out[445]: (numpy.dtype, object) 

तो np.float64 या तो एक हैश परिभाषित नहीं करता है, यह सिर्फ float से विरासत को देखो। d में __mro__ नहीं है क्योंकि यह एक वस्तु है, न कि वर्ग।

numpy में पर्याप्त संकलित कोड है, और इसका एक लंबा इतिहास है, कि आप हमेशा पाइथन दस्तावेज पर आवेदन नहीं कर सकते हैं।

np.dtype और np.float64 जाहिर सुनिश्चित करें कि __hash__ तरीकों का पालन बनाने में किसी भी प्रयास नहीं किया __eq__ तरीकों कि उन्हें एक दूसरे के साथ तुलना में होने की अनुमति है, लेकिन numpy डेवलपर्स की है। सबसे अधिक संभावना है क्योंकि उन्हें या तो एक शब्दकोश कुंजी के रूप में उपयोग करने की आवश्यकता नहीं है।

मैं कभी नहीं देखा है कोड की तरह:

In [453]: dd={np.float64:12,d:34} 

In [454]: dd 
Out[454]: {dtype('float64'): 34, numpy.float64: 12} 

In [455]: dd[np.float64] 
Out[455]: 12 

In [456]: dd[d] 
Out[456]: 34 
+0

यह वही है जो मैं खोज रहा था। धन्यवाद! –

+0

dtype ऑब्जेक्ट्स में वास्तव में एक कस्टम '__hash__' कार्यान्वयन होता है, लेकिन यह स्पॉट करना मुश्किल है, क्योंकि' PyTypeObject' का 'tp_hash' फ़ील्ड एक [वास्तव में अजीब स्थान] में सेट है (https://github.com/numpy/ numpy/ब्लॉब/d4eaa2c01801ca2ce46b0c8b345367a54c8dde4b/numpy/कोर/src/multiarray/multiarraymodule.C# L4599)। आप कार्यान्वयन [यहां] देख सकते हैं (https://github.com/numpy/numpy/blob/c11628abd820a1f44b052ea87af810f8f00cf2e4/numpy/core/src/multiarray/hashdescr.c#L297)। – user2357112

संबंधित मुद्दे