2009-04-28 5 views
8

मैं एक ऑब्जेक्ट को कार्यान्वित कर रहा हूं जो लगभग एक सेट के समान है, लेकिन एक अतिरिक्त आवृत्ति चर की आवश्यकता है, इसलिए मैं अंतर्निर्मित सेट ऑब्जेक्ट को उप-वर्गीकृत कर रहा हूं। यह सुनिश्चित करने का सबसे अच्छा तरीका क्या है कि जब मेरी ऑब्जेक्ट्स की प्रतिलिपि बनाई जाती है तो इस चर के मान की प्रतिलिपि बनाई जाती है?पाइथन सेट क्लास को उपclass करने के लिए सही (या सबसे अच्छा) तरीका क्या है, एक नया इंस्टेंस चर जोड़ना?

वर्ष सेट मॉड्यूल का उपयोग करना, निम्नलिखित कोड पूरी तरह से काम किया:

import sets 
class Fooset(sets.Set): 
    def __init__(self, s = []): 
     sets.Set.__init__(self, s) 
     if isinstance(s, Fooset): 
      self.foo = s.foo 
     else: 
      self.foo = 'default' 
f = Fooset([1,2,4]) 
f.foo = 'bar' 
assert((f | f).foo == 'bar') 

लेकिन इस का उपयोग कर काम नहीं करता है में निर्मित सेट मॉड्यूल।

एकमात्र समाधान जो मैं देख सकता हूं वह प्रतिलिपि सेट ऑब्जेक्ट लौटने वाली प्रत्येक विधि को ओवरराइड करना है ... इस मामले में मैं सेट ऑब्जेक्ट को उप-वर्गीकरण भी परेशान नहीं कर सकता। निश्चित रूप से ऐसा करने का एक मानक तरीका है?

(स्पष्ट करने के लिए निम्न कोड करता नहीं काम (अभिकथन विफल रहता है):

class Fooset(set): 
    def __init__(self, s = []): 
     set.__init__(self, s) 
     if isinstance(s, Fooset): 
      self.foo = s.foo 
     else: 
      self.foo = 'default' 

f = Fooset([1,2,4]) 
f.foo = 'bar' 
assert((f | f).foo == 'bar') 

)

उत्तर

14

मेरा पसंदीदा निर्मित एक संग्रह के तरीकों रैप करने के लिए जिस तरह से:

class Fooset(set): 
    def __init__(self, s=(), foo=None): 
     super(Fooset,self).__init__(s) 
     if foo is None and hasattr(s, 'foo'): 
      foo = s.foo 
     self.foo = foo 



    @classmethod 
    def _wrap_methods(cls, names): 
     def wrap_method_closure(name): 
      def inner(self, *args): 
       result = getattr(super(cls, self), name)(*args) 
       if isinstance(result, set) and not hasattr(result, 'foo'): 
        result = cls(result, foo=self.foo) 
       return result 
      inner.fn_name = name 
      setattr(cls, name, inner) 
     for name in names: 
      wrap_method_closure(name) 

Fooset._wrap_methods(['__ror__', 'difference_update', '__isub__', 
    'symmetric_difference', '__rsub__', '__and__', '__rand__', 'intersection', 
    'difference', '__iand__', 'union', '__ixor__', 
    'symmetric_difference_update', '__or__', 'copy', '__rxor__', 
    'intersection_update', '__xor__', '__ior__', '__sub__', 
]) 

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

+0

यह एक उपयोगी योगदान है, धन्यवाद। ऐसा लगता है कि आप _wrap_methods को किसी फ़ंक्शन की बजाय क्लास विधि बनाकर बहुत अधिक लाभ प्राप्त कर रहे हैं - क्या यह पूरी तरह से मॉड्यूलरिटी के लिए देता है? – rog

+0

यह वास्तव में अच्छा है, अंतर्दृष्टि योगदान के लिए धन्यवाद! – bjd2385

2

set1 | set2 एक ऑपरेशन में बदलाव नहीं करेगी या तो मौजूदा set, लेकिन एक वापसी इसके बजाय नया set। नया set बनाया गया है और लौटाया गया है। द्वारा | ऑपरेटर को अनुकूलित किए बिना, इसे set से नए बनाए गए set पर एक या दोनों से आर्बिटरी विशेषताओं को स्वचालित रूप से कॉपी करने का कोई तरीका नहीं है।

class MySet(set): 
    def __init__(self, *args, **kwds): 
     super(MySet, self).__init__(*args, **kwds) 
     self.foo = 'nothing' 
    def __or__(self, other): 
     result = super(MySet, self).__or__(other) 
     result.foo = self.foo + "|" + other.foo 
     return result 

r = MySet('abc') 
r.foo = 'bar' 
s = MySet('cde') 
s.foo = 'baz' 

t = r | s 

print r, s, t 
print r.foo, s.foo, t.foo 

प्रिंटों:

