2011-02-03 20 views
8

अजगर 3 एक में super() बजाय super(MyClass, self), लेकिन तरीकों में यह केवल काम करता है कि वर्ग के भीतर परिभाषित किया गया उपयोग कर सकते हैं। Michele Simionato's article में बताया गया है कि निम्न उदाहरण काम नहीं करता:मैन्युअल रूप से __class__ सेल भरकर सुपर() काम कैसे करें?

def __init__(self): 
    print('calling __init__') 
    super().__init__() 

class C(object): 
    __init__ = __init__ 

if __name__ == '__main__': 
    c = C() 

यह विफल रहता है क्योंकि super() एक __class__cell, जो इस मामले में निर्धारित नहीं है के लिए लग रहा है।

यह इस सेल मैन्युअल के बाद समारोह परिभाषित किया गया है स्थापित करने के लिए संभव है, या कि असंभव है?

दुर्भाग्य से मुझे समझ नहीं आता कैसे कोशिकाओं को इस संदर्भ में काम करते हैं (उस के लिए बहुत प्रलेखन नहीं मिला)। मैं

__init__.__class_cell_thingy__ = C 

बेशक मैं केवल एक स्थिति है जहाँ क्लास असाइनमेंट स्पष्ट/अद्वितीय (में मेरे मामले में automatized है एक वर्ग के लिए विधियां जोड़ने की पूरी प्रक्रिया है में इस का प्रयोग करेंगे की तरह कुछ के लिए आशा करती हूं कि, इसलिए ऐसी लाइन जोड़ने के लिए यह आसान होगा)।

+3

"क्या यह सेल मैन्युअल रूप से सेट करना संभव है"? जब आपने यह कोशिश की, तो क्या हुआ? –

+0

लेकिन फ़ंक्शन को परिभाषित करने के बाद मैं इस सेल तक कैसे पहुंच सकता हूं? '__init __.__ class__' निश्चित रूप से कुछ और है। मैंने अब इसे स्पष्ट करने के लिए प्रश्न संपादित किया है। – nikow

+0

आप कक्षा के बाहर समारोह को परिभाषित क्यों करना चाहते हैं? यदि आप किसी फ़ंक्शन का पुन: उपयोग करना चाहते हैं, या किसी फ़ंक्शन को कई बार विधि के रूप में उपयोग करना चाहते हैं, तो आप यह चाहते हैं। आप उन चीजों को नहीं कर सकते हैं और 'सुपर (...)' सही ढंग से उपयोग कर सकते हैं। और यदि आप रनटाइम पर कक्षा को संशोधित कर रहे हैं, तो आप इसे आसानी से हार्डकोड कर सकते हैं। –

उत्तर

15

गंभीरता से: तुम सच में ऐसा करने के लिए नहीं करना चाहती।

लेकिन, यह समझने के लिए पाइथन के उन्नत उपयोगकर्ताओं के लिए उपयोगी है, इसलिए मैं इसे समझाऊंगा।

सेल और फ्रीवर्स बंद होने पर मान दिए गए मान हैं। उदाहरण के लिए,

def f(): 
    a = 1 
    def func(): 
     print(a) 
    return func 

ffunc के आधार पर एक बंद देता है, a के लिए एक संदर्भ के संचय। वह संदर्भ एक सेल में संग्रहीत होता है (वास्तव में एक फ्रीवर में, लेकिन जो कार्यान्वयन तक है)। आप इस की जांच कर सकते हैं:।।।

myfunc = f() 
# ('a',) 
print(myfunc.__code__.co_freevars) 
# (<cell at 0xb7abce84: int object at 0x82b1de0>,) 
print(myfunc.__closure__) 

("सेल" और "freevars" बहुत समान हैं Freevars नाम, जहां कोशिकाओं अनुक्रमित है है वे दोनों func.__closure__ में जमा हो जाती है, कोशिकाओं पहले आने के साथ कर रहे हैं हम केवल बारे में परवाह यहाँ freevars, के बाद से है कि क्या __class__ है।)

एक बार जब आप इसे समझ लेंगे, तो आप देख सकते हैं कि सुपर() वास्तव में कैसे काम करता है। किसी भी समारोह को बंद करने में शामिल है कि super के लिए एक कॉल वास्तव में है, __class__ नाम के एक freevar साथ (जो भी करता है, तो आप __class__ अपने आप का उल्लेख जोड़ा जाता है):

