"है" ऑपरेटर पूर्णांक के साथ अप्रत्याशित व्यवहार करता है


509

निम्नलिखित पायथन में अप्रत्याशित रूप से व्यवहार क्यों करता है?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

मैं अजगर 2.5.2 का उपयोग कर रहा हूं। पायथन के कुछ अलग संस्करणों की कोशिश करते हुए, ऐसा प्रतीत होता है कि पायथन 2.3.3 99 और 100 के बीच के उपरोक्त व्यवहार को दर्शाता है।

उपरोक्त के आधार पर, मैं अनुमान लगा सकता हूं कि पायथन आंतरिक रूप से इस तरह से लागू किया जाता है कि "छोटे" पूर्णांक बड़े पूर्णांकों की तुलना में एक अलग तरीके से संग्रहीत होते हैं और isऑपरेटर अंतर बता सकता है। क्यों टपका अमंगल? दो मनमाना वस्तुओं की तुलना करने का एक बेहतर तरीका यह देखने के लिए है कि क्या वे समान हैं जब मुझे पहले से पता नहीं है कि वे संख्याएं हैं या नहीं?


1
यहां एक नज़र डालें > वर्तमान कार्यान्वयन -5 और 256 के बीच पूर्णांक ऑब्जेक्ट के लिए पूर्णांक ऑब्जेक्ट्स की एक सरणी रखता है, जब आप उस सीमा में एक इंट बनाते हैं तो आप वास्तव में केवल मौजूदा ऑब्जेक्ट का संदर्भ प्राप्त करते हैं।
user5319825

2
यह एक सीपीथॉन-विशिष्ट कार्यान्वयन विवरण और अपरिभाषित व्यवहार है, जिसमें
सावधानी बरतें

जवाबों:


392

इस पर एक नजर डालिए:

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

यहाँ मुझे पायथन 2 के दस्तावेज़, "प्लेन इंटेगर ऑब्जेक्ट्स" में मिला (यह पायथन 3 के लिए भी ऐसा ही है ):

वर्तमान कार्यान्वयन -5 और 256 के बीच सभी पूर्णांकों के लिए पूर्णांक ऑब्जेक्ट्स की एक सरणी रखता है, जब आप उस सीमा में एक इंट बनाते हैं तो आप वास्तव में मौजूदा ऑब्जेक्ट के लिए एक संदर्भ प्राप्त करते हैं। इसलिए 1. मान को बदलना संभव होना चाहिए। मुझे संदेह है कि इस मामले में अजगर के व्यवहार अपरिभाषित हैं। :-)


46
क्या किसी को पता है कि उस रेंज (-5, 256) को कैसे चुना गया? मुझे बहुत आश्चर्य नहीं होगा अगर यह (0, 255) या सम -255, 255) थे, लेकिन -5 पर शुरू होने वाली 262 संख्याओं की एक सीमा आश्चर्यजनक रूप से मनमाना लगती है।
वुड्रो बारलो

6
@WoodrowBarlow: -5 आम नकारात्मक प्लेसहोल्डर्स को पकड़ने के लिए सिर्फ एक अनुमान है, मुझे लगता है। एकल बाइट मान के 0..255 कवर सरणियाँ। यह 256 रहस्यमय है, लेकिन मुझे लगता है कि यह (dis) बाइट्स में / से बाइट्स को असेंबल करने के लिए है।
डेविस हेरिंग 1

3
मेरी समझ से कई परियोजनाओं (और कई भाषाओं) में आमतौर पर उपयोग किए जाने वाले मूल्यों को देखकर सीमा को चुना गया था।
टोनी सफ़ोकल 66

9
Reddit.com/r/Python/comments/18leav/… के अनुसार , इस श्रेणी का उपयोग [-5,100] के लिए किया जाता था। यह बाइट मानों की पूरी श्रृंखला - प्लस 256 को शामिल करने के लिए विस्तारित किया गया था, क्योंकि यह संभवतः एक सामान्य संख्या है।
mwfearnley

2
@ अश्वनी ने आपकी टिप्पणी के ठीक बाद की टिप्पणियों को पढ़ने की कोशिश की, दो साल पहले आपकी पोस्ट की थी, और आपको अपने प्रश्न का उत्तर मिल जाएगा।
jbg

116

अजगर "है" ऑपरेटर पूर्णांक के साथ अप्रत्याशित व्यवहार करता है?

