क्लास बॉडी के भीतर कॉलिंग स्टैटिकमेथोड?


159

जब मैं कक्षा के शरीर के भीतर से एक स्थैतिक विधि का उपयोग करने का प्रयास करता हूं, और staticmethodइस तरह से डेकोरेटर के रूप में अंतर्निहित फ़ंक्शन का उपयोग करके स्थैतिक विधि को परिभाषित करता हूं :

class Klass(object):

    @staticmethod  # use as decorator
    def _stat_func():
        return 42

    _ANS = _stat_func()  # call the staticmethod

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

मुझे निम्नलिखित त्रुटि मिलती है:

Traceback (most recent call last):<br>
  File "call_staticmethod.py", line 1, in <module>
    class Klass(object): 
  File "call_staticmethod.py", line 7, in Klass
    _ANS = _stat_func() 
  TypeError: 'staticmethod' object is not callable

मैं समझता हूं कि ऐसा क्यों हो रहा है (डिस्क्रिप्टर बाइंडिंग) , और इसके चारों ओर काम कर सकते हैं मैन्युअल रूप से _stat_func()अपने अंतिम उपयोग के बाद एक स्थैतिक रूप में परिवर्तित करना, जैसे:

class Klass(object):

    def _stat_func():
        return 42

    _ANS = _stat_func()  # use the non-staticmethod version

    _stat_func = staticmethod(_stat_func)  # convert function to a static method

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

तो मेरा सवाल है:

क्या बेहतर है, क्लीनर या अधिक "पायथोनिक" के रूप में, इसे पूरा करने के तरीके?


4
यदि आप Pythonicity के बारे में पूछ रहे हैं, तो मानक सलाह बिल्कुल उपयोग करने की नहीं है staticmethod। वे आमतौर पर मॉड्यूल-स्तरीय कार्यों के रूप में अधिक उपयोगी होते हैं, जिस स्थिति में आपकी समस्या कोई समस्या नहीं है। classmethodदूसरी ओर ...
बेंजामिन हॉजसन

1
@poorsod: हां, मैं उस विकल्प से अवगत हूं। हालाँकि वास्तविक कोड में जहाँ मुझे इस समस्या का सामना करना पड़ा, फ़ंक्शन को मॉड्यूल-स्तर पर रखने के बजाय एक स्थिर विधि बनाना, मेरे प्रश्न में प्रयुक्त सरल उदाहरण की तुलना में अधिक समझ में आता है।
मार्टिउ

जवाबों:


180

staticmethodऑब्जेक्ट्स में स्पष्ट __func__रूप से मूल कच्चे फ़ंक्शन को संग्रहीत करने की विशेषता होती है (समझ में आता है कि उन्हें)। तो यह काम करेगा:

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    _ANS = stat_func.__func__()  # call the staticmethod

    def method(self):
        ret = Klass.stat_func()
        return ret

एक तरफ के रूप में, हालांकि मुझे संदेह था कि एक स्थैतिक वस्तु में मूल फ़ंक्शन को संचय करने के लिए किसी प्रकार की विशेषता थी, मुझे बारीकियों का कोई पता नहीं था। किसी को मछली देने के बजाय उसे सिखाने की भावना में, यह वही है जिसकी मैंने जांच की और पाया कि बाहर (मेरे पायथन सत्र से एक C & P):

>>> class Foo(object):
...     @staticmethod
...     def foo():
...         return 3
...     global z
...     z = foo

>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>

एक इंटरैक्टिव सत्र में खुदाई करने के समान प्रकार ( dirबहुत मददगार है) अक्सर इस तरह के सवाल को बहुत जल्दी हल कर सकते हैं।


अच्छा अद्यतन ... मैं बस यह पूछने के बारे में था कि आप इसे कैसे जानते हैं क्योंकि मैं इसे प्रलेखन में नहीं देखता हूं - जो मुझे इसके उपयोग के बारे में थोड़ा परेशान करता है क्योंकि यह "कार्यान्वयन विवरण" हो सकता है।
मार्टिन

आगे पढ़ने के बाद मैं देखता हूं कि इसके __func__लिए सिर्फ एक और नाम है im_funcऔर Python 3 के लिए Py 2.6 को आगे-संगतता के लिए जोड़ा गया।
1

2
मैं देख रहा हूं, इसलिए तकनीकी रूप से यह इस संदर्भ में अनिर्धारित है।
0

