2012-05-25 11 views
19

यूनिट परीक्षण लिखते समय, मैं कभी-कभी एक परीक्षण काट और पेस्ट करता हूं और विधि नाम बदलने के लिए याद नहीं करता। इसके परिणामस्वरूप पिछले परीक्षण को ओवरराइट करने, प्रभावी रूप से इसे छिपाने और इसे चलाने से रोकने में परिणाम मिलता है। उदाहरण के लिए;मैं पाइथन कक्षा में डुप्लिकेट विधि नामों का पता कैसे लगा सकता हूं?

class WidgetTestCase(unittest.TestCase): 

    def test_foo_should_do_some_behavior(self): 
    self.assertEquals(42, self.widget.foo()) 

    def test_foo_should_do_some_behavior(self): 
    self.widget.bar() 
    self.assertEquals(314, self.widget.foo()) 

इस मामले में, केवल बाद परीक्षण कहा जाता हो जाएगा। क्या इस प्रकार की त्रुटि को प्रोग्रामेटिक रूप से पकड़ने का कोई तरीका है, कच्चे स्रोत कोड को सीधे पार्स करने से कम?

+1

प्रतिलिपि बनाना और चिपकाना आम तौर पर खराब कोडिंग अभ्यास का संकेत है - अधिकांश समय इसका मतलब है कि आपको किसी अन्य कार्य में कार्यक्षमता निकालना चाहिए। यदि आप इसे बाद में बदलना चाहते हैं, और इन तरह की समस्याएं हैं, तो यह समय, प्रयास को बचाएगा। –

+8

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

+0

@ स्कोटी: आप इसे कॉपी करने के बाद अपनी पहली कार्रवाई के रूप में एक विधि का नाम बदलने की आदत क्यों नहीं लेते? वैसे, यदि प्रत्येक विधि के लिए सेटअप कोड और दावे अलग-अलग हैं, तो केवल एक चीज जिसे आप वास्तव में कॉपी कर रहे हैं वह विधि का नाम है, जिसे आपने देखा होगा। –

उत्तर

23

आप अपने कोड पर चलाते हैं pylint, तो यह आपको सूचित करेंगे जब आप किसी अन्य विधि ओवरराइट है this online pylint checker में

class A(object): 
    def blah(self): 
     print("Hello World!") 

    def blah(self): 
     print("I give up!") 

:

उदाहरण के लिए, मैं इस भाग गया। सभी लापता docstrings इसके अलावा और इस तरह, मैं इस मिल:

E: 5:A.blah: method already defined line 2 
+3

या, यदि आप एक लिफ्टर चाहते हैं जो काफी परेशान नहीं है, तो pyflakes है: https://launchpad.net/pyflakes – habnabit

4

आप रनटाइम के दौरान इसे आसानी से/साफ ढंग से पहचान नहीं सकते हैं क्योंकि पुराने विधि को आसानी से बदल दिया गया है और एक सजावटी को प्रत्येक फ़ंक्शन परिभाषा पर उपयोग करना होगा। स्टेटिक विश्लेषण (पिलिंट इत्यादि) ऐसा करने का सबसे अच्छा तरीका है।

हालांकि, आप एक मेटाक्लास बनाने में सक्षम हो सकते हैं जो __setattr__ लागू करता है और परीक्षण करता है कि कोई विधि ओवरराइट की जा रही है या नहीं। - बस परीक्षण किया गया है और मेटाक्लास के __setattr__ वर्ग ब्लॉक में परिभाषित सामग्री के लिए नहीं कहा जाता है।

+1

दुख है कि '__setattr__' एक व्यवहार्य समाधान नहीं है - यह एक चालाक दृष्टिकोण की तरह लग रहा है :) –

+1

इस पर अनुवर्ती: http://stackoverflow.com/q/10762088/298479 – ThiefMaster

+1

भविष्य के पाठकों के लिए बस एक त्वरित स्पष्टीकरण, यह नहीं है कि metaclass '__setattr__' विधियों के लिए नहीं कहा जाता है, लेकिन यह केवल क्लास ** ऑब्जेक्ट ** पर विशेषताएँ सेट करते समय कहा जाता है, जो तब तक मौजूद नहीं है जब कक्षा ब्लॉक में परिभाषित नाम बाध्य किए जा रहे हैं। ThiefMaster के फॉलोअप प्रश्न में अधिक जानकारी। – Ben