सारांश में - मुझे जोर देने दें: पूर्णांक की तुलना करने के लिए उपयोग न करें is

यह वह व्यवहार नहीं है जिसके बारे में आपको कोई अपेक्षा होनी चाहिए।

इसके बजाय, क्रमशः समानता और असमानता के लिए उपयोग ==और !=तुलना करना। उदाहरण के लिए:

>>> a = 1000
>>> a == 1000       # Test integers like this,
True
>>> a != 5000       # or this!
True
>>> a is 1000       # Don't do this! - Don't use `is` to test integers!!
False

व्याख्या

यह जानने के लिए, आपको निम्नलिखित जानने की आवश्यकता है।

पहला, क्या करता isहै? यह तुलना ऑपरेटर है। से प्रलेखन :

ऑब्जेक्ट आइडेंटिटी के लिए ऑपरेटर isऔर is notटेस्ट: x is yसच है अगर और केवल एक्स और वाई एक ही ऑब्जेक्ट हैं। x is not yउलटा सत्य मूल्य देता है।

और इसलिए निम्नलिखित समतुल्य हैं।

>>> a is b
>>> id(a) == id(b)

से प्रलेखन :

id किसी वस्तु की "पहचान" लौटाएं। यह एक पूर्णांक (या लंबा पूर्णांक) है जो अपने जीवनकाल के दौरान इस वस्तु के लिए अद्वितीय और स्थिर रहने की गारंटी है। गैर-अतिव्यापी जीवनकाल वाली दो वस्तुओं का समान id()मूल्य हो सकता है ।

ध्यान दें कि तथ्य यह है कि सीपीथॉन में एक वस्तु की आईडी (पायथन के संदर्भ कार्यान्वयन) स्मृति में स्थान है एक कार्यान्वयन विवरण है। पायथन के अन्य कार्यान्वयन (जैसे कि ज्योथन या आयरनपिथॉन) के लिए आसानी से एक अलग कार्यान्वयन हो सकता है id

तो इसके लिए उपयोग-मामला क्या है is? PEP8 का वर्णन है :

Noneहमेशा की तरह एकल ऑपरेटरों के साथ तुलना की जानी चाहिए isया is notकभी भी समानता ऑपरेटरों के साथ नहीं होनी चाहिए ।

प्रश्न

आप पूछते हैं, और राज्य, निम्नलिखित प्रश्न (कोड के साथ):

निम्नलिखित पायथन में अप्रत्याशित रूप से व्यवहार क्यों करता है?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

यह है नहीं एक अपेक्षित परिणाम। इसकी उम्मीद क्यों है? इसका केवल यह अर्थ है कि पूर्णांक 256दोनों के संदर्भ में मूल्यवान हैं aऔर bपूर्णांक के एक ही उदाहरण हैं। पायथन में इंटेगर अपरिवर्तनीय हैं, इस प्रकार वे बदल नहीं सकते हैं। इससे किसी भी कोड पर कोई प्रभाव नहीं पड़ना चाहिए। इसकी उम्मीद नहीं की जानी चाहिए। यह एक कार्यान्वयन विवरण मात्र है।

लेकिन शायद हमें खुशी होनी चाहिए कि हर बार स्मृति में एक नया अलग उदाहरण नहीं होता है क्योंकि हम एक मान 256 के बराबर होते हैं।

>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?

ऐसा लगता है कि हमारे पास 257मेमोरी के मूल्य के साथ पूर्णांकों के दो अलग-अलग उदाहरण हैं । चूंकि पूर्णांक अपरिवर्तनीय हैं, इसलिए यह मेमोरी को बेकार कर देता है। चलो आशा करते हैं कि हम इसे बहुत बर्बाद नहीं कर रहे हैं। हम शायद नहीं हैं। लेकिन इस व्यवहार की गारंटी नहीं है।

>>> 257 is 257
True           # Yet the literal numbers compare properly

ठीक है, ऐसा लगता है कि पायथन का आपका विशेष कार्यान्वयन स्मार्ट होने की कोशिश कर रहा है और स्मृति में अनावश्यक रूप से मूल्यवान पूर्णांक नहीं बना रहा है जब तक कि उसे नहीं करना है। आप इंगित करते हैं कि आप पायथन के रेफ़रेंट कार्यान्वयन का उपयोग कर रहे हैं, जो कि सीपीथॉन है। सीपीथॉन के लिए अच्छा है।