1
@ अक्षयहजारी __func__एक स्थिर विधि की विशेषता आपको मूल फ़ंक्शन का संदर्भ देती है, जैसे कि आपने staticmethodडेकोरेटर का कभी उपयोग नहीं किया । इसलिए यदि आपके फ़ंक्शन को तर्कों की आवश्यकता है, तो आपको कॉल करते समय उन्हें पास करना होगा __func__। त्रुटि संदेश आपको काफी लगता है जैसे आपने इसे कोई तर्क नहीं दिया है। यदि stat_funcइस पोस्ट के उदाहरण में दो तर्क दिए गए हैं, तो आप उपयोग करेंगे_ANS = stat_func.__func__(arg1, arg2)
बेन

1
@ अक्षयहजारी मुझे यकीन नहीं है कि मैं समझ गया हूँ। वे वैरिएबल हो सकते हैं, उन्हें बस इन-स्कोप वैरिएबल होना चाहिए: या तो पहले उसी स्कोप में परिभाषित किया जाए जहां क्लास डिफाइन किया जाता है (अक्सर ग्लोबल), या क्लास स्कोप के भीतर पहले से डिफाइन किया जाता है ( stat_funcखुद भी ऐसा वैरिएबल है)। क्या आपका मतलब है कि आप जिस कक्षा को परिभाषित कर रहे हैं उसके उदाहरण विशेषताओं का उपयोग नहीं कर सकते हैं? यह सच है, लेकिन आश्चर्यजनक है; हमारे पास वर्ग का उदाहरण नहीं है , क्योंकि हम अभी भी कक्षा को परिभाषित कर रहे हैं! लेकिन वैसे भी, मेरा मतलब सिर्फ उन तर्कों के रूप में था जो भी तर्क आप पारित करना चाहते थे; आप वहां शाब्दिक उपयोग कर सकते हैं।
बेन

24

यह वह तरीका है जो मुझे पसंद है:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS

मैं DRY सिद्धांत केKlass.stat_func कारण इस समाधान को पसंद करता हूं । मुझे यह याद दिलाने की वजह है कि पायथन 3 में नया क्या है :)super()

लेकिन मैं दूसरों से सहमत हूं, आमतौर पर सबसे अच्छा विकल्प एक मॉड्यूल स्तर फ़ंक्शन को परिभाषित करना है।

उदाहरण के लिए, @staticmethodफ़ंक्शन के साथ , पुनरावृत्ति बहुत अच्छी नहीं लग सकती है (आपको Klass.stat_funcअंदर कॉल करके DRY सिद्धांत को तोड़ने की आवश्यकता होगी Klass.stat_func)। ऐसा इसलिए है क्योंकि आपके पास selfस्थैतिक विधि का संदर्भ नहीं है । मॉड्यूल स्तर फ़ंक्शन के साथ, सब कुछ ठीक लगेगा।


जबकि मैं मानता हूं कि self.__class__.stat_func()नियमित तरीकों का उपयोग करने से फायदे (DRY और वह सब) का अधिक उपयोग होता हैKlass.stat_func() , जो वास्तव में मेरे सवाल का विषय नहीं था - वास्तव में मैंने पूर्व के उपयोग से परहेज किया क्योंकि असंगति के मुद्दे को बादल नहीं।
मार्टिन्यू

1
यह वास्तव में प्रति se DRY का मुद्दा नहीं है। self.__class__बेहतर है क्योंकि यदि कोई उपवर्ग ओवरराइड करता है stat_func, तो Subclass.methodउपवर्ग को कॉल करेगा stat_func। लेकिन पूरी तरह से ईमानदार होने के लिए, उस स्थिति में केवल एक स्थिर के बजाय एक वास्तविक विधि का उपयोग करना बेहतर होता है।
asmeurer

@asmeurer: मैं एक वास्तविक विधि का उपयोग नहीं कर सकता क्योंकि वर्ग का कोई उदाहरण अभी तक नहीं बनाया गया है - वे तब से नहीं हो सकते हैं जब तक कि कक्षा खुद भी पूरी तरह से परिभाषित नहीं हुई है।
मार्टिन

मैं अपनी कक्षा के लिए स्थैतिक विधि को कॉल करने का एक तरीका चाहता था (जो विरासत में मिला है; इसलिए माता-पिता के पास वास्तव में कार्य है) और यह सबसे अच्छा लगता है (और अभी भी काम करेगा अगर मैं इसे बाद में ओवरराइड करता हूं)।
श्वेत ०४

