अजगर में जेनरिक / टेम्प्लेट?


86

अजगर जेनेरिक / टेम्प्लेट प्रकार परिदृश्यों को कैसे संभालता है? मान लें कि मैं एक बाहरी फ़ाइल "बाइनरीट्रीएड्रो" बनाना चाहता हूं और क्या यह बाइनरी पेड़ों को संभालती है, लेकिन किसी भी डेटा प्रकार के लिए।

इसलिए मैं इसे एक कस्टम ऑब्जेक्ट के प्रकार को पास कर सकता हूं और उस ऑब्जेक्ट का एक बाइनरी ट्री हो सकता है। यह अजगर में कैसे किया जाता है?


16
अजगर में बतख के खाके हैं
डेविड हेफर्नन

जवाबों:


84

पायथन डक टाइपिंग का उपयोग करता है , इसलिए इसे कई प्रकारों को संभालने के लिए विशेष सिंटैक्स की आवश्यकता नहीं होती है।

यदि आप C ++ बैकग्राउंड से हैं, तो आपको याद होगा कि, जब तक टेम्प्लेट फ़ंक्शन / क्लास में उपयोग किए जाने वाले ऑपरेशन किसी प्रकार T(सिंटैक्स स्तर पर) में परिभाषित किए जाते हैं , तब तक आप Tटेम्प्लेट में उस प्रकार का उपयोग कर सकते हैं ।

तो, मूल रूप से, यह उसी तरह काम करता है:

  1. बाइनरी ट्री में जिस प्रकार की वस्तुओं को आप सम्मिलित करना चाहते हैं, उनके लिए एक अनुबंध निर्धारित करें।
  2. इस अनुबंध को दस्तावेजित करें (अर्थात कक्षा प्रलेखन में)
  3. अनुबंध में निर्दिष्ट केवल संचालन का उपयोग करके बाइनरी ट्री को लागू करें
  4. का आनंद लें

हालाँकि, आप ध्यान देंगे कि जब तक आप स्पष्ट प्रकार की जाँच नहीं करते हैं (जो आमतौर पर हतोत्साहित किया जाता है), आप यह नहीं सोच पाएंगे कि बाइनरी ट्री में केवल चुने हुए प्रकार के तत्व होते हैं।


6
आंद्रे, मैं यह समझना चाहता हूं कि पाइथन में स्पष्ट प्रकार की जाँच सामान्य रूप से क्यों हतोत्साहित की जाती है। मैं उलझन में हूं क्योंकि यह एक गतिशील रूप से टाइप की गई भाषा के साथ प्रतीत होता है, अगर हम फ़ंक्शन में आने वाले संभावित प्रकारों की गारंटी नहीं दे सकते तो हम परेशानी में पड़ सकते हैं। लेकिन, फिर, मैं पायथन के लिए बहुत नया हूं। :-)
स्कॉटएडवर्ड्स २२

3
@ ScottEdwards2000 आप PEP 484 में टाइप संकेत के साथ निहित प्रकार की जाँच कर सकते हैं और एक प्रकार का चेकर
no 12z16

7
अजगर शुद्धतावादी के नजरिए में, अजगर एक गतिशील भाषा और बतख टाइपिंग है प्रतिमान; यानी, टाइप-सेफ्टी को 'नॉन-पायथोनिक' कहा जाता है। यह कुछ ऐसा है जो मेरे लिए स्वीकार्य होना मुश्किल था - थोड़ी देर के लिए - जैसा कि मैं सी # में भारी निहित हूं। एक ओर, मुझे टाइप-सेफ्टी एक आवश्यकता लगती है। जैसा कि मैंने .Net दुनिया और पाइथोनिक प्रतिमान के बीच तराजू को संतुलित किया है, मैंने स्वीकार किया है कि टाइप-सेफ्टी वास्तव में शांत है और अगर मुझे ज़रूरत है, तो मुझे बस इतना करना है या ... बहुत आसान है। if isintance(o, t):if not isinstance(o, t):
आइब्रीस्ट

2
धन्यवाद टिप्पणीकारों, महान जवाब। मैंने उन्हें पढ़ने के बाद महसूस किया कि मैं वास्तव में सिर्फ अपनी त्रुटियों को पकड़ने के लिए जाँच करना चाहता हूं। तो मैं सिर्फ अंतर्निहित प्रकार की जाँच का उपयोग करूंगा।
स्कॉटएडवर्ड्स २२

