2011-12-28 13 views
6

में ओवरलोडिंग के लिए सजावटी मुझे पता है कि यह तर्कों के प्रकार की देखभाल करने वाले कार्यों को लिखने के लिए पाइथोनिक नहीं है, लेकिन ऐसे मामले हैं जब प्रकारों को अनदेखा करना असंभव है क्योंकि उन्हें अलग-अलग संभाला जाता है।पाइथन

isinstance का एक गुच्छा होने के कारण आपके काम में केवल बदसूरत है; क्या कोई फ़ंक्शन सजावट उपलब्ध है जो फ़ंक्शन ओवरलोड को सक्षम बनाता है? कुछ इस तरह:

@overload(str) 
def func(val): 
    print('This is a string') 

@overload(int) 
def func(val): 
    print('This is an int') 

अद्यतन:

यहाँ कुछ टिप्पणियों मैं David Zaslavsky's answer पर छोड़ दिया है:

में कुछ संशोधन [एस] के साथ, यह मेरा प्रयोजनों सुंदर सूट कुंआ। आपके कार्यान्वयन में मैंने एक और सीमा देखी, चूंकि आप func.__name__ को कुंजीपटल कुंजी के रूप में उपयोग करते हैं, तो आप मॉड्यूल के बीच टकराव नाम देने के लिए प्रवण होते हैं, जो हमेशा वांछनीय नहीं होता है। [जारी]

