2010-12-20 16 views
6

मेरे पास एक मॉड्यूल है जिसमें एक ऐसा फ़ंक्शन है जिसका प्रोटोटाइप थ्रेड क्लास के समान है।विकल्प के साथ पायथन सजावट

def do(fn, argtuple=(), kwargdict={}, priority=0, 
      block=False, timeout=0, callback=None, daemon=False) 

    # do stuff 

fn एक प्रतिदेय है, और argtuple और kwargdict स्थितीय और शब्दकोश तर्क fn प्रतिदेय को दे दिया जाएगा, जब यह कहा जाता हो जाता है।

अब मैं इसके लिए एक सजावट लिखने की कोशिश कर रहा हूं, लेकिन मैं उलझन में हूं। मैंने कभी सजावटी पर बहुत अच्छी समझ नहीं ली है। क्या एक सजावटी बनाने का कोई तरीका है कि मैं उपरोक्त विकल्पों को समय-समय पर सेट कर सकता हूं, लेकिन फ़ंक्शन कहलाते समय argtuple और kwargdict में गुजरता है।

उदाहरण के लिए

तो:

@do(priority=2) 
def decoratedTask(arg, dic=3): 
    #do stuff 

decoratedTask(72) 

मैं कैसे सजाया समारोह में क्रम तर्क 72 से होकर गुजरेगा मैं उलझन में हूँ। मुझे लगता है कि सजावटकर्ता को एक कक्षा होने की आवश्यकता है जहां __call__ विधि फ़ंक्शन कॉल लौटाती है, लेकिन मुझे इस तरह के तर्कों में पारित होने के सिंटैक्स को अनिश्चितता है।

क्या यह समझ में आता है?

उत्तर

14

सजावटी क्या करता है कि यह कार्य को तर्क के रूप में लेता है और एक समारोह भी देता है, आमतौर पर सजावट में बनाया गया एक नया फ़ंक्शन।

उस नए फ़ंक्शन को आपके द्वारा सजाने वाले फ़ंक्शन के समान पैरामीटर लेने की आवश्यकता है, और इसे मूल फ़ंक्शन को कॉल करने की भी आवश्यकता है।

अब, जब आपके पास तर्क के साथ सजावट है, तो एक अतिरिक्त स्तर चल रहा है। उस सजावटी को तर्क लेना चाहिए, और एक सजावटी लौटाएं। आपके द्वारा उपयोग किया जाने वाला फ़ंक्शन वास्तव में एक सजावटी नहीं है, बल्कि एक सजावटी!

>>> def mydeco(count): 
...  def multipass(fn): 
...   def caller(*args, **kw): 
...    return [fn(*args, **kw) for x in range(count)] 
...   return caller 
...  return multipass 
... 
>>> @mydeco(5) 
... def printer(text): 
...  print(text) 
... 
>>> printer("Yabbadabbadoo!") 
Yabbadabbadoo! 
Yabbadabbadoo! 
Yabbadabbadoo! 
Yabbadabbadoo! 
Yabbadabbadoo! 
[None, None, None, None, None] 

आप इन decoratormakers वर्गों के रूप में कार्यान्वित के उदाहरण देखेंगे कि:

यहाँ एक उदाहरण है। मैं इसे भी कैसे पसंद करता हूं (हालांकि मैं आमतौर पर एक सजावट नहीं कर रहा हूं), लेकिन फ़ंक्शन में फ़ंक्शन में कोई फ़ंक्शन काम करता है। :)

+0

धन्यवाद इसने इसे और अधिक स्पष्ट बना दिया है। मैं बस उस तरह गहरी घोंसला वाली कार्य परिभाषाओं से भ्रमित हो जाता हूं। मुझे यकीन है कि मैं समझता हूँ। इसे स्वीकार करने से पहले मुझे इसके साथ थोड़ा सा खेलने दो। – Falmarri

4

यह नहीं है कि सजावटी वाक्यविन्यास कैसे काम करता है। जब आप @do(priority=2) लिखते हैं, तो पायथन do(priority=2) का मूल्यांकन करेगा और सजावट के रूप में उस कॉल के परिणाम का उपयोग करेगा। इसके लिए

decorator=do(priority=2) 
@decorator 

आशुलिपि है तो आप वास्तव में do एक डेकोरेटर कारखाने बनाना चाहते: तुम यह सब स्थितीय तर्क ले और एक डेकोरेटर वापस जाने के लिए चाहते हैं।

def do(args=(), kwargs={}, ...): 
    def _decorator(fn): 
     def newfn(*args, **kwargs): 
      return fn(*args, **kwargs) 
     return newfn 
    return _decorator 

