13

तो, मेरे पास सीरियल एपीआई के लिए बड़ी संख्या में संदेश पेलोड कक्षाएं हैं, जिनमें से प्रत्येक में कई अपरिवर्तनीय फ़ील्ड, एक पार्स विधि और साझा की जाने वाली कुछ विधियां हैं। जिस तरह से मैं इसे संरचित कर रहा हूं वह यह है कि प्रत्येक क्षेत्र के व्यवहार के लिए नामांकित से उत्तराधिकारी होगा, और माता-पिता वर्ग से सामान्य तरीकों को प्राप्त करेगा। हालांकि, मैं कंस्ट्रक्टर्स के साथ कुछ कठिनाइयां आ रही हूँ:पायथन में, मैं सुपर क्लास को कैसे कॉल करूं जब यह एक-ऑफ नामित है?

class Payload: 
    def test(self): 
     print("bar") 

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')): 
    __slots__ =() 
    def __init__(self, **kwargs): 
     super(DifferentialSpeed, self).__init__(**kwargs) 
     # TODO: Field verification 
     print("foo") 

    @classmethod 
    def parse(self, raw): 
     # Dummy for now 
     return self(left_speed = 0.0, right_speed = 0.1, 
        left_accel = 0.2, right_accel = 0.3) 

    def __str__(self): 
     return "Left Speed: %fm/s\nRight Speed: %fm/s\n"\ 
      "Left Acceleration: %fm/s^2\nRight Acceleration: %fm/s^2" % (
      self.left_speed, self.right_speed, self.left_accel, self.right_accel) 


payload = DifferentialSpeed.parse('dummy') 
print(payload) 

यह काम करता है, लेकिन मैं निम्नलिखित चेतावनी मिलती है: यदि मैं कॉल से **kwargs हटाने

DeprecationWarning: object.__init__() takes no parameters 
    super(DifferentialSpeed, self).__init__(**kwargs) 

, यह अभी भी काम करने के लिए लगता है, लेकिन क्यों? कन्स्ट्रक्टर को नामांकित करने के लिए उन तर्कों को कैसे पारित किया जा रहा है? क्या यह गारंटी है, या mro कैसे स्थापित किया गया है इसका यादृच्छिक परिणाम है?

अगर मैं सुपर से दूर रहना चाहता हूं, और इसे पुराना तरीका करना चाहता हूं, तो क्या मैं अपने कन्स्ट्रक्टर को कॉल करने के लिए नामांकित तक पहुंच सकता हूं? मुझे यह करने की ज़रूरत नहीं है:

DifferentialSpeed_ = namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel') 
class DifferentialSpeed(Payload, DifferentialSpeed_): 

वर्बोज़ और अनावश्यक लगता है।

यहां मेरा सबसे अच्छा तरीका क्या है? क्योंकि समय __init__ द्वारा वस्तु पहले से ही निर्माण किया है कहा जाता है

+1

ध्यान दें कि आप 'namedtuple' उपयोग करने के लिए स्मृति को बचाने के लिए कोशिश कर रहे हैं, तो आप' निर्धारित करने की आवश्यकता __slots__ =() 'व्युत्पन्न वर्ग के साथ ही अन्य विरासत में मिला' Payload' वर्ग, या कक्षा में अभी भी '__dict__' होगा। –

उत्तर

26

शुरुआत के लिए, tuple से namedtuple(whatever) इनहेरिट करती है, जो अपरिवर्तनीय है, और अपरिवर्तनीय प्रकार, __init__ से परेशान नहीं है। यदि आप namedtuple बेस क्लास में तर्क पारित करना चाहते हैं तो आपको इसके बजाय __new__ ओवरराइड करना होगा।

verbose=true तर्क में गुजरकर आप namedtuple() के परिणाम की परिभाषा देख सकते हैं; मुझे यह शैक्षिक लगता है।

+0

ओह, ठीक है। बहुत ही रोचक। चीजों को नामित करने के बारे में मुझसे कोई फर्क नहीं पड़ता; मैं बस यह सुनिश्चित करना चाहता था कि यह अपने तर्कों से चूकने वाला नहीं है। शायद मैं पूरी सुपर() समस्या को पूरी तरह से अनदेखा कर सकता हूं। – mikepurvis

+0

ठीक है, सहायक और संक्षिप्त होने के लिए, इसे स्वीकार करने वाला। मुझे लगता है कि अंत में, मैं शायद पेलोड में एक सामान्य कन्स्ट्रक्टर के साथ जाऊंगा, जो एक पॉलिमॉर्फिक "self.verify()" कहलाता है, जो तब आवश्यकतानुसार अपवाद फेंक सकता है। – mikepurvis

5

आपके पास तीन आधार वर्ग हैं: Payload, आपके नामांकित DifferentialSpeed_, और सामान्य आधार वर्ग object। पहले दो में से कोई भी __init__ फ़ंक्शन बिल्कुल नहीं है, जिसे object से विरासत में मिला है। namedtuple को __init__ की आवश्यकता नहीं है, क्योंकि अपरिवर्तनीय कक्षाओं की शुरुआत __new__ द्वारा की जाती है, जिसे __init__ से पहले कहा जाता है।