सीपीयथॉन वैश्विक स्तर पर ऐसा कर सकता है, अगर यह इतना सस्ते में हो सकता है (जैसा कि लुकअप में लागत होगी), शायद एक और कार्यान्वयन हो सकता है।

लेकिन कोड पर प्रभाव के लिए, आपको परवाह नहीं करनी चाहिए कि पूर्णांक पूर्णांक का एक विशेष उदाहरण है। आपको केवल इस बात पर ध्यान देना चाहिए कि उस उदाहरण का मूल्य क्या है, और आप इसके लिए सामान्य तुलना ऑपरेटरों का उपयोग करेंगे, अर्थात ==

क्या isकरता है

isजाँच करता है कि idदो वस्तुएं समान हैं। सीपीथॉन में, idस्मृति में स्थान है, लेकिन यह किसी अन्य कार्यान्वयन में विशिष्ट रूप से पहचान करने वाली संख्या हो सकती है। कोड के साथ इसे पुनर्स्थापित करने के लिए:

>>> a is b

के समान है

>>> id(a) == id(b)

isफिर हम क्यों इस्तेमाल करना चाहेंगे ?

यह कहने के सापेक्ष बहुत तेज़ जाँच हो सकती है, अगर दो बहुत लंबे तार बराबर हैं तो जाँच। लेकिन चूंकि यह वस्तु की विशिष्टता पर लागू होता है, इसलिए हमारे पास इसके लिए सीमित उपयोग के मामले हैं। वास्तव में, हम ज्यादातर इसे जांचने के लिए उपयोग करना चाहते हैं None, जो एक सिंगलटन (स्मृति में एक स्थान पर मौजूद एकमात्र उदाहरण) है। यदि हम उन्हें स्वीकार करने की क्षमता रखते हैं, तो हम अन्य एकल भी बना सकते हैं, जिसे हम जांच सकते हैं is, लेकिन ये अपेक्षाकृत दुर्लभ हैं। यहाँ एक उदाहरण है (पायथन 2 और 3 में काम करेगा) उदा

SENTINEL_SINGLETON = object() # this will only be created one time.

def foo(keyword_argument=None):
    if keyword_argument is None:
        print('no argument given to foo')
    bar()
    bar(keyword_argument)
    bar('baz')

def bar(keyword_argument=SENTINEL_SINGLETON):
    # SENTINEL_SINGLETON tells us if we were not passed anything
    # as None is a legitimate potential argument we could get.
    if keyword_argument is SENTINEL_SINGLETON:
        print('no argument given to bar')
    else:
        print('argument to bar: {0}'.format(keyword_argument))

foo()

कौन सा प्रिंट:

no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz

और इसलिए हम देखते हैं, isएक प्रहरी के साथ, हम इस बात में अंतर करने में सक्षम हैं कि कब barबिना किसी तर्क के साथ कहा जाता है और कब इसे कहा जाता है None। ये प्राथमिक उपयोग के मामले हैं is- पूर्णांक, स्ट्रिंग्स, ट्यूपल्स या इन जैसी अन्य चीजों की समानता के लिए परीक्षण करने के लिए इसका उपयोग करें।


"ये प्राथमिक उपयोग के मामले हैं is- पूर्णांक, स्ट्रिंग्स, ट्यूपल्स या इन जैसी अन्य चीजों की समानता के लिए परीक्षण करने के लिए इसका उपयोग न करें।" हालाँकि, मैं एक साधारण स्टेट मशीन को अपनी कक्षा में एकीकृत करने की कोशिश कर रहा हूँ, और चूंकि राज्य अपारदर्शी मान हैं, जिनकी एकमात्र अवलोकनीय संपत्ति समान या अलग-अलग होने के कारण, उनके साथ तुलनात्मक होना काफी स्वाभाविक लगता है is। मैं राज्यों के रूप में नजरबंद तारों का उपयोग करने की योजना बना रहा हूं। मैंने सादे पूर्णांक पसंद किए होंगे, लेकिन दुर्भाग्य से पायथन पूर्णांकों 0 is 0को लागू नहीं कर सकता ( यह एक कार्यान्वयन विवरण है)।
एलेक्सी

@ एसेक्स लगता है जैसे आपको एनम की जरूरत है? stackoverflow.com/questions/37601644/...
हारून हॉल