सूचना वहाँ वास्तव में यहाँ तीन कार्यों हैं कि!

  • do समारोह है कि हम अपने डेकोरेटर मिलता है कहते हैं
  • _decorator वास्तविक डेकोरेटर लौटे है, जो do
  • के तर्क पर निर्भर करता है के बाद से एक डेकोरेटर लेता है (ताकि जैसे do(priority=2) एक उदाहरण डेकोरेटर है) आउटपुट के रूप में एक फ़ंक्शन और आउटपुट के रूप में फ़ंक्शन देता है, हमें उस फ़ंक्शन को परिभाषित करने की आवश्यकता होती है जिसे सजावटी रिटर्न देता है। newfn वह फ़ंक्शन है।

उदाहरण:

>>> def rename(name): 
...  def _decorator(fn): 
...    def renamed(*args, **kwargs): 
...      return fn(*args, **kwargs) 
...    renamed.__name__ = name 
...    return renamed 
...  return _decorator 
... 
>>> @rename('I like omelettes in the morning.') 
... def foo(): 
...  return 'bar' 
... 
>>> foo() 
'bar' 
>>> foo.__name__ 
'I like omelettes in the morning.' 

या समतुल्य रूप

>>> omeletter = rename('I like omelettes in the morning.') 
>>> @omeletter 
... def foo(): 
...  return 'bar' 
... 
>>> foo() 
'bar' 
>>> foo.__name__ 
'I like omelettes in the morning.' 

वैसे, परिवर्तनशील डिफ़ॉल्ट तर्क के साथ देखभाल () और {}; मुझे यकीन है कि आप खतरे को जानते हैं!

+0

katrielalex: '()' परिवर्तनशील नहीं है और एक डिफ़ॉल्ट के रूप में ठीक है। आप '{}' के बारे में सही हैं। – Duncan

+0

@ डंकन - सच में सच! धन्यवाद =) – katrielalex

+0

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

3

के रूप में अन्य उत्तर समझा दिया है, सज्जाकार सामान्य रूप से एक भी निहित समारोह तर्क जब सिर्फ इस तरह उनके नाम का उपयोग करके लागू पारित कर रहे हैं:

@deco 
    def somefunc(...): pass 

कौन सा रूप में एक ही बात करता है:

def somefunc(...): pass 
    somefunc = deco(somefunc) 

इस स्थिति में तर्क फ़ंक्शन परिभाषा का एक संकलित संस्करण है जो तुरंत अनुसरण करता है। ऐसे मामलों में सजावटी एक कॉल करने योग्य लौटाता है जिसे संकलित फ़ंक्शन बॉडी के बजाय फ़ंक्शन नाम पर असाइन किया जाता है, जैसा आमतौर पर मामला होता है।

लेकिन अगर एक डेकोरेटर समारोह स्पष्ट रूप से एक या अधिक तर्क दिया जाता है जब यह इस तरह, लागू है:

@deco(args) 
    def somefunc(...): pass 

यह के बराबर हो जाता है:

def somefunc(...): pass 
    somefunc = deco(args)(somefunc) 

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

फिर – के रूप में अन्य लोगों ने – बताया है इस सज्जाकार स्पष्ट अर्थ में तर्क, डेकोरेटर कारखानों से पारित कर दिया है कि वे का निर्माण और वापसी 'नियमित' डेकोरेटर कार्यों में आता है।

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

यहां बताया गया है कि आप अपने मॉड्यूल में do() फ़ंक्शन के लिए सजावट कैसे कोड कर सकते हैं। नीचे दिए गए कोड में मैंने परिभाषित किया है कि यह कॉल करने से पहले फ़ंक्शन के तर्कों को प्रिंट करता है।

def do(fn, args=tuple(), kwargs={}, priority=0, 
     block=False, timeout=0, callback=None, daemon=False): 
    # show arguments 
    print ('in do(): fn={!r}, args={}, kwargs={}, priority={},\n' 
      '   block={}, timeout={}, callback={}, daemon={}' 
      .format(fn.__name__, args, kwargs, priority, 
        block, timeout, callback, daemon)) 
    # and call function 'fn' with its arguments 
    print (' calling {}({}, {})'.format(
       fn.__name__, 
       ', '.join(map(str, args)) if args else '', 
       ', '.join('{}={}'.format(k, v) for k,v in kwargs.items()) 
        if kwargs else '') 
     ) 
    fn(*args, **kwargs) 

def do_decorator(**do_kwargs): 
    def decorator(fn): 
     def decorated(*args, **kwargs): 
      do(fn, args, kwargs, **do_kwargs) 
     return decorated 
    return decorator 

@do_decorator(priority=2) 
def decoratedTask(arg, dic=42): 
    print 'in decoratedTask(): arg={}, dic={}'.format(arg, dic) 

decoratedTask(72, dic=3) 

आउटपुट:

in do(): fn='decoratedTask', args=(72,), kwargs={'dic': 42}, priority=2, 
     block=False, timeout=0, callback=None, daemon=False 
    calling decoratedTask(72, dic=3) 
