2013-02-22 33 views
11

पाइथन में स्थिर विधियों का उपयोग कब और कैसे किया जाता है? जब हम संभव हो तो ऑब्जेक्ट का उदाहरण बनाने के लिए कारखाने विधि के रूप में एक क्लास विधि का उपयोग कर पहले ही स्थापित कर चुके हैं। दूसरे शब्दों में, क्लास विधियों को वैकल्पिक कन्स्ट्रक्टर के रूप में उपयोग करना सबसे अच्छा अभ्यास नहीं है (Factory method for python object - best practice देखें)।पायथन में स्थिर तरीकों का उपयोग करना - सर्वोत्तम अभ्यास

आइए कहें कि मेरे पास डेटाबेस में कुछ इकाई डेटा का प्रतिनिधित्व करने के लिए उपयोग की जाने वाली कक्षा है। कल्पना करें कि डेटा dict ऑब्जेक्ट है जिसमें फील्ड नाम और फ़ील्ड मान हैं और फ़ील्ड में से एक आईडी आईडी है जो डेटा को अद्वितीय बनाती है।

class Entity(object): 
    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 

यहाँ मेरी __init__ विधि इकाई डेटा dict वस्तु लेता है। आइए कहें कि मेरे पास केवल एक आईडी नंबर है और मैं Entity उदाहरण बनाना चाहता हूं। सबसे पहले मुझे शेष डेटा ढूंढना होगा, फिर मेरे Entity ऑब्जेक्ट का एक उदाहरण बनाएं। मेरे पिछले प्रश्न से, हमने पाया कि फैक्ट्री विधि के रूप में एक क्लास विधि का उपयोग संभवतः संभवतः टाला जाना चाहिए।

class Entity(object): 

    @classmethod 
    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return cls(data, db_connection) 

    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 


# Create entity 
entity = Entity.from_id(id_number, db_connection) 

ऊपर क्या नहीं करना है या अगर कोई एक विकल्प है कम से कम क्या नहीं करना है का एक उदाहरण है। अब मैं सोच रहा हूं कि मेरी कक्षा विधि को संपादित करना है ताकि यह एक उपयोगिता विधि से अधिक हो और फैक्ट्री विधि से कम एक वैध समाधान हो। दूसरे शब्दों में, निम्न उदाहरण स्थिर तरीकों का उपयोग करने के लिए सर्वोत्तम अभ्यास का अनुपालन करता है।