super(DifferentialSpeed, self).__init__ के बाद से अगले __init__ कॉल श्रृंखला में ले कर जाता है, अगले __init__ जिसका मतलब है कि आपको लगता है कि कार्य करने के लिए तर्क दे रहे object.__init__ है। यह किसी की अपेक्षा नहीं करता है - object.__init__ पर तर्क पारित करने का कोई कारण नहीं है।

(यह स्वीकार करने के लिए और चुपचाप तर्क की उपेक्षा का इस्तेमाल किया है कि व्यवहार दूर जा रहा है -। जिसके कारण आप एक DeprecationWarning मिलता है - यह अजगर 3 में चला गया।) जोड़कर

आप समस्या को गति प्रदान कर सकते हैं और अधिक स्पष्ट रूप एक Payload.__init__ फ़ंक्शन जो कोई तर्क नहीं लेता है। जब आप `* kwargs के साथ पास करने का प्रयास करते हैं, तो यह एक त्रुटि उठाएगा।

इस मामले में करने के लिए सही चीज लगभग **kwargs तर्क को हटाने के लिए निश्चित रूप से है, और बस super(DifferentialSpeed, self).__init__() पर कॉल करें।यह कोई तर्क नहीं लेता है; DifferentialSpeedPayload गुजर रहा है अपने तर्क Payload, और कॉल श्रृंखला के आगे कार्य करता है, इसके बारे में कुछ भी नहीं पता है।

+0

आपका कथन "इस मामले में करने के लिए सही चीज लगभग ** ** kwargs' तर्क को हटाने के लिए निश्चित रूप से है, और केवल 'सुपर (डिफरेंशियल स्पीड, स्वयं) कॉल करें .__ init __ (** kwargs)' "विरोधाभासी प्रतीत होता है, नहीं होना चाहिए आखिरी हिस्सा "बस कॉल करें" सुपर (डिफरेंशियल स्पीड, स्वयं) .__ init __() '"? – martineau

+0

@ मार्टिनौ: बेशक, सिर्फ एक टाइपो। –

+1

यदि कोई और यहां आ जाता है, तो ध्यान दें कि इस चेतावनी में शामिल एक और कारण है जो दिखा रहा है या नहीं; ऑब्जेक्ट्स/typeobject.c में '__new__' के बारे में टिप्पणी देखें। (मुझे विस्तार से जाना पसंद नहीं है, क्योंकि ओपी ने इस जवाब को पूरी तरह से अनदेखा कर लिया है और जिसने अपने प्रश्न का उत्तर भी नहीं दिया है ...) –

3

जैसा कि अन्य ने इंगित किया है, टुपल्स एक अपरिवर्तनीय प्रकार हैं, जिन्हें उनके विधि के बजाय __new__() में प्रारंभ किया जाना चाहिए - इसलिए आपको अपने उप-वर्ग (और बाद वाले से छुटकारा पाने) में पूर्व को जोड़ना होगा। नीचे यह है कि यह आपके उदाहरण कोड पर कैसे लागू होगा। एकमात्र अन्य परिवर्तन शुरुआत में from import... कथन जोड़ रहा था।

नोट:cls क्योंकि यह एक स्थिर विधि है, हालांकि यह विशेष मामलों ताकि आप इसे घोषित करने के लिए एक होने की जरूरत नहीं है है __new__() में super() कॉल में दो बार पारित कर दिया जाना चाहिए।

from collections import namedtuple 

class Payload: 
    def test(self): 
     print("bar") 

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')): 
    #### NOTE: __new__ instead of an __init__ method #### 
    def __new__(cls, **kwargs): 
     self = super(DifferentialSpeed, cls).__new__(cls, **kwargs) 
     # TODO: Field verification 
     print("foo") 
     return self 

    @classmethod 
    def parse(self, raw): 
     # Dummy for now 
     return self(left_speed = 0.0, right_speed = 0.1, 
        left_accel = 0.2, right_accel = 0.3) 

    def __str__(self): 
     return "Left Speed: %fm/s\nRight Speed: %fm/s\n"\ 
      "Left Acceleration: %fm/s^2\nRight Acceleration: %fm/s^2" % (
      self.left_speed, self.right_speed, self.left_accel, self.right_accel) 


payload = DifferentialSpeed.parse('dummy') 
print(payload) 
+0

'__new__' को छूने की कोई आवश्यकता नहीं है। उसे केवल ऐसा करने की आवश्यकता होगी यदि वह उन तर्कों को संशोधित करना चाहता था जो 'DifferentialSpeed_' प्रारंभ में हैं; वह केवल उन्हें सत्यापित कर रहा है। –

+0

@Glenn Maynard: मुझे लगता है कि '__new __()' * * * शामिल होने की आवश्यकता है, इसलिए तर्कों को सत्यापित किया जा सकता है * इससे पहले * उन्हें tuple को सौंपा गया था (क्योंकि उन्हें बाद में बदला नहीं जा सकता है)। जैसे उन्हें डिफ़ॉल्ट मूल्य या किसी भी फर्जी असाइनमेंट के बिना उठाए गए अपवाद दिए जा सकते हैं। – martineau

+1

'__init__' से अपवाद उठाकर भी काम करता है। (चुपचाप एक डिफ़ॉल्ट मजबूर करना सायन व्यवहार की तरह नहीं लगता है।) –

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