MySet(['a', 'c', 'b']) MySet(['c', 'e', 'd']) MySet(['a', 'c', 'b', 'e', 'd']) 
bar baz bar|baz 
+0

यह मैं क्या संदिग्ध है। इस मामले में, मुझे __and__, __or__, __rand__, __ror__, __rsub__, __rxor__, __sub__, __xor__, जोड़, प्रतिलिपि, अंतर, चौराहे, symmetric_difference, और संघ को ओवरराइड करना होगा। क्या मैंने किसी को याद किया है? ईमानदार होने के लिए, मैं ऊपर सूचीबद्ध 2.5 समाधान की साधारण सामान्यता के साथ कुछ ढूंढ रहा था ... लेकिन नकारात्मक जवाब भी अच्छा है। यह मेरे लिए एक बग की तरह थोड़ा प्रतीत होता है। – rog

-2

मुझे इस के लिए पूरी तरह से Win32 पर पायथन 2.5.2 का उपयोग कर काम करता है।

f = Fooset([1,2,4]) 
s = sets.Set((5,6,7)) 
print f, f.foo 
f.foo = 'bar' 
print f, f.foo 
g = f | s 
print g, g.foo 
assert((f | f).foo == 'bar') 

मैं इस उत्पादन है, जो है मैं क्या उम्मीद मिलती है::

Fooset([1, 2, 4]) default 
Fooset([1, 2, 4]) bar 
Fooset([1, 2, 4, 5, 6, 7]) bar 
+0

हाँ, यह 2.5.2 के साथ काम करता है, लेकिन क्या आप इसे अंतर्निहित सेट प्रकार के साथ python 2.6 में काम कर सकते हैं? – rog

+0

चूंकि आपने अपने कोड में 'आयात सेट' किया था, और 2.6 का उल्लेख नहीं किया था, मुझे लगता है कि आप set.py मॉड्यूल का उपयोग करेंगे। यदि यह अब 2.6 में उपलब्ध नहीं है तो आपकी 0 किलोग्राम – Ber

2

ऐसा लगता है कि सेट नजरअंदाज तरह c code में __init__ आप वर्ग परिभाषा और निम्न परीक्षण का उपयोग करना। हालांकि आप Fooset का एक उदाहरण समाप्त कर देंगे, इसे फ़ील्ड की प्रतिलिपि बनाने का मौका नहीं मिला होगा।

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

+0

* श्वास * की संभावना है। धन्यवाद। वह भी सी कोड का मेरा पठन था, लेकिन मैं एक अजगर नौसिखिया हूँ इसलिए सोचा कि यह पूछने लायक था। मैं सामान्य रूप से उपclassing नापसंद के कारणों को भूल गया था - "बाहरी" subclass अपने superclass के अप्रकाशित आंतरिक कार्यान्वयन विवरण पर निर्भर हो जाता है। – rog

0

अन्य उत्तरों को मानना ​​सही है, और सभी तरीकों को ओवरराइड करना ऐसा करने का एकमात्र तरीका है, यह मेरा प्रयास करने का मामूली सुरुचिपूर्ण तरीका है। यदि अधिक आवृत्ति चर जोड़े गए हैं, तो कोड के केवल एक टुकड़े को बदलने की जरूरत है। दुर्भाग्यवश यदि सेट ऑब्जेक्ट में कोई नया बाइनरी ऑपरेटर जोड़ा जाता है, तो यह कोड टूट जाएगा, लेकिन मुझे नहीं लगता कि इससे बचने का कोई तरीका है। टिप्पणियाँ स्वागत है!

def foocopy(f): 
    def cf(self, new): 
     r = f(self, new) 
     r.foo = self.foo 
     return r 
    return cf 

class Fooset(set): 
    def __init__(self, s = []): 
     set.__init__(self, s) 
     if isinstance(s, Fooset): 
      self.foo = s.foo 
     else: 
      self.foo = 'default' 

    def copy(self): 
     x = set.copy(self) 
     x.foo = self.foo 
     return x 

    @foocopy 
    def __and__(self, x): 
     return set.__and__(self, x) 

    @foocopy 
    def __or__(self, x): 
     return set.__or__(self, x) 

    @foocopy 
    def __rand__(self, x): 
     return set.__rand__(self, x) 

    @foocopy 
    def __ror__(self, x): 
     return set.__ror__(self, x) 

    @foocopy 
    def __rsub__(self, x): 
     return set.__rsub__(self, x) 

    @foocopy 
    def __rxor__(self, x): 
     return set.__rxor__(self, x) 

    @foocopy 
    def __sub__(self, x): 
     return set.__sub__(self, x) 

    @foocopy 
    def __xor__(self, x): 
     return set.__xor__(self, x) 

    @foocopy 
    def difference(self, x): 
     return set.difference(self, x) 

    @foocopy 
    def intersection(self, x): 
     return set.intersection(self, x) 

    @foocopy 
    def symmetric_difference(self, x): 
     return set.symmetric_difference(self, x) 

    @foocopy 
    def union(self, x): 
     return set.union(self, x) 


