2012-01-09 8 views
12

मैं एक सजावटी लिख रहा हूं, और विभिन्न कष्टप्रद कारणों से [0] यह जांचने के लिए उपयुक्त होगा कि यह फ़ंक्शन लपेटने वाला कार्य स्टैंड-अलोन या कक्षा के हिस्से के रूप में परिभाषित किया जा रहा है (और आगे कौन सी कक्षाएं जो नई कक्षा है उपवर्गीकरण)।पायथन: क्या एक सजावटी निर्धारित कर सकता है कि कक्षा में एक फ़ंक्शन परिभाषित किया जा रहा है या नहीं?

उदाहरण के लिए:

def my_decorator(f): 
    defined_in_class = ?? 
    print "%r: %s" %(f, defined_in_class) 

@my_decorator 
def foo(): pass 

class Bar(object): 
    @my_decorator 
    def bar(self): pass 

प्रिंट चाहिए:

<function foo …>: False 
<function bar …>: True 

इसके अलावा, कृपया ध्यान दें:

  • बिंदु सज्जाकार समारोह लागू होते हैं पर अभी भी एक समारोह हो जाएगा, नहीं एक अनबाउंड विधि, इसलिए उदाहरण/अनबाउंड विधि के लिए परीक्षण (typeof या inspect का उपयोग करके) नहीं होगा टी काम नहीं
  • कृपया केवल सुझाव है कि हल इस समस्या प्रदान करते हैं - मुझे पता है इसी तरह के कई तरीके यह अंत पूरा करने के लिए (पूर्व, एक वर्ग डेकोरेटर का प्रयोग करके) हैं कि हूँ, लेकिन मैं उन पर सजावट समय होने के लिए चाहते हैं, बाद में नहीं।

[0]: विशेष रूप से, मैं एक सजावट लिख रहा हूं जो nose के साथ पैरामीटर परीक्षण करना आसान बना देगा। हालांकि, noseunittest.TestCase के उप-वर्गों पर परीक्षण जनरेटर चलाने के लिए नहीं होगा, इसलिए मैं अपने सजावटी को यह निर्धारित करने में सक्षम होना चाहूंगा कि इसका उपयोग TestCase के उप-वर्ग के अंदर किया जा रहा है और उचित त्रुटि के साथ विफल हो गया है। स्पष्ट समाधान - लिपटे समारोह कॉल करने से पहले isinstance(self, TestCase) का उपयोग कर काम नहीं करता, क्योंकि लिपटे समारोह जरूरत एक जनरेटर है, जो सभी पर निष्पादित नहीं हो सकता है।

+0

उत्सुकता के लिए, यहां परिणाम है: http://paste.pocoo.org/show/532430/ –

उत्तर

11

जब आप कोई विधि लपेटते हैं तो inspect.stack() के आउटपुट पर नज़र डालें। जब आपके सजावटी का निष्पादन चल रहा है, तो मौजूदा स्टैक फ्रेम आपके सजावटी को फ़ंक्शन कॉल है; अगला स्टैक फ्रेम नीचे @ रैपिंग एक्शन है जिसे नई विधि पर लागू किया जा रहा है; और तीसरा फ्रेम कक्षा परिभाषा ही होगा, जो एक अलग स्टैक फ्रेम की योग्यता है क्योंकि कक्षा परिभाषा इसका अपना नामस्थान है (जिसे इसे निष्पादित करने पर कक्षा बनाने के लिए लपेटा जाता है)।

मेरा सुझाव है, इसलिए:

defined_in_class = (len(frames) > 2 and 
        frames[2][4][0].strip().startswith('class ')) 

तो उन पागल अनुक्रमित के सभी unmaintainable देखो, तो आप अधिक स्पष्ट फ्रेम के अलावा इस तरह, टुकड़ा द्वारा टुकड़ा लेने के द्वारा किया जा सकता है:

import inspect 
frames = inspect.stack() 
defined_in_class = False 
if len(frames) > 2: 
    maybe_class_frame = frames[2] 
    statement_list = maybe_class_frame[4] 
    first_statment = statement_list[0] 
    if first_statment.strip().startswith('class '): 
     defined_in_class = True 

ध्यान दें कि मैं नहीं आपके रैपर चलाने के समय कक्षा के नाम या विरासत पदानुक्रम के बारे में पाइथन से पूछने का कोई तरीका देखता हूं; वह बिंदु प्रसंस्करण चरणों में "बहुत जल्दी" है, क्योंकि वर्ग निर्माण अभी तक समाप्त नहीं हुआ है। या तो class के साथ शुरू होने वाली रेखा को पार्स करें और उसके बाद सुपरक्लास खोजने के लिए उस फ्रेम के ग्लोबल्स को देखें, या फिर frames[1] कोड ऑब्जेक्ट के चारों ओर पोक करें ताकि आप यह जान सकें कि आप क्या सीख सकते हैं - ऐसा लगता है कि उपरोक्त कोड में वर्ग का नाम frames[1][0].f_code.co_name हो रहा है , लेकिन मुझे यह जानने के लिए कोई रास्ता नहीं मिल रहा है कि वर्ग निर्माण समाप्त होने पर सुपरक्लास को कौन सा जोड़ा जाएगा।