हो सकता है, धन्यवाद, उनमें से पता नहीं था। यह IMO का उत्तर देने के लिए एक उपयुक्त जोड़ हो सकता है।
एलेक्सी

हो सकता है कि आपके जवाब में प्रहरी की तरह कई गूंगी वस्तुओं का उपयोग करना अधिक हल्का समाधान होगा ...
एलेक्सी

@Alexey enums पायथन 3 मानक पुस्तकालय में हैं, और यह शायद आपके कोड को नंगे संतों की तुलना में थोड़ा अधिक सार्थक होने के लिए प्रोत्साहित करेगा।
हारून हॉल

60

यह इस बात पर निर्भर करता है कि आप देख रहे हैं कि क्या 2 चीजें समान हैं, या एक ही वस्तु।

isयह देखने के लिए जाँच करता है कि क्या वे समान वस्तु हैं, न के बराबर। छोटे ints संभवतः अंतरिक्ष दक्षता के लिए एक ही स्मृति स्थान की ओर इशारा कर रहे हैं

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

आपको ==मनमानी वस्तुओं की समानता की तुलना करने के लिए उपयोग करना चाहिए । आप के साथ व्यवहार निर्दिष्ट कर सकते हैं __eq__, और __ne__विशेषताएँ।


अंगूठे वास्तव में यह समझाते हैं कि मनमानी वस्तुओं की तुलना कैसे करें, जैसे ओपी ने पूछा !!
जूइयॉ

54

मुझे देर हो गई लेकिन, आप अपने उत्तर के साथ कुछ स्रोत चाहते हैं? मैं कोशिश करूँगा और इसे एक परिचयात्मक तरीके से बोलूंगा ताकि अधिक से अधिक लोग साथ चल सकें।


सीपीथॉन के बारे में एक अच्छी बात यह है कि आप वास्तव में इसके लिए स्रोत देख सकते हैं। मैं 3.5 रिलीज के लिए लिंक का उपयोग करने जा रहा हूं , लेकिन संबंधित 2.x वाले को ढूंढना तुच्छ है।

CPython में, C-API फ़ंक्शन जो एक नई intऑब्जेक्ट बनाने का काम करता है PyLong_FromLong(long v)। इस समारोह के लिए विवरण है:

वर्तमान कार्यान्वयन -5 और 256 के बीच सभी पूर्णांकों के लिए पूर्णांक ऑब्जेक्ट्स की एक सरणी रखता है, जब आप उस सीमा में एक इंट बनाते हैं तो आप वास्तव में मौजूदा ऑब्जेक्ट के लिए एक संदर्भ प्राप्त करते हैं । इसलिए 1. मान को बदलना संभव होना चाहिए। मुझे संदेह है कि इस मामले में अजगर के व्यवहार अपरिभाषित हैं। :-)

(मेरे विचार)

आपके बारे में नहीं जानते हैं, लेकिन मैं इसे देखता हूं और सोचता हूं: चलो उस सरणी को ढूंढें!

यदि आपने सीपी कोड को लागू करने वाले सी कोड से नहीं जोड़ा है, तो आपको चाहिए ; सब कुछ बहुत व्यवस्थित और पठनीय है। हमारे मामले के लिए, हम में देखने की जरूरत है Objectsउपनिर्देशिका का मुख्य स्रोत कोड निर्देशिका वृक्ष

PyLong_FromLonglongवस्तुओं के साथ सौदा करना इसलिए यह मुश्किल नहीं होना चाहिए कि हमें अंदर झांकना चाहिए longobject.c। अंदर देखने के बाद आप सोच सकते हैं कि चीजें अराजक हैं; वे कर रहे हैं, लेकिन डर नहीं है, हम जिस कार्य की तलाश कर रहे हैं, वह लाइन 230 पर चिल कर रहा है, हमें इसकी जांच करने के लिए इंतजार कर रहा है। यह एक छोटा सा कार्य है इसलिए मुख्य निकाय (घोषणाओं को छोड़कर) को आसानी से यहाँ चिपकाया जाता है:

PyObject *
PyLong_FromLong(long ival)
{
    // omitting declarations

    CHECK_SMALL_INT(ival);

    if (ival < 0) {
        /* negate: cant write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
    }

    /* Fast path for single-digit ints */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v; 
}

अब, हम कोई सी मास्टर-कोड- haxxorz नहीं हैं, लेकिन हम भी गूंगे नहीं हैं, हम उस CHECK_SMALL_INT(ival);झाँकते हुए हम सब को बहुत तेजी से देख सकते हैं ; हम समझ सकते हैं कि इसके साथ कुछ करना है। चलो पता करते हैं:

#define CHECK_SMALL_INT(ival) \
    do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
        return get_small_int((sdigit)ival); \
    } while(0)

