2011-11-02 22 views
22

में बिल्टिन प्रकारों को उपclassing जब बिल्टिन प्रकार subclassing, मैंने अंतर्निहित प्रकार के तरीकों के वापसी प्रकार में पाइथन 2 और पायथन 3 के बीच एक महत्वपूर्ण अंतर देखा। निम्नलिखित कोड सेट के लिए इस दिखाता है:पाइथन 2 और पायथन 3

class MySet(set): 

    pass 

s1 = MySet([1, 2, 3, 4, 5]) 

s2 = MySet([1, 2, 3, 6, 7]) 

print(type(s1.union(s2))) 

print(type(s1.intersection(s2))) 

print(type(s1.difference(s2))) 

अजगर 2 के साथ, सभी वापसी मान प्रकार MySet के हैं। पायथन 3 के साथ, रिटर्न प्रकार set हैं। परिणाम के बारे में कोई दस्तावेज नहीं मिला, न ही पाइथन 3 में परिवर्तन के बारे में कोई दस्तावेज नहीं मिला।

वैसे भी, मुझे वास्तव में किस चीज की परवाह है यह है: क्या पाइथन 3 में एक आसान तरीका है पाइथन 2 में देखा गया व्यवहार, अंतर्निहित प्रकारों की हर विधि को फिर से परिभाषित किए बिना?

+0

अजगर 2 पर केवल 's1' के प्रकार s2' के प्रकार नहीं प्रासंगिक है आवश्यकता होगी '। – agf

+2

यह इस तरह का है कि 'झूठी + झूठी' '0' है, न कि 'गलत' (' बूल' 'int' का उप-वर्ग है)। –

उत्तर

11

पायथन 2.x से 3.x - list और int से चलते समय अंतर्निर्मित प्रकारों के लिए यह सामान्य परिवर्तन नहीं है, उदाहरण के लिए, 2.x और 3.x में समान व्यवहार है। this bug tracker issue में चर्चा के अनुसार, केवल सेट प्रकार को अन्य प्रकारों के साथ लाने के लिए बदला गया था।

मुझे डर है कि यह पुराने तरीके से व्यवहार करने का कोई अच्छा तरीका नहीं है। यहां कुछ कोड है जिसके साथ मैं आने में सक्षम था:

class MySet(set): 
    def copy(self): 
     return MySet(self) 
    def _make_binary_op(in_place_method): 
     def bin_op(self, other): 
      new = self.copy() 
      in_place_method(new, other) 
      return new 
     return bin_op 
    __rand__ = __and__ = _make_binary_op(set.__iand__) 
    intersection = _make_binary_op(set.intersection_update) 
    __ror__ = __or__ = _make_binary_op(set.__ior__) 
    union = _make_binary_op(set.update) 
    __sub__ = _make_binary_op(set.__isub__) 
    difference = _make_binary_op(set.difference_update) 
    __rxor__ = xor__ = _make_binary_op(set.__ixor__) 
    symmetric_difference = _make_binary_op(set.symmetric_difference_update) 
    del _make_binary_op 
    def __rsub__(self, other): 
     new = MySet(other) 
     new -= self 
     return new 

यह आपके सभी प्रकार के संस्करणों के साथ सभी विधियों को आसानी से ओवरराइट करेगा। (वहाँ बहुत सारी विधियां हैं!)

शायद आपके आवेदन के लिए, आप copy() ओवरराइटिंग से दूर हो सकते हैं और इन-प्लेस विधियों पर चिपके रह सकते हैं।

+0

राइट, पायथन 2 यहां संगत नहीं था। यदि आप पाइथन 2 में 'क्लास माईसेट (सेट): पास' बनाते हैं, तो' प्रिंट टाइप (माईसेट() कॉपी()) '' क्लास '__main __। माईसेट'> 'देता है, लेकिन यदि आप 'क्लास' बनाते हैं MyDict (dict): पास', फिर 'प्रिंट प्रकार (MyDict()। कॉपी()) '' देता है। – Cito

+0

