क्लास एट्रिब्यूट की प्रतिलिपि () करें यदि केवल एक उप-वर्ग एक विधि को ओवरराइड करता है

मैं निम्नलिखित प्राप्त करने की कोशिश कर रहा हूं:

class Data(object):
    def __init__(self, data):
        self.data = data
        self.valid = False
        #Analyze and validate data
        self.preprocess_data()
        self.validate_data()

    def preprocess_data():
        pass

    def validate_data():
        #process data

class MyData(Data):
    def __init__():
        super(MyData,self).__init__(data)

    def preprocess_data(self):
        #preprocess it

When a subclass executes the overriden preprocess_data method, I want to automatically perform the following operation: self.data = self.data.copy()

यह कैसे किया जा सकता है (अगर बिल्कुल)? मैंने preprocess को सजाने के बारे में सोचा था, लेकिन मुझे नहीं लगता कि बेस क्लास में सजाए गए ओवरराइड विधियों को "सजावट"

2
जोड़ा संपादित
विचारों: 1

4 उत्तर

class Data(object):
    def __init__(self, data):
        self.data = data
        self.valid = False
        #Analyze and validate data
        self._preprocess_data()
        self.validate_data()
    def _preprocess_data(self):
        if self.preprocess_data.im_func != Data.preprocess_data.im_func:
            self.data = self.data.copy()
        return self.preprocess_data()

यह परीक्षण अगर self.preprocess_data </​​code> के पीछे फ़ंक्शन Data.preprocess_data </​​code> है; यदि नहीं, तो यह आपके डेटा की प्रतिलिपि बनाता है। बेशक इसके लिए आपको अपनी कक्षा में _preprocess_data </​​code> को कॉल करने की आवश्यकता है ताकि अतिरिक्त कोड वास्तव में निष्पादित किया जा सके।

2
जोड़ा
संक्षिप्त और काम पूरा हो जाता है। धन्यवाद @ थिफमास्टर।
जोड़ा लेखक sebastian, स्रोत

मैं @ चीफमास्टर के काम करने के तरीके के लिए वोट दूंगा। यदि आप अपने जीवन को जटिल बनाना चाहते हैं, तो आप मेटाक्लास का उपयोग कर सकते हैं। संगठन/पुस्तकालय/functools.html "rel =" nofollow noreferrer "> सजावटी :

from functools import wraps

class PreprocessMetaclass(type): 
    def __new__(cls, name, bases, dct):

        try:
            if Data in bases and "preprocess_data" in dct:
                f = dct["preprocess_data"]
                @wraps(f)
                def preprocess_data(self, *args, **kwargs):
                    self.data = self.data.copy()
                    return f(self, *args, **kwargs)

                attrs = dct.copy()
                attrs["preprocess_data"] = preprocess_data
        except NameError as e:
            # This is if Data itself is being created, just leave it as it is.
            # Raises an NameError, because Data does not exist yet in "if Data in bases"
            attrs = dct

        return super(PreprocessMetaclass, cls).__new__(cls, name, bases, attrs)


class Data(object):
    # This here works if the subclasses don't specify
    # their own metaclass that isn't a subclass of the above.
    __metaclass__ = PreprocessMetaclass

    def __init__(self, data):
        self.data = data
        self.valid = False
        #Analyze and validate data
        self.preprocess_data()
        self.validate_data()

    def preprocess_data(self):
        self.data["newkey"] = 3

    def validate_data(self):
        print "This is the result: self.data =", self.data

class MyData(Data):
    def __init__(self, data):
        super(MyData,self).__init__(data)

    def preprocess_data(self):
        """The docs of the subclass"""
        self.data["newkey"] = 4

if __name__ == "__main__":
    dct1, dct2 = {"data": 1}, {"mydata": 2}

    print "Create Data"
    d = Data(dct1)

    print "Create MyData"
    md = MyData(dct2)

    print "The original dict of Data (changed):", dct1
    print "The original dict of MyData (unchanged):", dct2

    # If you do that, you will see that the docstring is still there,
    # but the arguments are no longer the same.
    # If that is needed (you don't have arguments, but the subclass might have)
    # then just enter the same arguments as the subclass's function
    # help(MyData)

