2012-12-12 16 views
10

अद्यतन विधियां जोड़ने - 2012/12/13गतिशील रूप से या metaclass बिना

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

जिस चीज में मुझे रूचि है, वह सीख रहा है कि अलग-अलग दृष्टिकोणों का उपयोग करके कक्षाओं में विधियों को जोड़ने का मौलिक अंतर क्या है और वास्तव में मुझे मेटाक्लास का उपयोग करने की आवश्यकता क्यों है। उदाहरण के लिए A Primer on Python Metaclass Programming कहा गया है कि:

शायद metaclasses का सबसे आम उपयोग [...]: बताया हटाने, नाम बदलने, या उत्पादन वर्ग में परिभाषित किया उन लोगों के लिए तरीकों प्रतिस्थापन।

और चूंकि ऐसा करने के और तरीके हैं, इसलिए मैं परेशान हूं और स्पष्टीकरण की तलाश में हूं।

धन्यवाद!

मूल - 2012/12/12

मैं गतिशील (उस वर्ग पर आधारित है और नई जनरेट वर्ग) एक वर्ग के लिए विधियों को जोड़ने की जरूरत है। मैं दो दृष्टिकोणों के साथ आया, जिसमें मेटाक्लास शामिल था, दूसरा एक बिना कर रहा था।

class Meta(type): 
     def __init__(cls, *args, **kwargs): 
       setattr(cls, "foo", lambda self: "[email protected]%s(class %s)" % (self, 
         cls.__name__)) 

class Y(object): 
     __metaclass__ = Meta 

y = Y() 
y.foo() # Gives '[email protected]<__main__.Y object at 0x10e4afd10>(class Y)' 
: metaclass के साथ)

दृष्टिकोण # 1, मैं क्या दो दृष्टिकोण मुझे तथ्य बाद "काला जादू" metaclass शामिल नहीं करता है के अलावा अन्य दे में कोई अंतर नहीं देख सकते हैं

दृष्टिकोण # 2 metaclass बिना:

class Z(object): 
     def __init__(self): 
       setattr(self.__class__, "foo", 
         lambda self: "[email protected]%s(class %s)" % 
         (self, self.__class__.__name__)) 

z = Z() 
z.foo() # Gives '[email protected]<__main__.Z object at 0x10c865dd0>(class Z)' 

जहां तक ​​मेरा कर सकते हैं बताओ, दोनों दृष्टिकोण मुझे एक ही परिणाम और "व्यक्तित्व" देते हैं। यहां तक ​​कि जब मैं type("NewClassY", (Y,), {}) या type("NewClassZ", (Z,), {}) का उपयोग करके एक नई कक्षाएं बनाने का प्रयास करता हूं, तो मुझे वही अपेक्षित परिणाम मिलते हैं जो दोनों दृष्टिकोणों के बीच भिन्न नहीं होते हैं।

तो, मैं सोच रहा हूँ अगर वहाँ वास्तव में, दृष्टिकोण में किसी भी "अंतर्निहित" अंतर नहीं है या यह है कि अगर हो, तो जो कुछ भी "काटने" मुझे बाद में अगर मैं इस्तेमाल किया जाएगा या तो # 1 या # 2 या बस एक वाक्य रचनात्मक चीनी?

पीएस: हां, मैंने पाइथन और पायथनिक डेटा मॉडल में मेटाक्लास के बारे में बात करते हुए अन्य धागे पढ़े हैं।

+0

क्या आप 'वाई.फू' और 'जेड' को कॉल कर सकते हैं।दोनों दृष्टिकोणों का उपयोग कर foo'? – Blender

+2

अंतर यह है कि मेटाक्लास क्लास परिभाषित होने के समय विधियों का निर्माण करेगा और जब आप कक्षा को तुरंत चालू करेंगे तो '__init__'way विधियों का निर्माण करेगा। कई विरासत (मिश्रण) भी हैं जिन्हें आप विधियों को जोड़ने के लिए उपयोग कर सकते हैं। –

+0

@ ब्लेंडर नंबर, आप उन्हें इस तरह से कॉल नहीं कर सकते क्योंकि वे "कक्षा विधियां" नहीं हैं। – TomasHeran

उत्तर

2

यदि समस्या गतिशील रूप से विधियों को जोड़ रही है, तो पाइथन इसे काफी सरल तरीके से संभालता है। यह हो जाता है इस प्रकार है:

#!/usr/bin/python 
class Alpha(): 

    def __init__(self): 
     self.a = 10 
     self.b = 100 

alpha = Alpha() 
print alpha.a 
print alpha.b 

def foo(self): 
    print self.a * self.b * self.c 

Alpha.c = 1000 
Alpha.d = foo 

beta = Alpha() 
beta.d() 

आउटपुट:

$ python script.py 
10 
100 
1000000 

सादर!