class Entity(object): 

    @staticmethod 
    def data_from_id(id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return data 


# Create entity 
data = Entity.data_from_id(id_number, db_connection) 
entity = Entity(data) 

या आईडी आईडी से इकाई डेटा खोजने के लिए एक स्टैंडअलोन फ़ंक्शन का उपयोग करने के लिए और अधिक समझदारी होती है।

def find_data_from_id(id_number, db_connection): 
    filters = [['id', 'is', id_number]] 
    data = db_connection.find(filters) 
    return data 


# Create entity. 
data = find_data_from_id(id_number, db_connection) 
entity = Entity(data, db_connection) 

नोट: मैं अपने __init__ प्रक्रिया में परिवर्तन करना नहीं चाहता। पहले लोगों ने इस __init__(self, data=None, id_number=None) की तरह कुछ देखने के लिए मेरी __init__ विधि बनाने का सुझाव दिया है लेकिन इकाई डेटा खोजने के 101 अलग-अलग तरीके हो सकते हैं, इसलिए मैं उस तर्क को कुछ हद तक अलग रखना पसंद करूंगा। सही बात?

+0

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

उत्तर

6

आपका पहला उदाहरण मुझे सबसे ज्यादा समझ में आता है: Entity.from_id बहुत संक्षिप्त और स्पष्ट है।

यह अगले दो उदाहरणों में data के उपयोग से बचाता है, जो वर्णन नहीं करता है कि क्या किया जा रहा है; data का उपयोग Entity बनाने के लिए किया जाता है। यदि आप के बारे में विशिष्ट होना चाहते हैं तो Entity बनाने के लिए उपयोग किया जा रहा है, तो आप Entity.with_data_for_id या समकक्ष फ़ंक्शन entity_with_data_for_id जैसे कुछ तरीके का नाम दे सकते हैं।

find जैसे क्रिया का उपयोग करना बहुत भ्रमित हो सकता है, क्योंकि यह रिटर्न वैल्यू का कोई संकेत नहीं देता है - डेटा को मिलने पर क्या कार्य करना चाहिए? (हाँ, मुझे पता है str एक find विधि है, यह बेहतर index_of नाम दिया नहीं होता लेकिन फिर भी index है ...?) यह मेरे क्लासिक की याद दिलाता है:

find x

मैं हमेशा सोचने की कोशिश करें किस नाम से किसी को संकेत मिलेगा (ए) सिस्टम का कोई ज्ञान नहीं, और (बी) सिस्टम के अन्य हिस्सों के ज्ञान - यह नहीं कहना कि मैं हमेशा सफल हूं!

+1

"ढूंढें" के बारे में अच्छा बिंदु। यह पुराने स्कॉट एडम्स टेक्स्ट एडवेंचर्स की तरह है, जहां मुख्य पहेली यह समझ रही थी कि कौन सा संज्ञा सभी उद्देश्य "उपयोग" क्रिया को देना है। (एक इन्फोकॉम गेम में, आप "स्पूल को रस्सी से लें, रस्सी को हुक में बांधें, फिर रस्सी को स्लाइड करें"; एक एसए गेम में, आप "स्पूल का उपयोग करें। रुच का उपयोग करें।") – abarnert

10

जुड़ा हुआ सवाल का जवाब विशेष रूप से इस का कहना है:

एक @classmethod एक "वैकल्पिक निर्माता" ऐसा करने के लिए, वहाँ सभी stdlib-itertools.chain.from_iterable, दिनांक से अधिक उदाहरण हैं मुहावरेदार तरीका है .datetime.fromordinal, आदि

तो मुझे नहीं पता कि आपको यह कैसे पता चला कि क्लासमेड का उपयोग करना स्वाभाविक रूप से खराब है। मुझे वास्तव में आपकी विशिष्ट स्थिति में एक वर्ग विधि का उपयोग करने का विचार पसंद है, क्योंकि यह कोड का पालन करता है और एपीआई का उपयोग आसान बनाता है।

विकल्प तो जैसे डिफ़ॉल्ट निर्माता तर्क का उपयोग करने के होगा:

class Entity(object): 
    def __init__(self, id, db_connection, data=None): 
     self.id = id 
     self.db_connection = db_connection 
     if data is None: 
      self.data = self.from_id(id, db_connection) 
     else: 
      self.data = data 

    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     return db_connection.find(filters) 

मैं classmethod संस्करण है कि आप मूल रूप से हालांकि लिखा पसंद करते हैं। विशेष रूप से data काफी अस्पष्ट है।

16

पाइथन में स्थैतिक विधियों का उपयोग कब और कैसे किया जाता है?

ग्लिब उत्तर है: अक्सर नहीं।

यहां तक ​​कि ग्लिबर लेकिन बेकार जवाब के रूप में काफी नहीं है: जब वे आपका कोड अधिक पठनीय बनाते हैं।


पहले, आइए the docs करने के लिए एक चक्कर डालें:

अजगर में स्टेटिक तरीकों जावा या C++ पाए जाने वाले के समान हैं। वैकल्पिक श्रेणी रचनाकार बनाने के लिए उपयोगी एक संस्करण के लिए classmethod() भी देखें।

तो, जब आपको सी ++ में स्थिर विधि की आवश्यकता होती है, तो आपको पाइथन में एक स्थिर विधि की आवश्यकता है, है ना?

अच्छा, नहीं।

जावा में, कोई फ़ंक्शंस नहीं है, केवल विधियां हैं, इसलिए आप छद्म-कक्षाएं बनाते हैं जो स्थिर विधियों के बंडल हैं। पायथन में एक ही चीज़ करने का तरीका केवल मुफ्त कार्यों का उपयोग करना है।

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

सी ++ में जावा के समान सीमा नहीं है, लेकिन कई सी ++ शैलियों वैसे भी समान हैं। (दूसरी तरफ, यदि आप एक "आधुनिक सी ++" प्रोग्रामर हैं, जो "मुक्त फ़ंक्शंस क्लास इंटरफ़ेस का हिस्सा हैं" को आंतरिक बनाते हैं, तो "पाइथन के लिए" जहां कहीं भी स्थिर तरीके हैं "के लिए आपकी सहजताएं शायद पाइथन के लिए बहुत सभ्य हैं।)

एक @staticmethod मूल रूप से सिर्फ एक वैश्विक समारोह है:


लेकिन यदि आप किसी अन्य भाषा से की तुलना में, पहले सिद्धांतों से इस पर आ रहे हैं, वहाँ एक आसान तरीका चीजों को देखने की है।यदि आपके पास foo_module.bar() फ़ंक्शन है जो किसी कारण से अधिक पठनीय होगा यदि इसे foo_module.BazClass.bar() के रूप में लिखा गया है, तो इसे @staticmethod बनाएं। यदि नहीं, तो मत करो। वास्तव में यह सब कुछ है। एकमात्र समस्या एक मूर्खतापूर्ण पायथन प्रोग्रामर के लिए और अधिक पठनीय के लिए अपने सहज ज्ञान का निर्माण कर रही है।

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


अंत में, अपने विशिष्ट प्रश्न के लिए हो रही:

एकमात्र कारण ग्राहकों को कभी आईडी के आधार पर डेटा को देखने के लिए की जरूरत है एक Entity, कि एक कार्यान्वयन विस्तार आप को उजागर नहीं किया जाना चाहिए की तरह लगता है के निर्माण के लिए है , और यह क्लाइंट कोड को और अधिक जटिल बनाता है। बस एक कन्स्ट्रक्टर का उपयोग करें। यदि आप अपने __init__ को संशोधित नहीं करना चाहते हैं (और आप सही हैं कि अच्छे कारण हैं जो आप नहीं चाहते हैं), @classmethod को वैकल्पिक कन्स्ट्रक्टर के रूप में उपयोग करें: Entity.from_id(id_number, db_connection)

दूसरी तरफ, यदि वह लुकअप कुछ ऐसा है जो अन्य मामलों में ग्राहकों के लिए स्वाभाविक रूप से उपयोगी है, जिनके पास Entity निर्माण के साथ कुछ लेना देना नहीं है, ऐसा लगता है कि Entity वर्ग (या कम से कम नहीं एक ही मॉड्यूल में कुछ और)। तो, बस इसे एक मुफ्त समारोह बनाओ।