PS It's the first time I ever needed to use metaclasses, but here it's the perfect scenario. You need to override a function definition before the class is created (before __init__). Now, you might not need it, but more generally, you might be forced to use that.

एक और अधिक सरल उदाहरण:

if not (self.preprocess_data is Data.preprocess_data):
    self.data = self.data.copy()
self.preprocess_data()
1
जोड़ा
jadkik94, metaclass + सजावटी दृष्टिकोण के लिए धन्यवाद। अभी के लिए, मैं सादगी का चयन करने और @ ThiefMaster के समाधान को अपनाने वाला हूं। यद्यपि अन्य विकल्पों को जानना हमेशा अच्छा होता है। धन्यवाद!!! इसके अलावा, मुझे आपका अंतिम सुझाव पसंद है।
जोड़ा लेखक sebastian, स्रोत
आपका स्वागत है, मुझे आशा है कि आप इसका कुछ समय पर इसका उपयोग करेंगे: व्यक्तिगत रूप से, यह पहली बार है जब मैंने मेटाक्लास को उपयोगी पाया। यदि आप इसे और अधिक जटिल हो जाते हैं तो आप इसका उपयोग करने पर विचार कर सकते हैं।
जोड़ा लेखक jadkik94, स्रोत

इसे सजावटी और सरल मेटाक्लास के साथ हल किया जा सकता है। यह थोड़ा हैकी है, लेकिन वही करता है जो आपने पूछा था।

import functools

class DataMeta(type):
    def __new__(cls, name, bases, dictn):
        fn = dictn.get('preprocess_data')
        if fn:
            if getattr(fn, '_original', False) is False:
                @functools.wraps(fn)
                def wrapper(self, *args, **kwargs):
                    self.data = self.data.copy()
                    return fn(self, *args, **kwargs)
                dictn['preprocess_data'] = wrapper
        return type.__new__(cls, name, bases, dictn)

def base_method(fn):
    fn._original = True
    return fn

class Data(object):
    __metaclass__ = DataMeta

    def __init__(self, data):
        self.data = data
        self.valid = False
        #Analyze and validate data
        self.preprocess_data()
        self.validate_data()

    @base_method
    def preprocess_data(self):
        print "Original preprocess_data called"

    def validate_data(self):
        pass

class MyData(Data):
    def __init__(self, data):
        super(MyData, self).__init__(data)

    def preprocess_data(self):
        print "Overridden preprocess_data called"

class MyData1(Data):
    def __init__(self, data):
        super(MyData1, self).__init__(data)

class Dummy(object):
    def copy(self):
        print 'Copying data'

md = MyData(Dummy()) # Prints 'Copying data'
md1 = MyData1(Dummy()) # Doesn't print it, since MyData1 doesn't override preprocess_data
1
जोड़ा
जेथू, आपके समाधान के लिए धन्यवाद। मैं इसकी सराहना करता हूं।
जोड़ा लेखक sebastian, स्रोत

बस कैसे के बारे में:

class Data(object):

    # Part of user API
    def preprocess_data():
        self.data = self.data.copy()
        self.preprocess_data_impl()

    # Part of internal API    
    def preprocess_data_impl():
        pass

class MyData(Data):

    def preprocess_data_impl():
        # Do stuff

मुझे पता है कि यह एक सजावटी का उपयोग करने के रूप में ग्लैमरस नहीं है, लेकिन वास्तव में क्या होता है इसका पालन करना बहुत आसान बनाता है।

0
जोड़ा
हाय @aix, यदि आप बेस क्लास डेटा में अपना __ init__ देखते हैं, तो मैं सत्यापन प्रक्रिया के लिए एक टेम्पलेट लागू कर रहा हूं (पहले प्रीप्रोसेस, फिर मान्य करें।) मेरा इरादा यह है कि सभी सबक्लास इस कोड को कॉल करते हैं । यदि subclasses preprocess_data </​​code> को लागू करते हैं, तो मैं self.data.copy() करना चाहता हूं। अन्यथा मैं इसे कॉपी करना चाहता हूं नहीं
जोड़ा लेखक sebastian, स्रोत