मैं ऑब्जेक्ट की आवृत्ति के __class__ विशेषता को क्यों नहीं बदल सकता?


10
class A(object):
    pass

class B(A):
    pass

o = object()
a = A()
b = B()

जब मैं बदल सकता हूं a.__class__, मैं ऐसा नहीं कर सकता o.__class__(यह एक TypeErrorत्रुटि फेंकता है )। क्यों?

उदाहरण के लिए:

isinstance(a, A) # True
isinstance(a, B) # False
a.__class__ = B
isinstance(a, A) # True
isinstance(a, B) # True

isinstance(o, object) # True
isinstance(o, A) # False
o.__class__ = A # This fails and throws a TypeError
# isinstance(o, object)
# isinstance(o, A)

मुझे पता है कि यह आमतौर पर एक अच्छा विचार नहीं है, क्योंकि यह कुछ बहुत ही अजीब व्यवहार को जन्म दे सकता है अगर इसे गलत तरीके से संभाला जाए। यह सिर्फ जिज्ञासा के लिए है।


3
अंतर्निहित प्रकार दक्षता के कारणों के लिए उपयोगकर्ता-परिभाषित प्रकार की गतिशीलता का त्याग करते हैं। ध्यान दें, एक और वैकल्पिक अनुकूलन स्लॉट है, जो इसी तरह इसे रोक देगा।
— juanpa.arrivillaga

जवाबों:


6

इस विषय पर CPython ने ऑब्जेक्ट्स / typeobject.c में एक टिप्पणी की है :

3.5 से पहले के CPython के संस्करणों में, कोड compatible_for_assignmentको ठीक से मेमोरी लेआउट / स्लॉट / आदि के लिए गैर-HEAPTYPE वर्गों के लिए जांचने के लिए सेट नहीं किया गया था, इसलिए हमने __class__किसी भी मामले में असाइनमेंट को अस्वीकार कर दिया था जो HEAPTYPE>> HEAPTYPE नहीं था।

3.5 विकास चक्र के दौरान, हमने compatible_for_assignmentमनमाने प्रकारों के बीच संगतता को सही ढंग से जांचने के लिए कोड निर्धारित किया , और __class__सभी मामलों में असाइनमेंट की अनुमति देना शुरू कर दिया जहां पुराने और नए प्रकारों में वास्तव में संगत स्लॉट और मेमोरी लेआउट होते हैं (चाहे वे HEAPTYPEs के रूप में लागू किए गए हों) या नहीं)।

3.5 जारी होने से ठीक पहले, हालांकि, हमें पता चला कि इससे इंट जैसे अपरिवर्तनीय प्रकार की समस्याएं पैदा हुईं, जहां दुभाषिया मानता है कि वे अपरिवर्तनीय हैं और कुछ मूल्यों को नजरअंदाज करते हैं। पूर्व में यह कोई समस्या नहीं थी, क्योंकि वे वास्तव में अपरिवर्तनीय थे - विशेष रूप से, सभी प्रकार जहां दुभाषिया ने इस इंटर्निंग ट्रिक को लागू किया, वह भी सांख्यिकीय रूप से आवंटित किया गया था, इसलिए पुराने HEAPTYPE नियमों को "गलती से" __class__असाइनमेंट की अनुमति देकर रोक दिया गया था। लेकिन __class__असाइनमेंट में बदलाव के साथ , हमने कोड की अनुमति देना शुरू कर दिया

class MyInt(int):
#   ...
# Modifies the type of *all* instances of 1 in the whole program,
# including future instances (!), because the 1 object is interned.
 (1).__class__ = MyInt