f = Fooset([1,2,4]) 
f.foo = 'bar' 
assert((f | f).foo == 'bar') 
+0

आपको कॉपी विधि में कुछ अनंत रिकर्सन मिला है। x = self.copy() x = super होना चाहिए (फूसेट, स्वयं) .copy() –

+0

हाँ, आप सही हैं। superclass स्पष्ट रूप से उल्लेख करने से बेहतर सुपर() का उपयोग कर रहा है? – rog

4

मुझे लगता है कि ऐसा करने का अनुशंसित तरीका सीधे निर्मित set से उपclass नहीं है, बल्कि Abstract Base Class Set का उपयोग collections में उपलब्ध कराने के लिए है।

का उपयोग करें ताकि आप केवल __contains__(), __len__() और __iter__() को परिभाषित करते हुए एक न्यूनतम सेट वर्ग हो सकता है एबीसी सेट आप एक मिश्रण के रूप में मुक्त करने के लिए कुछ तरीकों देता है। यदि आप intersection() और difference() जैसे कुछ अच्छे सेट विधियों को चाहते हैं, तो आपको शायद उन्हें लपेटना होगा।

यहाँ मेरी प्रयास है (यह एक एक frozenset की तरह होता है, लेकिन आप MutableSet से विरासत कर सकते हैं एक परिवर्तनशील संस्करण प्राप्त करने के):

from collections import Set, Hashable 

class CustomSet(Set, Hashable): 
    """An example of a custom frozenset-like object using 
    Abstract Base Classes. 
    """ 
    ___hash__ = Set._hash 

    wrapped_methods = ('difference', 
         'intersection', 
         'symetric_difference', 
         'union', 
         'copy') 

    def __repr__(self): 
     return "CustomSet({0})".format(list(self._set)) 

    def __new__(cls, iterable): 
     selfobj = super(CustomSet, cls).__new__(CustomSet) 
     selfobj._set = frozenset(iterable) 
     for method_name in cls.wrapped_methods: 
      setattr(selfobj, method_name, cls._wrap_method(method_name, selfobj)) 
     return selfobj 

    @classmethod 
    def _wrap_method(cls, method_name, obj): 
     def method(*args, **kwargs): 
      result = getattr(obj._set, method_name)(*args, **kwargs) 
      return CustomSet(result) 
     return method 

    def __getattr__(self, attr): 
     """Make sure that we get things like issuperset() that aren't provided 
     by the mix-in, but don't need to return a new set.""" 
     return getattr(self._set, attr) 

    def __contains__(self, item): 
     return item in self._set 

    def __len__(self): 
     return len(self._set) 

    def __iter__(self): 
     return iter(self._set) 
3

दुःख की बात है सेट नियमों और __new__ का पालन नहीं करता नहीं है नए set ऑब्जेक्ट्स बनाने के लिए कहा जाता है, भले ही वे प्रकार रखें। यह स्पष्ट रूप से पायथन में एक बग है (समस्या # 1721812, जो 2.x अनुक्रम में तय नहीं की जाएगी)। आपको 0 ऑब्जेक्ट्स बनाने वाले ऑब्जेक्ट को type ऑब्जेक्ट को कॉल किए बिना टाइप एक्स का ऑब्जेक्ट कभी भी प्राप्त नहीं करना चाहिए! यदि set.__or____new__ पर कॉल नहीं करेगा, तो यह औपचारिक रूप से उप-वर्ग वस्तुओं के बजाय set ऑब्जेक्ट्स लौटने के लिए बाध्य है।

लेकिन वास्तव में, nosklo द्वारा पोस्ट को नोट करते हुए, आपका मूल व्यवहार कोई समझ नहीं आता है। Set.__or__ ऑपरेटर को इसके परिणाम बनाने के लिए किसी भी स्रोत ऑब्जेक्ट का पुन: उपयोग नहीं करना चाहिए, इसे एक नया बनाना चाहिए, जिस स्थिति में foo"default" होना चाहिए!

तो, व्यावहारिक रूप से, यह करने वाले किसी भी व्यक्ति को को उन ऑपरेटरों को अधिभारित करना होगा ताकि वे जान सकें कि foo की कौन सी प्रति उपयोग की जाती है। यदि यह संयुक्त रूप से फूसेट्स पर निर्भर नहीं है, तो आप इसे कक्षा डिफ़ॉल्ट बना सकते हैं, इस मामले में इसे सम्मानित किया जाएगा, क्योंकि नई वस्तु सोचती है कि यह सबक्लास प्रकार का है।

क्या मेरा मतलब है, अपने उदाहरण काम करेगा, एक तरह से, अगर आप इस किया:

class Fooset(set): 
    foo = 'default' 
    def __init__(self, s = []): 
    if isinstance(s, Fooset): 
     self.foo = s.foo 

f = Fooset([1,2,5]) 
assert (f|f).foo == 'default' 
संबंधित मुद्दे

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