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():
        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

4 回答 4

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您的类,以便实际执行附加代码。

于 2012-05-25T06:52:40.210 回答
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
于 2012-05-25T07:48:41.983 回答
1

我会投票支持@ThiefMaster 的做事方式。如果你想让你的生活复杂化,你可以使用带有装饰器的元类

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()
于 2012-05-25T07:51:46.217 回答
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

我知道这不像使用装饰器那样迷人,但可以很容易地了解实际发生的情况。

于 2012-05-25T08:02:14.997 回答