एक ही ऑपरेशन में कम से कम गैर-विशेष तरीकों को संभालने का एक तरीका है। मैं अपने स्वयं के प्रश्न का उत्तर दूंगा कि कैसे (मैं एक टिप्पणी में कोड नहीं डाल सकता)। लेकिन यह अभी भी एक रास्ता है जो मैं चाहता हूं, सभी विशेष तरीकों के साथ एक-एक करके संभालने के लिए। – khinsen

0

शायद एक metaclass सब नीरस रैपिंग करने के लिए आप इसे आसान बनाना होगा के लिए:

class Perpetuate(type): 
    def __new__(metacls, cls_name, cls_bases, cls_dict): 
     if len(cls_bases) > 1: 
      raise TypeError("multiple bases not allowed") 
     result_class = type.__new__(metacls, cls_name, cls_bases, cls_dict) 
     base_class = cls_bases[0] 
     known_attr = set() 
     for attr in cls_dict.keys(): 
      known_attr.add(attr) 
     for attr in base_class.__dict__.keys(): 
      if attr in ('__new__'): 
       continue 
      code = getattr(base_class, attr) 
      if callable(code) and attr not in known_attr: 
       setattr(result_class, attr, metacls._wrap(base_class, code)) 
      elif attr not in known_attr: 
       setattr(result_class, attr, code) 
     return result_class 
    @staticmethod 
    def _wrap(base, code): 
     def wrapper(*args, **kwargs): 
      if args: 
       cls = args[0] 
      result = code(*args, **kwargs) 
      if type(result) == base: 
       return cls.__class__(result) 
      elif isinstance(result, (tuple, list, set)): 
       new_result = [] 
       for partial in result: 
        if type(partial) == base: 
         new_result.append(cls.__class__(partial)) 
        else: 
         new_result.append(partial) 
       result = result.__class__(new_result) 
      elif isinstance(result, dict): 
       for key in result: 
        value = result[key] 
        if type(value) == base: 
         result[key] = cls.__class__(value) 
      return result 
     wrapper.__name__ = code.__name__ 
     wrapper.__doc__ = code.__doc__ 
     return wrapper 

class MySet(set, metaclass=Perpetuate): 
    pass 

s1 = MySet([1, 2, 3, 4, 5]) 

s2 = MySet([1, 2, 3, 6, 7]) 

print(s1.union(s2)) 
print(type(s1.union(s2))) 

print(s1.intersection(s2)) 
print(type(s1.intersection(s2))) 

print(s1.difference(s2)) 
print(type(s1.difference(s2))) 
+0

कुछ टिप्पणियां: 1. यह 'e()' नामक विधि को लपेटने में असफल हो जाएगी, लेकिन यह गुणों में बेस टाइप की ऑब्जेक्ट्स को स्टोर करने से रोकने के लिए '__getattribute __()' लपेटता है। 2. यह विशेष रूप से विशेषताओं को पुनर्प्राप्त करने के लिए एक गंभीर प्रदर्शन हिट होगा।यदि आप किसी विशेषता में एक सूची संग्रहीत करते हैं, तो इसे हर एक्सेस पर फिर से चालू किया जाएगा। अधिक प्रदर्शन समस्याएं हैं, शायद इंगित करने के लिए बहुत सारे हैं। –

+0

@SvenMarnach: यह 'e()' को लपेटने में क्यों विफल रहेगा? –

+0

क्योंकि 'e' नाम के लिए, स्थिति 'attr (' __new __ ') में होगी। माना जाता है कि यह एक सस्ता है, लेकिन इस कोड में और अधिक अस्पष्ट बग हैं। –

0

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

चेतावनियां: से मैं अपने कोड में लेना पसंद

1) यह और अधिक जादू प्रवंचना है।

2) मैं अभी भी विशेष तरीकों रैप करने के लिए (__and__ आदि) मैन्युअल रूप से, क्योंकि उनके देखने नजरअंदाज __getattribute__

import types 

class MySet(set): 

    def __getattribute__(self, name): 
     attr = super(MySet, self).__getattribute__(name) 
     if isinstance(attr, types.BuiltinMethodType): 
      def wrapper(self, *args, **kwargs): 
       result = attr(self, *args, **kwargs) 
       if isinstance(result, set): 
        return MySet(result) 
       else: 
        return result 
      setattr(MySet, name, wrapper) 
      return wrapper 
     return attr 
संबंधित मुद्दे