class foo: 
    def bar(self): 
     print(__class__) 

(चेतावनी:। यह वह जगह है जहां चीजें बुराई मिल)

ये कक्ष func.__closure__ में दिखाई दे रहे हैं, लेकिन यह केवल पढ़ने के लिए है; आप इसे बदल नहीं सकते इसे बदलने का एकमात्र तरीका एक नया फ़ंक्शन बनाना है, जो types.FunctionType कन्स्ट्रक्टर के साथ किया जाता है। हालांकि, आपके __init__ फ़ंक्शन में __class__ फ्रीवर नहीं है - इसलिए हमें एक को जोड़ने की आवश्यकता है। इसका मतलब है कि हमें एक नया कोड ऑब्जेक्ट भी बनाना है।

नीचे कोड यह करता है। मैंने प्रदर्शन उद्देश्यों के लिए बेस क्लास B जोड़ा। यह कोड कुछ धारणाएं बनाता है, उदाहरण के लिए। कि __init__ में पहले से ही एक फ्री वैरिएबल नहीं है जिसका नाम __class__ है।

यहां एक और हैक है: सेल प्रकार के लिए एक कन्स्ट्रक्टर प्रतीत नहीं होता है। इसके आसपास काम करने के लिए, एक डमी फ़ंक्शन C.dummy बनाया गया है जिसमें सेल वैरिएबल की आवश्यकता है।

import types 

class B(object): 
    def __init__(self): 
     print("base") 

class C(B): 
    def dummy(self): __class__ 

def __init__(self): 
    print('calling __init__') 
    super().__init__() 

def MakeCodeObjectWithClass(c): 
    """ 
    Return a copy of the code object c, with __class__ added to the end 
    of co_freevars. 
    """ 
    return types.CodeType(c.co_argcount, c.co_kwonlyargcount, c.co_nlocals, 
      c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names, 
      c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno, 
      c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars) 

new_code = MakeCodeObjectWithClass(__init__.__code__) 
old_closure = __init__.__closure__ or() 
C.__init__ = types.FunctionType(new_code, globals(), __init__.__name__, 
    __init__.__defaults__, old_closure + (C.dummy.__closure__[0],)) 

if __name__ == '__main__': 
    c = C() 
+0

धन्यवाद, उत्कृष्ट उत्तर। यह जानना अच्छा लगता है कि ये चीजें कैसे काम करती हैं। मुझे लगता है कि मैं आपकी सलाह लेगा और अभी के लिए स्पष्ट 'सुपर' कॉल पर चिपक जाऊंगा। जब तक पायथन 3 मुख्य नहीं हो जाता है, वैसे भी यह कुछ हद तक अकादमिक है। – nikow

+0

पीएस लगता है कि इस सवाल का जवाब एक भी तर्क '__class__' के साथ एक अन्य समारोह के अंदर एक समारोह को परिभाषित करने और तो आंतरिक समारोह वापसी (या बस के बंद होने) के लिए 10k प्रतिनिधि से आप धक्का दिया धक्का दिया :-) – nikow

+1

एक और चाल है। फिर आपको बंद करने के लिए वहां डमी विधि की आवश्यकता नहीं होगी, या आप वास्तविक विधि को इस तरह के आंतरिक कार्य के रूप में भी परिभाषित कर सकते हैं। एक और बात यह है कि आप एक वर्ग कंटेनर कार्यों स्टोर करने के लिए (शायद जादू छुपा तरीकों के साथ एक सहायक metaclass के साथ) का उपयोग कर सकते है, तो आप उस डरावना कॉल के साथ वस्तुओं कस्टम कोड बनाने के लिए नहीं होगा। –

1

हो सकता है, लेकिन क्या तुम करोगी द्वारा? दोनों मामलों में आपको किसी भी तरह से स्पष्ट होना चाहिए कि यह कौन सी कक्षा है, क्योंकि अंतर्निहित तरीका काम नहीं करता है। शायद आप सेल को किसी भी तरह से सेट कर सकते हैं, लेकिन ऐसा करने का कोई कारण नहीं है। बस पैरामीटर में स्पष्ट रूप से पास करें।