6

यहाँ कैसे किसी भी विश्लेषण उपकरण की आवश्यकता के बिना सज्जाकार का उपयोग कर कार्यावधि में यह पता लगाने के लिए एक विकल्प है:

def one_def_only(): 
    names = set() 
    def assert_first_def(func): 
    assert func.__name__ not in names, func.__name__ + ' defined twice' 
    names.add(func.__name__) 
    return func 
    return assert_first_def 

class WidgetTestCase(unittest.TestCase): 
    assert_first_def = one_def_only() 

    @assert_first_def 
    def test_foo_should_do_some_behavior(self): 
    self.assertEquals(42, self.widget.foo()) 

    @assert_first_def 
    def test_foo_should_do_some_behavior(self): 
    self.widget.bar() 
    self.assertEquals(314, self.widget.foo()) 

एक का उदाहरण आयात या चलाने का प्रयास:

>>> import testcases 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "testcases.py", line 13, in <module> 
    class WidgetTestCase(unittest.TestCase): 
    File "testcases.py", line 20, in WidgetTestCase 
    @assert_first_def 
    File "testcases.py", line 7, in assert_first_def 
    assert func.__name__ not in names, func.__name__ + ' defined twice' 
AssertionError: test_foo_should_do_some_behavior defined twice 
14

एक भयानक हैक जो अनियंत्रित, कार्यान्वयन-विशिष्ट पायथन सुविधाओं का उपयोग करता है, निम्नानुसार है। आपको कभी भी कभी भी ऐसा कुछ नहीं करना चाहिए।

यह पाइथन 2.6.1 और 2.7.2 पर परीक्षण किया गया है; पाइथन 3.2 के साथ काम के रूप में काम नहीं कर रहा है, लेकिन फिर, आप Python 3.x में do this right कर सकते हैं।

import sys 

class NoDupNames(object): 

    def __init__(self): 
     self.namespaces = [] 

    def __call__(self, frame, event, arg): 
     if event == "call": 
      if frame.f_code.co_flags == 66: 
       self.namespaces.append({}) 
     elif event in ("line", "return") and self.namespaces: 
      for key in frame.f_locals.iterkeys(): 
       if key in self.namespaces[-1]: 
        raise NameError("attribute '%s' already declared" % key) 
      self.namespaces[-1].update(frame.f_locals) 
      frame.f_locals.clear() 
      if event == "return": 
       frame.f_locals.update(self.namespaces.pop()) 
     return self 

    def __enter__(self): 
     self.oldtrace = sys.gettrace() 
     sys.settrace(self) 

    def __exit__(self, type, value, traceback): 
     sys.settrace(self.oldtrace) 

उपयोग:

with NoDupNames(): 
    class Foo(object): 
     num = None 
     num = 42 

परिणाम:

NameError: attribute 'num' already declared 

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

नकारात्मक के रूप में, हमारे "सभी स्थानीय लोगों को साफ़ करें!"दृष्टिकोण यदि आप ऐसा नहीं कर सकते हैं मतलब है:

with NoDupNames(): 
    class Foo(object): 
     a = 6 
     b = 7 
     c = a * b 

क्योंकि जहाँ तक अजगर जानता है के रूप में, वहाँ कोई नाम a और b जब c = a * b क्रियान्वित किया जाता हैं, के रूप में हम उन्हें देखा था हम जितनी जल्दी उन को मंजूरी दे दी इसके अलावा, अगर। आप एक ही चर दो बार एक पंक्ति में (जैसे, a = 0; a = 1) यह भी समझ नहीं होगा इसके अलावा आवंटित । हालांकि, यह अधिक विशिष्ट वर्ग परिभाषा के लिए काम करता है।

, आप एक के अंदर वर्ग परिभाषाओं के अलावा कुछ भी नहीं रखना चाहिए NoDupNames संदर्भ। मुझे नहीं पता कि क्या होगा, शायद कुछ भी बुरा नहीं है। लेकिन मैंने कोशिश नहीं की है, इसलिए सिद्धांत में ब्रह्मांड को अपने स्वयं के प्लूघोल में चूसा जा सकता है।

यह संभवतः सबसे बुरा कोड है जिसे मैंने कभी लिखा है, लेकिन यह निश्चित रूप से मजेदार था!

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