3
मुझे लगता है कि कई पायथनवादियों को इस बारे में बात याद आती है - जेनेरिक एक ही समय में स्वतंत्रता और सुरक्षा प्रदान करने का एक तरीका है। यहां तक ​​कि जेनरिक को छोड़कर और केवल टाइप किए गए मापदंडों का उपयोग करते हुए, फ़ंक्शन लेखक को पता है कि वे कक्षा प्रदान करने वाले किसी भी तरीके का उपयोग करने के लिए अपने कोड को संशोधित कर सकते हैं; बत्तख टाइपिंग के साथ यदि आप एक ऐसी विधि का उपयोग करना शुरू करते हैं जिसका आपने पहले उपयोग नहीं किया था, तो आपने अचानक बत्तख की परिभाषा बदल दी, और चीजें शायद टूट जाएंगी।
केन विलियम्स

60

अन्य उत्तर पूरी तरह से ठीक हैं:

  • पाइथन में जेनरिक का समर्थन करने के लिए एक विशेष सिंटैक्स की आवश्यकता नहीं होती है
  • एंड्रे द्वारा बताया गया कि अजगर टाइपिंग का उपयोग करता है ।

हालाँकि, यदि आप अभी भी एक टाइप किया हुआ वैरिएंट चाहते हैं, तो पायथन 3.5 के बाद से बिल्ट-इन समाधान है।

सामान्य वर्ग :

from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        # Create an empty list with items of type T
        self.items: List[T] = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()

    def empty(self) -> bool:
        return not self.items
# Construct an empty Stack[int] instance
stack = Stack[int]()
stack.push(2)
stack.pop()
stack.push('x')        # Type error

सामान्य कार्य:

from typing import TypeVar, Sequence

T = TypeVar('T')      # Declare type variable

def first(seq: Sequence[T]) -> T:
    return seq[0]

def last(seq: Sequence[T]) -> T:
    return seq[-1]


n = first([1, 2, 3])  # n has type int.

संदर्भ: जेनरिक के बारे में मैपी दस्तावेज ।


1
निश्चित रूप से यहां सबसे अच्छा जवाब
डेनिस इट्राकोविच

Docs.python.org/3.5/library/typing.html से महत्वपूर्ण नोट है "जेनेरिक द्वारा उपयोग की जाने वाली मेटाक्लस abc.ABCMeta का एक उपवर्ग है।"
जोनाथन कोमर

20

दरअसल अब आप पायथन 3.5+ में जेनरिक का उपयोग कर सकते हैं। देखें पीईपी-484 और टाइपिंग मॉड्यूल प्रलेखन

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


12
यह जेनेरिक tbh के सस्ते चीर-फाड़ जैसा दिखता है। यह ऐसा है जैसे किसी को जेनरिक मिला है, उन्हें एक ब्लेंडर में डाल दिया है, इसे चलने दें और इसके बारे में भूल जाएं जब तक कि ब्लेंडर मोटर बाहर जला न जाए, और फिर इसे 2 दिन बाद निकाल लिया और कहा: "अरे हमें जेनरिक मिल गया"।
हर कोई

3
वे "टाइप संकेत" हैं, उनका जेनेरिक से कोई लेना-देना नहीं है।
ऊन.in.silver

टाइपस्क्रिप्ट में भी लेकिन यह जावा (सिंटैक्टिकली) की तरह काम करता है। इन भाषाओं में जेनरिक केवल संकेत हैं
डेविड

11

अजगर में जेनेरिक प्रकार बनाने पर कुछ अच्छे विचारों के साथ आने के बाद, मैंने उन अन्य लोगों की तलाश शुरू की जिनके पास एक ही विचार था, लेकिन मुझे कोई नहीं मिला। तो, यहाँ यह है। मैंने इसे आज़माया और यह अच्छी तरह से काम करता है। यह हमें अजगर में हमारे प्रकारों को मानकीकृत करने की अनुमति देता है।

class List( type ):

    def __new__(type_ref, member_type):

        class List(list):

            def append(self, member):
                if not isinstance(member, member_type):
                    raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                        type(member).__name__,
                        type(self).__name__,
                        member_type.__name__ 
                    ))

                    list.append(self, member)

        return List 

अब आप इस सामान्य प्रकार से प्रकार प्राप्त कर सकते हैं।

class TestMember:
        pass

