python - Abc.ABCMeta का उपयोग इस तरह से पाइथन 2.7 और पायथन 3.5 दोनों के साथ संगत है




python-2.7 metaclass (3)

Abc.ABCMeta का उपयोग इस तरह से पाइथन 2.7 और पायथन 3.5 दोनों के साथ संगत है

अगर हम केवल पायथन 3 का उपयोग कर रहे थे (यह 3.4 में नया है ) हम कर सकते हैं:

from abc import ABC

और object बजाय ABC से प्राप्त होता है। अर्थात्:

class SomeAbstractClass(ABC):
    ...etc

आपको अभी भी एक अतिरिक्त निर्भरता (छः मॉड्यूल) की आवश्यकता नहीं है - आप माता-पिता बनाने के लिए मेटाक्लास का उपयोग कर सकते हैं (यह अनिवार्य रूप से छह मॉड्यूल with_metaclass में करता है):

import abc

# compatible with Python 2 *and* 3:
ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) 

class SomeAbstractClass(ABC):

    @abc.abstractmethod
    def do_something(self):
        pass

या आप इसे जगह में कर सकते हैं (लेकिन यह अधिक गन्दा है, और पुन: उपयोग करने के लिए उतना योगदान नहीं देता है):

# use ABCMeta compatible with Python 2 *and* 3 
class SomeAbstractClass(abc.ABCMeta('ABC', (object,), {'__slots__': ()})):

    @abc.abstractmethod
    def do_something(self):
        pass

ध्यान दें कि हस्ताक्षर six.with_metaclass से थोड़ा six.with_metaclass लेकिन यह अतिरिक्त निर्भरता के बिना, वही अर्थशास्त्र है।

या तो समाधान

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

>>> SomeAbstractClass()
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    SomeAbstractClass()
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract methods do_something

__slots__ = () पर नोट

हमने अभी पाइथन 3 की मानक लाइब्रेरी में एबीसी सुविधा वर्ग में खाली __slots__ जोड़ा है , और मेरा उत्तर इसे शामिल करने के लिए अपडेट किया गया है।

ABC माता-पिता में उपलब्ध __dict__ और __weakref__ नहीं होने से उपयोगकर्ताओं को बाल वर्गों के लिए अपनी रचना से इनकार करने और स्मृति को बचाने की अनुमति मिलती है - तब तक कोई डाउनसाइड्स नहीं है, जब तक कि आप पहले से ही बाल कक्षाओं में __slots__ का उपयोग नहीं कर रहे थे और ABC माता-पिता से __dict__ या __weakref__ निर्माण पर भरोसा करते थे।

फास्ट फ़िक्स आपके बच्चे वर्ग में उचित रूप से __dict__ या __weakref__ घोषित करना होगा। बेहतर ( __dict__ ) आपके सभी सदस्यों को स्पष्ट रूप से घोषित करना हो सकता है।

मैं एक कक्षा बनाना चाहता हूं जिसमें abc.ABCMeta एक abc.ABCMeta रूप में है और दोनों पायथन 2.7 और पायथन 3.5 के साथ संगत है। अब तक, मैं केवल 2.7 या 3.5 पर ऐसा करने में सफल रहा - लेकिन दोनों संस्करणों पर एक साथ कभी नहीं। क्या कोई मुझे हाथ दे सकता है?

पायथन 2.7:

import abc
class SomeAbstractClass(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def do_something(self):
        pass

पायथन 3.5:

import abc
class SomeAbstractClass(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def do_something(self):
        pass

परिक्षण

यदि हम पाइथन दुभाषिया (पायथन 2.7 -> उदाहरण 1, पायथन 3.5 -> उदाहरण 2) के उपयुक्त संस्करण का उपयोग करके निम्न परीक्षण चलाते हैं, तो यह दोनों परिदृश्यों में सफल होता है:

import unittest
class SomeAbstractClassTestCase(unittest.TestCase):
    def test_do_something_raises_exception(self):
        with self.assertRaises(TypeError) as error:
            processor = SomeAbstractClass()
        msg = str(error.exception)
        expected_msg = "Can't instantiate abstract class SomeAbstractClass with abstract methods do_something"
        self.assertEqual(msg, expected_msg)

मुसीबत

पायथन 3.5 का उपयोग करके परीक्षण चलाते समय, अपेक्षित व्यवहार नहीं होता है ( SomeAbstractClass को चालू करते समय उठाया नहीं SomeAbstractClass ):

======================================================================
FAIL: test_do_something_raises_exception (__main__.SomeAbstractClassTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/tati/sample_abc.py", line 22, in test_do_something_raises_exception
    processor = SomeAbstractClass()
AssertionError: TypeError not raised

----------------------------------------------------------------------

जबकि Python 2.7 का उपयोग कर परीक्षण चला रहा है एक SyntaxError :

 Python 2.7 incompatible
 Raises exception:
  File "/home/tati/sample_abc.py", line 24
    class SomeAbstractClass(metaclass=abc.ABCMeta):
                                     ^
 SyntaxError: invalid syntax

आप six.add_metaclass या six.with_metaclass उपयोग कर सकते हैं:

import abc, six

@six.add_metaclass(abc.ABCMeta)
class SomeAbstractClass():
    @abc.abstractmethod
    def do_something(self):
        pass

six पाइथन 2 और 3 संगतता पुस्तकालय है । आप इसे pip install six चलाने या अपनी परियोजना निर्देशिका में six.py . six.py के नवीनतम संस्करण को डाउनलोड करके स्थापित कर सकते हैं।

आप में से जो six से अधिक future पसंद करते future , प्रासंगिक कार्य future है future.utils.with_metaclass


मैं हारून हॉल का जवाब पसंद करता हूं, लेकिन यह ध्यान रखना महत्वपूर्ण है कि इस मामले में टिप्पणी जो पंक्ति का हिस्सा है:

ABC = abc.ABCMeta('ABC', (object,), {}) # compatible with Python 2 *and* 3 

... कोड जितना महत्वपूर्ण है उतना ही महत्वपूर्ण है। टिप्पणी के बिना, कुछ भविष्य के काउबॉय को लाइन को हटाने और वर्ग विरासत को बदलने के लिए कुछ भी नहीं है:

class SomeAbstractClass(abc.ABC):

... इस प्रकार सब कुछ पायथन 3.4 तोड़ना।

एक चिमटा जो किसी और के लिए थोड़ा अधिक स्पष्ट / स्पष्ट हो सकता है- इसमें स्वयं दस्तावेज है- इसके बारे में आप क्या हासिल करने की कोशिश कर रहे हैं:

import sys
import abc

if sys.version_info >= (3, 4):
    ABC = abc.ABC
else:
    ABC = abc.ABCMeta('ABC', (), {})

class SomeAbstractClass(ABC):
    @abc.abstractmethod
    def do_something(self):
        pass

कड़ाई से बोलना, यह करना जरूरी नहीं है, लेकिन यह बिल्कुल स्पष्ट है, बिना किसी टिप्पणी के, क्या चल रहा है।







abc