in decoratedTask(): arg=72, dic=3 

यहाँ कैसे इस जटिल देख सामान काम करता है की एक झटका-दर-झटका खाता है:

बाहरी डेकोरेटर समारोह do_decorator(), एक और भीतरी डेकोरेटर समारोह जो यह रिटर्न को परिभाषित करता है , यहां रचनात्मक रूप से decorator नाम दिया गया है।

क्या decorator करता है क्या परिभाषित कार्यों है कि एक साधारण से सजाया गया है 'कोई तर्क' परिदृश्य — यहाँ जो परिभाषित करने और अभी तक एक और – लेकिन अंतिम – लौटा रहा है नेस्टेड समारोह decorated कहा जाता है, जो सिर्फ मॉड्यूल के do() फ़ंक्शन को कॉल करने और करने के लिए होता है do() फ़ंक्शन के उपयोग के लिए लक्षित किए गए लोगों के साथ, आमंत्रण के बिंदु से कोई भी तर्क दोनों को पास करता है।

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

0

मैं @Lennart Regebro, ऊपर @katrielalex और @martineau जवाब पसंद है, लेकिन बहुत सुनने में अजीब लग का खतरा होता है, मैं एक कहानी आधारित उदाहरण लेखन का जोखिम होगा।

इस दो भाग कहानी में, हम शिक्षकों कि अनुभव से छात्रों सिखाना हो सकता है।

def self_taught_teacher(fn): 
    ''' I teach students, because I had no teacher to guide me.''' 
    def a_student(*real_life_issues, **details_about_my_world): 
     ''' I'm a student who has been trained by a teacher 
     who has taught me about the problems I may encounter in real life. 

     Thanks to my teacher for giving me extra knowledge.  
     ''' 
     print 'I have to remember what my teacher taught me.' 
     my_answer = fn(*real_life_issues, **details_about_my_world) 
     print 'Ah yes, I made the right decision.' 
     # 
     return my_answer 
    # 
    return a_student 

@self_taught_teacher 
def student_named_Lisa_practicing_maths(length, width, height): 
    print 'Im consulting my powers of maths...' 
    return length * width * height 

चलो देखते हैं क्या छात्र जानता है ...

>>> answer = student_named_Lisa_practicing_maths(10, 20, height=3) 
I have to remember what my teacher taught me. 
Im consulting my powers of maths... 
Ah yes, I made the right decision. 
>>> answer 
600 

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

def professor_who_trains_teachers(*subjects, **to_train_teachers): 
    '''I am a profeseur. I help train teachers. ''' 
    # 
    def a_teacher_who_gets_trained(fn): 
     ''' I learn subjects I should teach to my students.''' 
     knowledge = [s for s in subjects] 
     # 
     def a_student(*real_life_issues, **details_about_my_world): 
      ''' I'm a student who has been trained by a teacher 
      who has taught me about the problems I may encounter in real life. 

      Thanks to my teacher for giving me extra knowledge.  
      ''' 
      print '(I know %s that i learned from my teacher,...)' % \ 
        [information for information in knowledge] 
      my_answer = fn(*real_life_issues, **details_about_my_world) 
      print 'Ah yes, I made the right decision.' 
      # 
      return my_answer 
     # 
     return a_student 
     # 
     # 
    return a_teacher_who_gets_trained 

तो हम एक शिक्षक प्रशिक्षित कर सकते हैं और उन्हें एक छात्र को पढ़ाने के हैं ...

>>> teacher1 = professor_who_trains_teachers('math','science') 
>>> teacher1 
<function a_teacher_who_gets_trained at 0x104a7f500> 
>>> teacher1.__name__ 
'a_teacher_who_gets_trained' 
>>> 

@teacher1 
def student_named_Lisa_practicing_maths(length, width, height): 
    print 'Im consulting my powers of maths...' 
    return length * width * height 

छात्र उनके गणित जानता है कि ..

>>> answer = student_named_Lisa_practicing_maths(20, 10, 2) 
(I know ['math', 'science'] that i learned from my teacher,...) 
Im consulting my powers of maths... 
Ah yes, I made the right decision. 
>>> 
>>> answer 
400 

और हम शिक्षक बाहर बना सकते हैं ठीक है।

@professor_who_trains_teachers('math', 'science', remember='patience') 
def student_named_Lisa_practicing_maths(length, width, height): 
    print 'Im consulting my powers of maths...' 
    return length * width * height 

फिर नए छात्र उनके गणित कर सकते हैं ...

>>> answer = student_named_Lisa_practicing_maths(10, 20, height=3) 
(I know ['math', 'science'] that i learned from my teacher,...) 
Im consulting my powers of maths... 
Ah yes, I made the right decision. 
>>> 
>>> answer 
600 
संबंधित मुद्दे