class TestList(List(TestMember)):

    def __init__(self):
        super().__init__()


test_list = TestList()
test_list.append(TestMember())
test_list.append('test') # This line will raise an exception

यह समाधान सरल है, और इसकी सीमाएँ हैं। हर बार जब आप एक सामान्य प्रकार बनाते हैं, तो यह एक नया प्रकार बनाएगा। इस प्रकार, List( str )माता-पिता के रूप में विरासत में मिली कई कक्षाएं दो अलग-अलग वर्गों से विरासत में मिली होंगी। इसे दूर करने के लिए, आपको एक नया वर्ग बनाने के बजाय, आंतरिक वर्ग के विभिन्न रूपों को संग्रहीत करने और पिछले बनाए गए आंतरिक वर्ग को वापस करने के लिए एक तानाशाही बनाने की आवश्यकता है। यह एक ही पैरामीटर से डुप्लिकेट प्रकारों को बनने से रोकेगा। यदि दिलचस्पी है, तो सज्जाकारों और / या मेटाक्लासेस के साथ एक अधिक सुरुचिपूर्ण समाधान बनाया जा सकता है।


क्या आप इस बात पर विस्तार से बता सकते हैं कि उपरोक्त उदाहरण में किस तरह से हुकुम का उपयोग किया जा सकता है? क्या आपके पास इसके लिए स्निपेट है या तो गिट में या कुछ और में? धन्यवाद ..
सूक्ति

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

प्रेरणा के लिए धन्यवाद - मेटाक्लस के साथ इस तकनीक के विस्तार के लिए मेरा जवाब देखें
एरिक

4

क्योंकि पायथन गतिशील रूप से टाइप किया गया है, वस्तुओं के प्रकार कई मामलों में मायने नहीं रखते हैं। किसी भी चीज को स्वीकार करना बेहतर विचार है।

प्रदर्शित करने के लिए कि मेरा क्या मतलब है, यह वृक्ष वर्ग अपनी दो शाखाओं के लिए कुछ भी स्वीकार करेगा:

class BinaryTree:
    def __init__(self, left, right):
        self.left, self.right = left, right

और यह इस तरह इस्तेमाल किया जा सकता है:

branch1 = BinaryTree(1,2)
myitem = MyClass()
branch2 = BinaryTree(myitem, None)
tree = BinaryTree(branch1, branch2)

6
वस्तुओं के प्रकार के कर बात। यदि आप कंटेनर के आइटम पर लूप कर रहे हैं और fooप्रत्येक ऑब्जेक्ट पर एक विधि कह रहे हैं , तो कंटेनर में तार डालना एक बुरा विचार है। किसी भी चीज को स्वीकार करना बेहतर विचार नहीं है । हालांकि, यह है सुविधाजनक की आवश्यकता नहीं करने के लिए कक्षा से कंटेनर निकाले जाते हैं में सभी वस्तुओं है कि HasAFooMethod
एंड्रे कैरन

1
वास्तव में, प्रकार मायने रखता है: इसे ऑर्डर करना होगा।
फ्रेड फू

ओह ठीक। मैंने तब गलत समझा।
एंड्रिया

3

चूंकि अजगर गतिशील रूप से टाइप किया जाता है, यह सुपर आसान है। वास्तव में, आपको अपने बाइनरीट्री क्लास के लिए किसी भी डेटा प्रकार के साथ काम न करने के लिए अतिरिक्त काम करना होगा।

उदाहरण के लिए, यदि आप उन मुख्य मूल्यों को चाहते हैं जो ऑब्जेक्ट में उपलब्ध ऑब्जेक्ट को उस ऑब्जेक्ट के भीतर ट्री में रखने के लिए उपयोग किए जाते हैं जैसे key()आप key()ऑब्जेक्ट पर कॉल करते हैं। उदाहरण के लिए:

class BinaryTree(object):

    def insert(self, object_to_insert):
        key = object_to_insert.key()

ध्यान दें कि आपको कभी भी यह परिभाषित करने की आवश्यकता नहीं है कि किस प्रकार का वर्ग object_to_insert है। इसलिए जब तक इसका एक key()तरीका है, यह काम करेगा।

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


7
इसके विपरीत: सभी डेटा प्रकार पायथन में ऑब्जेक्ट हैं। उन्हें लिपटे जाने की आवश्यकता नहीं है (जैसे कि Integerमुक्केबाजी / अनबॉक्सिंग के साथ जावा में )।
जॉर्ज हिलियार्ड