तो यह एक मैक्रो है जो फ़ंक्शन को कॉल करता है get_small_intयदि मूल्य ivalस्थिति को संतुष्ट करता है:

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)

तो क्या NSMALLNEGINTSऔर क्या हैं NSMALLPOSINTS? मैक्रो! यहाँ वे हैं :

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

तो हमारी हालत if (-5 <= ival && ival < 257)कॉल है get_small_int

आगे हम get_small_intइसकी सभी महिमा पर नज़र डालते हैं (ठीक है, हम इसके शरीर को देखेंगे क्योंकि यह दिलचस्प चीजें हैं):

PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);

ठीक है, एक घोषणा करें PyObject, दावा करें कि पिछली स्थिति रखती है और कार्य को निष्पादित करती है:

v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];

small_intsउस सरणी की तरह बहुत कुछ दिखता है जिसे हम खोज रहे हैं, और यह है! हम सिर्फ लानत प्रलेखन पढ़ सकते हैं और हम सब के साथ पता होता! :

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

तो हाँ, यह हमारा लड़का है। जब आप intसीमा में एक नया बनाना चाहते हैं [NSMALLNEGINTS, NSMALLPOSINTS)तो आपको पहले से मौजूद किसी वस्तु का एक संदर्भ वापस मिल जाएगा जिसे प्रचारित किया गया है।

चूंकि संदर्भ एक ही वस्तु को संदर्भित करता है, id()सीधे जारी करना या isउस पर पहचान के लिए जांच करना बिल्कुल उसी चीज को वापस करेगा।

लेकिन, उन्हें कब आवंटित किया जाता है ??

_PyLong_Initपायथन में आरंभीकरण के दौरान ख़ुशी से एक लूप में प्रवेश करेंगे जो आपके लिए ऐसा करते हैं:

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {

लूप बॉडी को पढ़ने के लिए स्रोत की जाँच करें!

मुझे आशा है कि मेरे स्पष्टीकरण ने आपको सी चीजें स्पष्ट रूप से (वाक्य स्पष्ट रूप से इरादा) कर दी हैं।


लेकिन 257 is 257,? क्या हो रहा है?

यह वास्तव में समझाने में आसान है, और मैंने पहले ही ऐसा करने का प्रयास किया है ; यह इस तथ्य के कारण है कि पायथन इस संवादात्मक बयान को एक ही ब्लॉक के रूप में निष्पादित करेगा:

>>> 257 is 257

इस कथन की शिकायत के दौरान, सीपीथॉन देखेंगे कि आपके पास दो मिलान शाब्दिक हैं और उसी का PyLongObjectप्रतिनिधित्व करेंगे 257। आप इसे देख सकते हैं यदि आप संकलन स्वयं करते हैं और इसकी सामग्री की जांच करते हैं:

>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)

जब CPython ऑपरेशन करता है, तो यह अब ठीक उसी वस्तु को लोड करने जा रहा है:

>>> import dis
>>> dis.dis(codeObj)
  1           0 LOAD_CONST               0 (257)   # dis
              3 LOAD_CONST               0 (257)   # dis again
              6 COMPARE_OP               8 (is)

तो isलौट आएगा True


37

जैसा कि आप स्रोत फ़ाइल intobject.c में देख सकते हैं , पायथन दक्षता के लिए छोटे पूर्णांक को कैश करता है। हर बार जब आप एक छोटे पूर्णांक का संदर्भ बनाते हैं, तो आप कैश्ड छोटे पूर्णांक का उल्लेख कर रहे होते हैं, नई वस्तु का नहीं। 257 एक छोटा पूर्णांक नहीं है, इसलिए इसकी गणना एक अलग वस्तु के रूप में की जाती है।

==उस उद्देश्य के लिए उपयोग करना बेहतर है ।


19

मुझे लगता है कि आपकी परिकल्पना सही है। प्रयोग id(वस्तु की पहचान) के साथ:

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

ऐसा प्रतीत होता है कि संख्याओं <= 255को शाब्दिक माना जाता है और ऊपर कुछ भी अलग तरह से व्यवहार किया जाता है!