-2

मुझे लगता है कि inspect मॉड्यूल में कार्य करता है कि आप क्या चाहते हैं, विशेष रूप isfunction और ismethod करना होगा:

:

>>> import inspect 
>>> def foo(): pass 
... 
>>> inspect.isfunction(foo) 
True 
>>> inspect.ismethod(foo) 
False 
>>> class C(object): 
...  def foo(self): 
...    pass 
... 
>>> inspect.isfunction(C.foo) 
False 
>>> inspect.ismethod(C.foo) 
True 
>>> inspect.isfunction(C().foo) 
False 
>>> inspect.ismethod(C().foo) 
True 

फिर आप बाध्य या अनबाउंड विधि के अंदर समारोह का उपयोग करने के Types and Members table पालन कर सकते हैं

>>> C.foo.im_func 
<function foo at 0x1062dfaa0> 
>>> inspect.isfunction(C.foo.im_func) 
True 
>>> inspect.ismethod(C.foo.im_func) 
False 
+0

मुझे उम्मीद है कि 'निरीक्षण' शामिल होगा, लेकिन जिस तरह से आपने वर्णन किया है। मेरा पहला नोट देखें (उस समय सजावटी लागू होते हैं, फ़ंक्शन केवल 'फ़ंक्शन' का एक उदाहरण है, न कि 'unboundmethod' या 'boundmethod')। –

+0

उन्होंने स्पष्ट रूप से यह नहीं बताया, लेकिन उनका मुद्दा यह था कि यह कक्षा परिभाषा समय पर काम नहीं करता है। –

+0

हां, आप सही हैं - मैंने अपना प्रश्न अधिक स्पष्ट होने के लिए अपडेट किया है। मैं पूरी तरह से उत्तर की सराहना करता हूं, यह सिर्फ मेरे प्रश्न का समाधान नहीं करता है। –

2

कुछ hacky समाधान है कि मुझे मिल गया है:

import inspect 

def my_decorator(f): 
    args = inspect.getargspec(f).args 
    defined_in_class = bool(args and args[0] == 'self') 
    print "%r: %s" %(f, defined_in_class) 

लेकिन यह फ़ंक्शन में self तर्क की उपस्थिति पर निर्भर करता है।

1

आप यह जांच सकते हैं कि सजावटी को मॉड्यूल स्तर पर या किसी अन्य चीज़ के भीतर घोंसला कहा जा रहा है या नहीं।

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>" 
+0

एचआरएम ... लेकिन यह कुछ के भीतर घोंसला वर्गों के लिए काम नहीं करेगा, है ना? (उदा, 'def mk_class(): वर्ग MyClass: ...; MyClass वापस लौटें) –

+0

फ़ंक्शन या रिटर्न क्या करता है 'inspect.currentframe()। f_back' के मान को प्रभावित नहीं करता है; केवल एक चीज जो मायने रखती है वह है जहां 'my_decorator' कहा जाता है, और यह हमेशा एक ही स्तर पर जा रहा है जिस पर यह चारों ओर लपेटता है परिभाषित किया जाता है। – chepner

+0

मैंने आपकी टिप्पणी को गलत समझा होगा; अन्य कार्यों के अंदर कोई भी सजाया गया कार्य सजावट को तब तक नहीं चलाएगा जब तक कि संलग्न कार्य को बुलाया न जाए, लेकिन लपेटा हुआ फ़ंक्शन का दायरा सही ढंग से गणना की जाएगी। जब आप सजावटी चलाते हैं तो उस बिंदु पर संलग्न स्कोप की एक सूची देखने के लिए आप सजावटी के अंदर इसका उपयोग कर सकते हैं: 'x [0] .f_code.co_name x के लिए inspect.stack() [1:]]'। – chepner

2

पार्टी यहाँ करने के लिए एक छोटी सी देर हो गई, लेकिन इस करता है, तो एक डेकोरेटर एक समारोह एक वर्ग में परिभाषित पर किया जा रहा है का निर्धारण करने का एक विश्वसनीय साधन साबित हो गया है:

frames = inspect.stack() 

className = None 
for frame in frames[1:]: 
    if frame[3] == "<module>": 
     # At module level, go no further 
     break 
    elif '__module__' in frame[0].f_code.co_names: 
     className = frame[0].f_code.co_name 
     break 

का लाभ स्वीकार्य उत्तर पर यह विधि यह है कि यह उदाहरण के साथ काम करता है py2exe।

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

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