2

यहाँ इस उत्तर का एक प्रकार है जो गन्दा सिंटैक्स से बचने के लिए मेटाक्लस का उपयोग करता है, और typing-स्टाइल List[int]सिंटैक्स का उपयोग करता है :

class template(type):
    def __new__(metacls, f):
        cls = type.__new__(metacls, f.__name__, (), {
            '_f': f,
            '__qualname__': f.__qualname__,
            '__module__': f.__module__,
            '__doc__': f.__doc__
        })
        cls.__instances = {}
        return cls

    def __init__(cls, f):  # only needed in 3.5 and below
        pass

    def __getitem__(cls, item):
        if not isinstance(item, tuple):
            item = (item,)
        try:
            return cls.__instances[item]
        except KeyError:
            cls.__instances[item] = c = cls._f(*item)
            item_repr = '[' + ', '.join(repr(i) for i in item) + ']'
            c.__name__ = cls.__name__ + item_repr
            c.__qualname__ = cls.__qualname__ + item_repr
            c.__template__ = cls
            return c

    def __subclasscheck__(cls, subclass):
        for c in subclass.mro():
            if getattr(c, '__template__', None) == cls:
                return True
        return False

    def __instancecheck__(cls, instance):
        return cls.__subclasscheck__(type(instance))

    def __repr__(cls):
        import inspect
        return '<template {!r}>'.format('{}.{}[{}]'.format(
            cls.__module__, cls.__qualname__, str(inspect.signature(cls._f))[1:-1]
        ))

इस नई मेटाक्लस के साथ, हम उस उदाहरण में फिर से लिख सकते हैं जो मैं इस लिंक से करता हूं:

@template
def List(member_type):
    class List(list):
        def append(self, member):
            if not isinstance(member, member_type):
                raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                    type(member).__name__,
                    type(self).__name__,
                    member_type.__name__ 
                ))

                list.append(self, member)
    return List

l = List[int]()
l.append(1)  # ok
l.append("one")  # error

इस दृष्टिकोण के कुछ अच्छे लाभ हैं

print(List)  # <template '__main__.List[member_type]'>
print(List[int])  # <class '__main__.List[<class 'int'>, 10]'>
assert List[int] is List[int]
assert issubclass(List[int], List)  # True

1

देखो कि अंतर्निर्मित कंटेनर इसे कैसे करते हैं। dictऔर listइसी तरह आप जिन भी प्रकार के विषम तत्वों को पसंद करते हैं। यदि आप परिभाषित करते हैं, कहते हैं, insert(val)अपने पेड़ के लिए एक समारोह, यह कुछ बिंदु पर ऐसा कुछ node.value = valकरेगा और अजगर बाकी की देखभाल करेगा।


1

सौभाग्य से अजगर में जेनेरिक प्रोग्रामिंग के लिए कुछ प्रयास किए गए हैं। एक पुस्तकालय है: सामान्य

यहाँ इसके लिए प्रलेखन है: http://generic.readthedocs.org/en/latest/

यह वर्षों से प्रगति नहीं कर रहा है, लेकिन आप अपने स्वयं के पुस्तकालय का उपयोग करने और बनाने के लिए एक मोटा विचार कर सकते हैं।

चियर्स


1

यदि आप पायथन 2 का उपयोग कर रहे हैं या जावा कोड को फिर से लिखना चाहते हैं। उनका वास्तविक समाधान इसके लिए नहीं है। यहाँ मैं एक रात में काम कर रहा हूं: https://github.com/FlorianSteenbuck/python-generics मुझे अभी भी कोई कंपाइलर नहीं मिला है इसलिए आप वर्तमान में इसका उपयोग कर रहे हैं:

class A(GenericObject):
    def __init__(self, *args, **kwargs):
        GenericObject.__init__(self, [
            ['b',extends,int],
            ['a',extends,str],
            [0,extends,bool],
            ['T',extends,float]
        ], *args, **kwargs)

    def _init(self, c, a, b):
        print "success c="+str(c)+" a="+str(a)+" b="+str(b)

सब

  • संकलक
  • सामान्य कक्षाएं और प्रकार काम करना (जैसी चीजों के लिए <? extends List<Number>>) प्राप्त करें
  • superसमर्थन जोड़ें
  • ?समर्थन जोड़ें
  • कोड क्लीन अप
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.