1
ऐसा इसलिए है क्योंकि ५-५ से ५:६६ तक मूल्यों का प्रतिनिधित्व करने वाले ऑब्जेक्ट्स स्टार्टअप समय पर बनाए जाते हैं - और इसलिए उन सभी मूल्य का उपयोग पूर्वनिर्मित वस्तु के लिए उपयोग करते हैं। उस सीमा के बाहर पूर्णांकों के लगभग सभी संदर्भ एक नई आंतरिक वस्तु बनाते हैं जो हर बार संदर्भित होते हैं। मुझे लगता है कि शाब्दिक शब्द का उपयोग भ्रामक है - शाब्दिक सामान्यतः किसी भी मूल्य को संदर्भित करता है जो कोड के एक टुकड़े में टाइप किया जाता है - इसलिए स्रोत कोड में सभी संख्याएं शाब्दिक हैं।
टोनी सफ़ोकल 66

13

अपरिवर्तनीय मूल्य वस्तुओं के लिए, जैसे ints, strings या datetimes, ऑब्जेक्ट पहचान विशेष रूप से उपयोगी नहीं है। समानता के बारे में सोचना बेहतर है। पहचान अनिवार्य रूप से मूल्य वस्तुओं के लिए एक कार्यान्वयन विवरण है - क्योंकि वे अपरिवर्तनीय हैं, एक ही वस्तु या कई वस्तुओं के कई संदर्भ होने के बीच कोई प्रभावी अंतर नहीं है।


12

एक और मुद्दा है जो मौजूदा उत्तरों में से किसी में भी इंगित नहीं किया गया है। पायथन को किसी भी दो अपरिवर्तनीय मूल्यों को मर्ज करने की अनुमति है, और पूर्व-निर्मित छोटे अंतर मान केवल ऐसा नहीं हो सकता है। एक पायथन कार्यान्वयन को कभी भी ऐसा करने की गारंटी नहीं दी जाती है, लेकिन वे सभी इसे केवल छोटे ints से अधिक के लिए करते हैं।


एक बात के लिए, कुछ अन्य पूर्व-निर्मित मान हैं, जैसे कि खाली tuple, strऔर bytes, और कुछ छोटे तार (CPython 3.6 में, यह 256 एकल-वर्ण लैटिन -1 स्ट्रिंग्स है)। उदाहरण के लिए:

>>> a = ()
>>> b = ()
>>> a is b
True

लेकिन साथ ही, गैर-पूर्व-निर्मित मूल्य भी समान हो सकते हैं। इन उदाहरणों पर विचार करें:

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

और यह intमूल्यों तक सीमित नहीं है :

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

जाहिर है, सीपीथॉन इसके लिए पहले से बनाए गए floatमूल्य के साथ नहीं आता है 42.23e100। तो, यहाँ क्या हो रहा है?

CPython संकलक की तरह कुछ ज्ञात-अपरिवर्तनीय प्रकार के निरंतर मूल्यों में मर्ज हो जाएगी int, float, str, bytes, एक ही संकलन इकाई में। एक मॉड्यूल के लिए, पूरा मॉड्यूल एक संकलन इकाई है, लेकिन इंटरैक्टिव दुभाषिया में, प्रत्येक कथन एक अलग संकलन इकाई है। चूंकि cऔर dअलग-अलग बयानों में परिभाषित किए गए हैं, उनके मूल्यों का विलय नहीं किया गया है। चूंकि eऔर fएक ही बयान में परिभाषित किया गया है, उनके मूल्यों को मिला दिया जाता है।