[शेष भाग।] उदाहरण के लिए, अगर मैं एक मॉड्यूल है कि func overloads, और एक अन्य पूरी तरह से असंबंधित मॉड्यूल भी overloads func है, ये भार के भिड़ना होगा क्योंकि समारोह प्रेषण dict वैश्विक है। उस निर्देश को किसी भी तरह मॉड्यूल के लिए स्थानीय बनाया जाना चाहिए। और न केवल वह, इसे किसी प्रकार की 'विरासत' का भी समर्थन करना चाहिए। [cont'd]

[cont।] 'विरासत' से मेरा मतलब यह है: कहें कि मेरे पास कुछ ओवरलोड के साथ मॉड्यूल first है। फिर दो और मॉड्यूल जो असंबंधित हैं लेकिन प्रत्येक आयात first; इन दोनों मॉड्यूल में पहले से मौजूद मौजूदा लोगों को नए अधिभार जोड़ते हैं जिन्हें उन्होंने अभी आयात किया था। ये दो मॉड्यूल first में ओवरलोड का उपयोग करने में सक्षम होना चाहिए, लेकिन जो नए वे अभी जोड़े गए हैं उन्हें मॉड्यूल के बीच एक दूसरे के साथ टकराव नहीं करना चाहिए। (यह सही करने के लिए, अब मैं इसके बारे में लगता है कि वास्तव में बहुत मुश्किल है।)

इन समस्याओं में से कुछ संभवतः डेकोरेटर वाक्य रचना एक छोटा सा बदलकर हल किया जा सकता:

first.py

@overload(str, str) 
def concatenate(a, b): 
    return a + b 

@concatenate.overload(int, int) 
def concatenate(a, b): 
    return str(a) + str(b) 

second.py

from first import concatenate 

@concatenate.overload(float, str) 
def concatenate(a, b): 
    return str(a) + b 
+0

हम्म ... अपने संपादन में ऐसा है, तो वास्तव में क्या आप '' first.py' में contatenate.overload' द्वारा मतलब है? जैसा लिखा है, यह 'concatenate' फ़ंक्शन' की 'अधिभार' विशेषता तक पहुंचने का प्रयास करेगा, जो इस उदाहरण में मौजूद नहीं है। –

+0

@DavidZaslavsky समारोह के पहले अधिभार 'साथ @ overload' है, जो एक प्रतिदेय वस्तु है कि लौटने और विशेषता' overload' जाएगा सजाया जाना चाहिए। बाद के सभी भार के इस पहले से ही विद्यमान वस्तु, '@ object.overload' के साथ सजाया जाना चाहिए, इसलिए वहाँ वस्तु में केवल एक dict, नहीं एक वैश्विक dict है। (यह मानक '@ प्रॉपर्टी 'के समान काम करता है।) मैं इसे कार्यान्वित करता हूं और इसे पोस्ट करते समय आपको सूचित करता हूं। –

+1

यह पारंपरिक ओवरलोडिंग वाक्यविन्यास से प्रस्थान होगा। लेकिन अगर है कि तुम क्या चाहते हो, [अधिभार पैकेज] पर एक नज़र (http://pypi.python.org/pypi/overload/1 है।1) (जो मैं अपने जवाब में संपादित करने वाला हूं)। यह अनिवार्य रूप से उस विधि का उपयोग करता है। –

उत्तर

4

त्वरित जवाब: है वहाँ PyPI पर एक overload package जो इस अधिक मजबूती को लागू करता है जैसा कि मैंने नीचे वर्णित किया है, उससे भी कम है थोड़ा अलग वाक्यविन्यास। इसे केवल पायथन 3 के साथ काम करने की घोषणा की गई है, लेकिन यह पाइथन 2 के साथ काम करने के लिए केवल मामूली संशोधन (यदि कोई है, मैंने कोशिश नहीं की है) की आवश्यकता होगी।


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

from collections import defaultdict 

def determine_types(args, kwargs): 
    return tuple([type(a) for a in args]), \ 
      tuple([(k, type(v)) for k,v in kwargs.iteritems()]) 

function_table = defaultdict(dict) 
def overload(arg_types=(), kwarg_types=()): 
    def wrap(func): 
     named_func = function_table[func.__name__] 
     named_func[arg_types, kwarg_types] = func 
     def call_function_by_signature(*args, **kwargs): 
      return named_func[determine_types(args, kwargs)](*args, **kwargs) 
     return call_function_by_signature 
    return wrap 

overload दो वैकल्पिक तर्क, एक टपल सभी स्थितीय तर्क के प्रकार और सभी कीवर्ड तर्कों के नाम प्रकार मैपिंग का प्रतिनिधित्व tuples के एक टपल का प्रतिनिधित्व करने के साथ बुलाया जाना चाहिए:

यहाँ एक सरल कार्यान्वयन है। यहाँ एक उपयोग उदाहरण है:

>>> @overload((str, int)) 
... def f(a, b): 
...  return a * b 

>>> @overload((int, int)) 
... def f(a, b): 
...  return a + b 

>>> print f('a', 2) 
aa 
>>> print f(4, 2) 
6 

>>> @overload((str,), (('foo', int), ('bar', float))) 
... def g(a, foo, bar): 
...  return foo*a + str(bar) 

>>> @overload((str,), (('foo', float), ('bar', float))) 
... def g(a, foo, bar): 
...  return a + str(foo*bar) 

>>> print g('a', foo=7, bar=4.4) 
aaaaaaa4.4 
>>> print g('b', foo=7., bar=4.4) 
b30.8 

इस की कमियों शामिल

  • यह वास्तव में जांच नहीं करता समारोह डेकोरेटर करने के लिए लागू किया जाता है डेकोरेटर करने के लिए दिए गए तर्कों के साथ भी संगत है कि। आप

    @overload((str, int)) 
    def h(): 
        return 0 
    

    लिख सकते हैं और फ़ंक्शन कहलाते समय आपको एक त्रुटि मिल जाएगी।

  • यह शान से इस मामले में जहां कोई अतिभारित संस्करण तर्क पारित कर के प्रकार के लिए इसी मौजूद संभाल नहीं करता है (यह एक और वर्णनात्मक त्रुटि को बढ़ाने के लिए मदद मिलेगी)

  • यह नाम दिया है और स्थितीय तर्क में भेद किया, तो

    g('a', 7, bar=4.4) 
    

    काम नहीं करता है।

  • नेस्टेड इस का उपयोग करते हुए, g के लिए परिभाषा के रूप में शामिल कोष्ठकों का एक बहुत हैं।
  • टिप्पणी में उल्लेख किया है, यह काम करता है विभिन्न मॉड्यूल में एक ही नाम होने के साथ सौदा नहीं करता है।

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

+0

कुछ संशोधन के साथ, यह मेरे उद्देश्यों को अच्छी तरह से अनुकूल करेगा। आपके कार्यान्वयन में मैंने एक और सीमा देखी, चूंकि आप 'func .__ name__' का उपयोग कुंजीपटल कुंजी के रूप में करते हैं, तो आप मॉड्यूल के बीच टकराव नाम देने के लिए प्रवण होते हैं, जो हमेशा वांछनीय नहीं होता है। [cont'd] –

+0

[cont।] उदाहरण के लिए, यदि मेरे पास एक मॉड्यूल है जो 'func' को अधिभारित करता है, और दूसरा _ अपूर्ण रूप से असंबंधित_ मॉड्यूल जो 'func' को ओवरलोड करता है, तो इन अधिभारों को टक्कर मिल जाएगी क्योंकि फ़ंक्शन प्रेषण बिंदु वैश्विक है। उस निर्देश को किसी भी तरह मॉड्यूल के लिए स्थानीय बनाया जाना चाहिए। और न केवल वह, इसे किसी प्रकार की 'विरासत' का भी समर्थन करना चाहिए। [cont'd] –

+0

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

0

यह आपके प्रश्न का सीधे उत्तर नहीं देता है, लेकिन यदि आप वास्तव में कुछ ऐसा करना चाहते हैं जो विभिन्न प्रकारों के लिए ओवरलोडेड फ़ंक्शन की तरह व्यवहार करता है और (काफी हद तक) आइंस्टेंस का उपयोग नहीं करना चाहता है तो मैं कुछ सुझाव दूंगा:

def func(int_val=None, str_val=None): 
    if sum(x != None for x in (int_val, str_val)) != 1: 
     #raise exception - exactly one value should be passed in 
    if int_val is not None: 
     print('This is an int') 
    if str_val is not None: 
     print('This is a string') 

उपयोग में आशय स्पष्ट है, और यह भी विभिन्न प्रकार के लिए विभिन्न विकल्पों की आवश्यकता नहीं है:

func(int_val=3) 
func(str_val="squirrel") 
+0

यह सरल उदाहरणों के लिए काम करता है, लेकिन यह कितना अच्छा है? –

+0

यदि आपके पास बहुत सारी संभावनाएं हैं तो आप इसके बजाय '** kwargs' का उपयोग कर सकते हैं और इसे थोड़ा और प्रोग्रामेटिक बना सकते हैं। मैंने इसे 17 स्वीकार्य खोजशब्दों के साथ एक कन्स्ट्रक्टर में उपयोग किया है और यह मेरे लिए ठीक काम करता है। –

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

  • कोई संबंधित समस्या नहीं^_^