def __init__(self): 
    print('calling __init__') 
    super(self.__class__, self).__init__() 

class C(object): 
    __init__ = __init__ 

if __name__ == '__main__': 
    c = C() 

(यह बेहतर है तुम इतनी तरह सीधे वास्तविक कक्षा में पारित कर सकते हैं,:

def __init__(self): 
    print('calling __init__') 
    super(C, self).__init__() 

class C(object): 
    __init__ = __init__ 

if __name__ == '__main__': 
    c = C() 

लेकिन अगर आप, आप C पर __init__ सीधे डाल सकता है सकते हैं, तो आप मान सकते हैं ' टी।

+2

उत्तर देने के लिए धन्यवाद, लेकिन आप यहां जो सुझाव देते हैं वह बहुत गलत है। आप 'सुपर' के साथ 'self.__ class__' का उपयोग नहीं कर सकते, जैसा कि हाल ही में यहां अपवादित किया गया था: http://stackoverflow.com/questions/4883822/shortcut-for-supertypeself-self – nikow

+0

@nikow: ठीक है, अगर आप जा रहे हैं इसे उपclass करने के लिए आप समस्याओं में हो सकते हैं, हाँ, अगर वह सबक्लास भी सुपर() का उपयोग करता है। तो आप कई अलग-अलग वर्गों के लिए समान '__init__' का उपयोग करना चाहते हैं (क्योंकि अन्यथा आप इसे कक्षा के बाहर परिभाषित नहीं करते थे) और आप नहीं जानते कि कौन से क्लासेस हैं, और आप नहीं जानते कि ये कक्षाएं या कैसे हैं subclassed? उस मामले में मैं कहूंगा कि आप समस्या को हल कर रहे हैं। क्या मैं एक कक्षा सजावट, या एडाप्टर के साथ एक घटक वास्तुकला का सुझाव दे सकता हूं? –

+0

@Lennart: जोड़ा गया फ़ंक्शन एओपी से विचारों के बाद कहीं और परिभाषित किया गया है, और इसने हमारी परियोजना में बहुत अच्छा काम किया है। वास्तविक कोड में इन "पहलुओं" को रनटाइम पर सक्रिय/निष्क्रिय किया जाता है, इसलिए सजावटी कक्षाओं का उपयोग वास्तव में एक विकल्प नहीं है। मैं बस उम्मीद कर रहा था कि कोई भी सरलीकृत 'सुपर()' कॉल सक्षम कर सकता है, इसके बाद उन्हें किसी कारण से पायथन 3 में जोड़ा गया था। – nikow

2

आप समारोह के शब्दकोश का उपयोग कर सकते हैं।

def f(self): 
    super(f.owner_cls, self).f() 
    print("B") 

def add_to_class(cls, member, name=None): 
    if hasattr(member, 'owner_cls'): 
     raise ValueError("%r already added to class %r" % (member, member.owner_cls)) 
    member.owner_cls = cls 
    if name is None: 
     name = member.__name__ 
    setattr(cls, name, member) 

class A: 
    def f(self): 
     print("A") 

class B(A): 
    pass 

add_to_class(B, f) 

B().f() 

यदि आप फ़ंक्शन के अंदर सदस्य के नाम का नाम हार्डकोड नहीं करना चाहते हैं तो आप एक और विशेषता member_name भी जोड़ सकते हैं।

+0

यह काम करेगा, लेकिन मुझे यकीन नहीं है कि मैं इसे कक्षा के साथ स्पष्ट समाधान के लिए पसंद करूंगा। आखिरकार मुझे पता है कि विधि किस वर्ग से संबंधित होगी, मैं बस अधिक सुविधाजनक पायथन 3 'सुपर() 'से लाभ प्राप्त करना चाहता हूं। – nikow

+2

यदि आप पहले ऐसा कुछ कर रहे हैं, तो संभवतः आप फ़ंक्शन को एक से अधिक कक्षाओं में असाइन करने में सक्षम होना चाहते हैं। –

+0

@Glenn: हाँ, आप सही हैं। हालांकि यह मेरा प्राथमिक उपयोग मामला नहीं है, मुझे याद आया कि ऐसे मामले हैं जहां यह एक मुद्दा होगा। – nikow

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