(देखें https://bugs.python.org/issue24912 )

सिद्धांत रूप में उचित निर्धारण यह पहचानने के लिए होगा कि कौन से वर्ग इस अपरिवर्तनीय पर भरोसा करते हैं और किसी तरह __class__केवल उनके लिए असाइनमेंट को अस्वीकार करते हैं, शायद एक नए Py_TPFLAGS_IMMUTABLE ध्वज ("ब्लैकलिस्टिंग" दृष्टिकोण) जैसे कुछ तंत्र के माध्यम से। लेकिन व्यवहार में, चूंकि इस समस्या को 3.5 आरसी चक्र में देर से देखा नहीं गया था, हम रूढ़िवादी दृष्टिकोण ले रहे हैं और उसी HEAPTYPE-> HEAPTYPE की जांच कर रहे हैं जो हमारे पास है, साथ ही एक "श्वेतसूची" भी। अभी के लिए, श्वेतसूची में केवल मॉड्यूल टाइप उपप्रकार होते हैं, क्योंकि वे मामले हैं जिन्होंने पहली जगह में पैच को प्रेरित किया है - https://bugs.python.org/issue22986 देखें - और चूंकि मॉड्यूल ऑब्जेक्ट परस्पर भिन्न हैं, इसलिए हम सुनिश्चित हो सकते हैं कि वे निश्चित रूप से नजरबंद नहीं किए जा रहे हैं। तो अब हम HEAPTYPE-> HEAPTYPE या की अनुमति देते हैं मॉड्यूलप्रकार उपप्रकार -> मॉड्यूलटाइप उपप्रकार।

अब तक हम जानते हैं कि, 'अगर' कथन के बाद के सभी कोड गैर-HEAPTYPE वर्गों को सही ढंग से संभालेंगे, और HEAPTYPE चेक की जरूरत केवल गैर-HEAPTYPE वर्गों के उस सबसेट को बचाने के लिए है, जिसके लिए दुभाषिया ने इस धारणा में सेंध लगाई है कि सभी उदाहरण वास्तव में अपरिवर्तनीय हैं।

स्पष्टीकरण:

CPython वस्तुओं को दो तरीकों से संग्रहीत करता है:

ऑब्जेक्ट्स ढेर पर आवंटित संरचनाएं हैं। विशेष नियम वस्तुओं के उपयोग पर लागू होते हैं यह सुनिश्चित करने के लिए कि वे ठीक से कचरा एकत्र किए जाते हैं। वस्तुओं को कभी भी स्टैटिकली या स्टैक पर आवंटित नहीं किया जाता है; उन्हें विशेष मैक्रो और फ़ंक्शंस के माध्यम से ही एक्सेस किया जाना चाहिए। (टाइप ऑब्जेक्ट्स पहले नियम के अपवाद हैं; मानक प्रकारों को स्टेटिकली इनिशियलाइज्ड टाइप ऑब्जेक्ट्स द्वारा दर्शाया जाता है, हालांकि पायथन 2.2 के लिए टाइप / क्लास यूनिफिकेशन पर काम करने से ढेर-टाइप टाइप ऑब्जेक्ट्स भी संभव हो गए हैं।

टिप्पणी से सूचना में शामिल करें / object.h ।

जब आप एक नया मान सेट करने का प्रयास कर रहे हैं some_obj.__class__, तो object_set_classफ़ंक्शन को कहा जाता है। यह PyBaseObject_Type से विरासत में मिला है , /* tp_getset */फ़ील्ड देखें । यह फ़ंक्शन जाँचता है : क्या नया प्रकार पुराने प्रकार को बदल सकता है some_obj?

अपना उदाहरण लें:

class A:
    pass

class B:
    pass

o = object()
a = A() 
b = B() 

पहला मामला:

a.__class__ = B 

के प्रकार aवस्तु है A, ढेर प्रकार है, क्योंकि यह गतिशील रूप से आवंटित किया जाता है। साथ ही द B। aके प्रकार एक समस्या के बिना बदल जाता है।

दूसरा मामला:

o.__class__ = B

का प्रकार oअंतर्निहित प्रकार object( PyBaseObject_Type) है। यह ढेर प्रकार नहीं है, इसलिए TypeErrorउठाया जाता है:

TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.

4

आप केवल __class__दूसरे प्रकार में बदल सकते हैं जिसमें समान आंतरिक (C) लेआउट है । रनटाइम उस लेआउट को भी नहीं जानता है जब तक कि प्रकार स्वयं को गतिशील रूप से आवंटित नहीं किया जाता है (एक "हीप प्रकार"), इसलिए यह एक आवश्यक शर्त है जो अंतर्निहित प्रकारों को स्रोत या गंतव्य के रूप में शामिल नहीं करता है। आपके पास __slots__समान नामों के साथ समान सेट भी होना चाहिए ।

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