11

वर्ग परिभाषा के बाद वर्ग विशेषता को इंजेक्ट करने के बारे में क्या?

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    def method(self):
        ret = Klass.stat_func()
        return ret

Klass._ANS = Klass.stat_func()  # inject the class attribute with static method value

1
यह एक काम के आसपास मेरे पहले प्रयास के समान है, लेकिन मैं कुछ ऐसा पसंद करूंगा जो कक्षा के अंदर था ... आंशिक रूप से क्योंकि इसमें शामिल विशेषता में एक प्रमुख अंडरस्कोर है और निजी है।
मार्टीन्यू

11

यह स्टैटेमेथोड एक डिस्क्रिप्टर होने के कारण है और डिस्क्रिप्टर प्रोटोकॉल का प्रयोग करने के लिए एक क्लास-स्तरीय विशेषता लाने की आवश्यकता है और यह सच होने योग्य है।

स्रोत कोड से:

इसे या तो कक्षा पर (उदाहरण के लिए C.f()) या उदाहरण पर (जैसे ) कहा जा सकता है C().f(); इसके वर्ग को छोड़कर उदाहरण को नजरअंदाज किया जाता है।

लेकिन सीधे वर्ग के अंदर से नहीं जबकि इसे परिभाषित किया जा रहा है।

लेकिन जैसा कि एक टिप्पणीकार ने उल्लेख किया है, यह वास्तव में "पायथोनिक" डिजाइन बिल्कुल नहीं है। इसके बजाय बस एक मॉड्यूल स्तर फ़ंक्शन का उपयोग करें।


जैसा कि मैंने अपने प्रश्न में कहा था, मैं समझता हूं कि मूल कोड क्यों काम नहीं किया। क्या आप समझा सकते हैं (या ऐसा कुछ करने के लिए एक लिंक प्रदान करते हैं) क्यों इसे unPythonic माना जाता है?
मार्टिउ

स्टैटिकमेथोड को सही ढंग से काम करने के लिए स्वयं क्लास ऑब्जेक्ट की आवश्यकता होती है। लेकिन अगर आप इसे कक्षा के भीतर कक्षा स्तर से बुलाते हैं तो कक्षा वास्तव में उस बिंदु पर पूरी तरह से परिभाषित नहीं होती है। इसलिए आप इसे अभी तक संदर्भित नहीं कर सकते। इसे परिभाषित करने के बाद कक्षा के बाहर से स्टेथमिथोड को कॉल करना ठीक है। लेकिन " पायथोनिक " वास्तव में अच्छी तरह से परिभाषित नहीं है, सौंदर्यशास्त्र की तरह। मैं आपको बता सकता हूं कि मेरी अपनी पहली छाप उस कोड के अनुकूल नहीं थी।
कीथ

किस संस्करण का प्रभाव (या दोनों)? और क्यों, विशेष रूप से?
मार्टिन

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

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

8

इस समाधान के बारे में क्या? यह @staticmethodडेकोरेटर कार्यान्वयन के ज्ञान पर निर्भर नहीं करता है । आंतरिक वर्ग StaticMethod स्थैतिक आरंभीकरण कार्यों के एक कंटेनर के रूप में खेलता है।

class Klass(object):

    class StaticMethod:
        @staticmethod  # use as decorator
        def _stat_func():
            return 42

    _ANS = StaticMethod._stat_func()  # call the staticmethod

    def method(self):
        ret = self.StaticMethod._stat_func() + Klass._ANS
        return ret

2
रचनात्मकता के लिए +1, लेकिन मैं अब __func__इसका उपयोग करने के बारे में चिंतित नहीं हूं क्योंकि यह अब आधिकारिक रूप से प्रलेखित है ( पायथन 2.7 में व्हाट्स न्यू का अनुभाग अन्य भाषा परिवर्तन देखें और इसके अंक 5982 का संदर्भ )। आपका समाधान और भी अधिक पोर्टेबल है, क्योंकि यह संभवतः 2.6 से पहले पायथन संस्करणों में भी काम करेगा (जब __func__इसे पहली बार एक पर्याय के रूप में पेश किया गया था im_func)।
मार्टिन्यू

यह एकमात्र समाधान है जो पायथन 2.6 के साथ काम करता है।
बेंसलमे जुले

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