आप देख सकते हैं कि बाइटकोड को डिसाइड करके क्या चल रहा है। एक फ़ंक्शन को परिभाषित करने की कोशिश करें जो उस पर e, f = 128, 128कॉल करता है और फिर आपको बताता है dis.disकि एक निरंतर मूल्य है(128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

आप देख सकते हैं कि संकलक ने 128एक स्थिरांक के रूप में संग्रहीत किया है, भले ही यह वास्तव में बाईटेकोड द्वारा उपयोग नहीं किया गया है, जो आपको यह अनुमान लगाता है कि सीपीथॉन का संकलक कितना कम अनुकूलन करता है। जिसका अर्थ है कि (गैर-खाली) टुपल्स वास्तव में विलीन नहीं होते हैं:

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

रखो कि एक समारोह में, disयह, और कम से देखो co_consts, वहाँ एक है 1और एक 2, दो (1, 2)tuples ही हिस्सा है कि 1और 2लेकिन समान नहीं हैं, और एक ((1, 2), (1, 2))टपल दो अलग-अलग समान tuples है।


एक और अनुकूलन है जो सीपीथॉन करता है: स्ट्रिंग इंटर्निंग। कंपाइलर निरंतर तह के विपरीत, यह स्रोत कोड शाब्दिकों तक सीमित नहीं है:

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

दूसरी ओर, यह strप्रकार तक सीमित है , और आंतरिक भंडारण प्रकार "एससीआई कॉम्पैक्ट", "कॉम्पैक्ट", या "विरासत तैयार" के तार के लिए , और कई मामलों में केवल "एएससीआई कॉम्पैक्ट" को नजरबंद कर दिया जाएगा।


किसी भी दर पर, मान क्या होना चाहिए, के लिए नियम हो सकता है, या कार्यान्वयन से कार्यान्वयन के लिए, और एक ही कार्यान्वयन के संस्करणों के बीच भिन्न नहीं हो सकता है, और शायद एक ही कोड के एक ही कोड के रन के बीच भी हो सकता है ।

यह इसके लिए एक विशिष्ट पायथन के नियमों को सीखने के लायक हो सकता है। लेकिन यह आपके कोड में उन पर भरोसा करने लायक नहीं है। एकमात्र सुरक्षित नियम है:

  • ऐसा कोड न लिखें जो दो समान हो, लेकिन अलग-अलग बनाए गए अपरिवर्तनीय मान समान हों (उपयोग न करें x is y, उपयोग न करें x == y)
  • ऐसे कोड न लिखें जो दो समान हों, लेकिन अलग-अलग निर्मित अपरिवर्तनीय मान अलग-अलग हों (उपयोग न करें x is not y, उपयोग न करें x != y)

या, दूसरे शब्दों में, केवल isप्रलेखित एकल (जैसे None) के लिए परीक्षण करने के लिए उपयोग करते हैं या जो केवल कोड में एक जगह ( _sentinel = object()मुहावरे की तरह ) बनाए जाते हैं ।


कम गुप्त सलाह बस है: x is yतुलना करने के लिए उपयोग नहीं करते , का उपयोग करें x == y। इसी तरह का उपयोग नहीं करते हैं x is not y, का उपयोग करेंx != y
smci

तो इस सवाल को देखते हुए , a=257; b=257एक ही लाइन पर क्यों है a is bट्रू
जो

8

is है पहचान समानता ऑपरेटर (जैसे कार्य कर रहाid(a) == id(b) ); यह सिर्फ इतना है कि दो समान संख्याएं समान वस्तु नहीं हैं। प्रदर्शन कारणों से कुछ छोटे पूर्णांक याद रखने योग्य हो जाते हैं, इसलिए वे समान होंगे (ऐसा तब किया जा सकता है क्योंकि वे अचल हैं)।

===दूसरी ओर, PHP के ऑपरेटर को समानता और प्रकार की जाँच के रूप में वर्णित किया गया है: x == y and type(x) == type(y)पाउलो फ्रीटास की टिप्पणी के अनुसार। यह सामान्य संख्याओं के लिए पर्याप्त होगा, लेकिन इससे भिन्न होगाis उन कक्षाओं के लिए__eq__ जो एक बेतुके तरीके से परिभाषित करते हैं:

class Unequal:
    def __eq__(self, other):
        return False

PHP जाहिरा तौर पर "बिल्ट-इन" कक्षाओं (जो मैं सी स्तर पर कार्यान्वित करने के लिए लेती हूं, पीएचपी में नहीं) के लिए एक ही चीज़ की अनुमति देता है। थोड़ा कम बेतुका उपयोग एक टाइमर ऑब्जेक्ट हो सकता है, जिसका नंबर के रूप में उपयोग किए जाने पर हर बार एक अलग मूल्य होता है। काफी क्यों आप विजुअल बेसिक का अनुकरण करना चाहते हैं Nowयह दिखाने के बजाय कि यह एक मूल्यांकन है जिसके साथ time.time()मुझे नहीं पता है।

ग्रेग हेवगिल (ओपी) ने एक स्पष्ट टिप्पणी की "मेरा लक्ष्य मूल्य की समानता के बजाय वस्तु पहचान की तुलना करना है। संख्याओं को छोड़कर, जहां मैं वस्तु की पहचान को मूल्य की समानता के रूप में व्यवहार करना चाहता हूं।"

इसका एक और जवाब होगा, क्योंकि हमें चीजों को संख्याओं के रूप में वर्गीकृत करना होगा या नहीं, यह चयन करने के लिए कि हम साथ तुलना करते हैं ==या नहीं isCPython PyNumber_Check सहित संख्या प्रोटोकॉल को परिभाषित करता है , लेकिन यह पायथन से ही सुलभ नहीं है।

हम isinstanceउन सभी प्रकारों के साथ उपयोग करने का प्रयास कर सकते हैं जिन्हें हम जानते हैं, लेकिन यह अनिवार्य रूप से अधूरा होगा। प्रकार मॉड्यूल में एक StringTypes सूची होती है, लेकिन कोई NumberTypes नहीं। पायथन 2.6 के बाद से, संख्या वर्गों में निर्मित एक आधार वर्ग है numbers.Number, लेकिन इसमें एक ही समस्या है:

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

वैसे, NumPy कम संख्या के अलग-अलग उदाहरणों का उत्पादन करेगा।

मुझे वास्तव में इस प्रश्न के इस प्रकार का उत्तर नहीं पता है। मुझे लगता है कि एक व्यक्ति कॉल करने के लिए सैद्धांतिक रूप से ctypes का उपयोग कर सकता है PyNumber_Check, लेकिन यहां तक ​​कि उस फ़ंक्शन पर बहस की गई है , और यह निश्चित रूप से पोर्टेबल नहीं है। हम अभी के लिए हम क्या परीक्षण के बारे में कम विशेष होना चाहिए।

अंत में, इस मुद्दे अजगर मूल रूप से की तरह विधेय के साथ एक प्रकार का पेड़ नहीं होने की वजह से उपजी योजना के number? , या हास्केल के प्रकार वर्ग अंकisवस्तु पहचान की जाँच करता है, समानता का मूल्य नहीं। PHP का एक रंगीन इतिहास भी है, जहाँ ===जाहिरा तौर isपर PHP5 में वस्तुओं पर ही व्यवहार होता है, लेकिन PHP4 में नहीं । ऐसी भाषाओं के बढ़ने के बढ़ते दर्द (एक के संस्करण सहित) हैं।


4

यह तार के साथ भी होता है:

>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

अब सब कुछ ठीक लग रहा है।

>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

वह भी अपेक्षित है।

>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)

