如果只有子类重写方法,则执行类属性的copy()

时间:2012-05-25 06:29:23

标签: python inheritance decorator

我正在努力实现以下目标:

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

当子类执行覆盖 preprocess_data 方法时,我想自动执行以下操作: self.data = self.data.copy()

如何做到这一点(如果有的话)?我考虑过装饰 preprocess ,但我不认为在基类中装饰的重写方法会继承“装饰”

4 个答案:

答案 0 :(得分:2)

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后面的函数是Data.preprocess_data;如果没有,它会复制您的数据。当然,这需要您在班级中调用_preprocess_data,以便实际执行附加代码。

答案 1 :(得分:1)

这可以通过装饰器和简单的元类来解决。这有点hacky,但完全符合你的要求。

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

答案 2 :(得分:1)

我会投票给@ ThiefMaster做事的方式。如果您想让生活复杂化,可以将metaclassesdecorators

一起使用
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 这是我第一次需要使用元类,但这是完美的场景。您需要在创建类之前覆盖函数定义(在__init__之前)。现在,您可能不需要它,但更一般地说,您可能被迫使用它。

一个更简单的例子:

if not (self.preprocess_data is Data.preprocess_data):
    self.data = self.data.copy()
self.preprocess_data()

答案 3 :(得分:0)

简单地说:

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

我知道这并不像使用装饰器一样迷人,但却很容易理解实际发生的事情。