我正在努力实现以下目标:
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
,但我不认为在基类中装饰的重写方法会继承“装饰”
答案 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做事的方式。如果您想让生活复杂化,可以将metaclasses与decorators:
一起使用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
我知道这并不像使用装饰器一样迷人,但却很容易理解实际发生的事情。