पीएस: मैं यहाँ काला जादू की कोई समानता देखते हैं (:

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

मार्टिन्यू की टिप्पणी को देखते हुए, मैं data attributes जोड़ रहा, method function attributes नहीं मैं वास्तव में Alpha.d = foo और Alpha.d = lambda self: foo(self) कर बीच कोई अंतर नहीं देख सकते हैं, सिवाय इसके कि। मैं समारोह foo जोड़ा करने के लिए एक आवरण के रूप में एक लैम्ब्डा समारोह का उपयोग कर होता जा

विधि के अलावा एक ही है, और अजगर ही दोनों परिवर्धन नाम एक ही:।

#!/usr/bin/python 
class Alpha(): 

    def __init__(self): 
     self.a = 10 
     self.b = 100 

alpha = Alpha() 
print alpha.a 
print alpha.b 

def foo(self): 
    print self.a * self.b * self.c 

Alpha.c = 1000 

Alpha.d = lambda self: foo(self) 
Alpha.e = foo 

print Alpha.d 
print Alpha.e 

a = Alpha() 
a.d() 
a.e() 

आउटपुट:

10 
100 
<unbound method Alpha.<lambda>> 
<unbound method Alpha.foo> 
1000000 
1000000 

दिखाया गया है, अजगर ही तरीके के रूप में दोनों परिणामी जोड़ नाम - फर्क सिर्फ इतना है कि एक foo कार्य करने के लिए एक संदर्भ है है, और अन्य एक लैम्ब्डा समारोह के लिए एक संदर्भ है कि इसकी परिभाषा निकाय में foo फ़ंक्शन का उपयोग करता है।

अगर मैंने कुछ गलत कहा, तो कृपया मुझे सही करें।

सम्मान!

+0

आपका उत्तर कक्षा में विधियों को नहीं जोड़ता है, यह केवल डेटा विशेषताओं को जोड़ता है, विधि विधि गुण नहीं। – martineau

+0

क्या आप एक 'विधि फ़ंक्शन विशेषता' का उदाहरण देना चाहते हैं? – Rubens

+1

'Alpha.answer = lambda self: 42' – martineau

9

मेटाक्लास का उपयोग करने का स्पष्ट कारण यह है कि वे वास्तव में कक्षा के बारे में मेटाडेटा प्रदान करते हैं जैसे ही कक्षा ज्ञात होती है, वस्तुओं की उपस्थिति से संबंधित नहीं है या नहीं। ट्रिविअल, है ना?

In [287]: hasattr(Y,'foo') 
Out[287]: True 

In [288]: hasattr(Z,'foo') 
Out[288]: False 

In [289]: Y.__dict__ 
Out[289]: 
<dictproxy {..., 'foo': <function __main__.<lambda>>}> 

In [290]: Z.__dict__ 
Out[290]: 
<dictproxy {...}> 

In [291]: z= Z() 

In [292]: hasattr(Z,'foo') 
Out[292]: True 

In [293]: Z.__dict__ 
Out[293]: 
<dictproxy {..., 'foo': <function __main__.<lambda>>}> 

In [294]: y = Y() 

In [295]: hasattr(Y,'foo') 
Out[295]: True 

In [296]: Y.__dict__ 
Out[296]: 
<dictproxy {..., 'foo': <function __main__.<lambda>>}> 

आप दूसरे संस्करण देख सकते हैं वास्तव में काफी वर्ग जेड बदल के बाद यह घोषित किया गया है, तो आप अक्सर कुछ: ठीक है, हमें देखने के लिए इसका क्या मतलब है कुछ आदेश मैं अपने मूल Z और Y वर्गों पर निष्पादित दिखाते हैं से बचने के लिए। यह निश्चित रूप से पाइथन के लिए 'प्रकार' (कक्षा वस्तुओं) पर संचालन करने के लिए एक असामान्य ऑपरेशन नहीं है और आप संभवतः इस मामले के लिए जितना संभव हो उतना संगत होना चाहते हैं (जहां विधि रनटाइम पर वास्तव में गतिशील नहीं है, केवल घोषणा पर पहर)।

एक ऐसा एप्लिकेशन जो दिमाग में कूदता है वह डॉक्यूमेनेशन है। यदि आप मेटाक्लास का उपयोग करके foo पर डॉकस्ट्रिंग जोड़ना चाहते हैं, तो दस्तावेज़ __init__ विधि के माध्यम से इसे चुन सकता है, यह बहुत ही असंभव है।

इससे हार्ड-टू-स्पॉट बग भी हो सकते हैं। कोड के एक टुकड़े पर विचार करें जो कक्षा के मेटाफॉर्मेशन का उपयोग करता है। यह हो सकता है कि 99.99% मामलों में यह Z के उदाहरण के बाद निष्पादित किया गया है, लेकिन 001% क्रैश नहीं होने पर अजीब व्यवहार का कारण बन सकता है।

यह पदानुक्रम श्रृंखलाओं में भी मुश्किल हो सकता है, जहां आपको माता-पिता के निर्माता को कॉल करने के लिए अच्छी देखभाल करना होगा।

class Zd(Z): 
    def __init__(self): 
     self.foo() 
     Z.__init__(self) 
a = Zd() 
... 
AttributeError: 'Zd' object has no attribute 'foo' 

हालांकि यह ठीक काम करता है:

class Yd(Y): 
    def __init__(self): 
     self.foo() 
     Y.__init__(self) 
a = Yd() 

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

+0

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

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