>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)

अब यह अप्रत्याशित है।


इस पर हुआ - सहमत, कि भी अजीब। तो मैं इसके साथ खेला, और यह अभी तक अजीब है - अंतरिक्ष से संबंधित। उदाहरण के लिए, स्ट्रिंग 'xx'अपेक्षित है, जैसा है 'xxx', लेकिन 'x x'नहीं है।
ब्रायन

2
ऐसा इसलिए है क्योंकि यह एक प्रतीक की तरह दिखता है अगर इसमें कोई जगह नहीं है। नामों को स्वचालित रूप से इंटर्न किया जाता है, इसलिए यदि xxआपके पायथन सत्र में कहीं भी कुछ भी नाम दिया गया है, तो वह स्ट्रिंग पहले से ही इंटर्न है; और एक ऐसा अनुमान हो सकता है कि अगर यह सिर्फ एक नाम जैसा है। संख्या के साथ, यह किया जा सकता है क्योंकि वे अपरिवर्तनीय हैं। docs.python.org/2/library/functions.html#intern guilload.com/python-string-interning
Yann Vernier

3

पायथन 3.8 में नया क्या है: पायथन व्यवहार में परिवर्तन :

कंपाइलर अब एक सिंटेक्सवर्निंग का उत्पादन करता है जब कुछ विशेष प्रकार के शाब्दिक (जैसे स्ट्रिंग्स, इनट्स ) के साथ पहचान जांच ( isऔर is not) का उपयोग किया जाता है। ये अक्सर सीपीथॉन में दुर्घटना से काम कर सकते हैं, लेकिन भाषा की कल्पना की गारंटी नहीं है। चेतावनी उपयोगकर्ताओं को इसके बजाय समानता परीक्षण ( == और !=) का उपयोग करने की सलाह देती है ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.