在超类中使用描述符来避免子类中的代码重复
Using descriptors in a superclass to avoid code duplication in subclasses
在 python 中,我有一个基础 class,我从中导出了一组子 class。每个 subclass 都有一些 subclass 特定的函数和一些独特的属性,这些属性具有使用 @property
装饰器定义的 getter 和 setter。但是如果我有很多属性,就会导致大量代码重复,因为 getter 和 setter 都具有相同的形式。这是一个粗略的示例草图,以数据库访问为例:
class BaseClass():
def __init__(self):
self.handle = None
def write(self, **kwargs):
self.handle.write(**kwargs)
class subClass_1(BaseClass):
def __init__(self):
self.handle = db_connector("table1")
@property
def property1a(self):
return self.handle.read('columnX')
@property1a.setter
def property1a(self, data):
self.write(columnX=data)
@property
def property1b(self):
return self.handle.read('columnY')
@property1b.setter
def property1b(self, data):
self.write(columnY=data)
def some_bespoke_function():
pass
class subClass_2(BaseClass):
def __init__(self):
self.handle = db_connector("table2")
@property
def property2a(self):
return self.handle.read('columnZ')
@property2a.setter
def property2a(self, data):
self.write(columnZ=data)
def another_bespoke_function():
pass
有没有什么方法可以在 BaseClass 中定义一些东西以避免必须编写所有那些 @property
装饰器?理想情况下,我只是在 subclass 中定义一些东西,比如 self.mapping = {'property1a':'columnX', 'property1a':'columnX'}
代表 SubClass_1 和 self.mapping = {'property2a':'columnZ'}
代表 SubClass_2.
回答我自己的问题:解决方案似乎是在基础 class 中使用 __getattr__
,仅当属性 时才调用(我没有意识到)不存在。所以这里的关键是(奇怪的是)不是来定义属性,而是捕获对这些不存在的"properties"的读写调用,并根据需要触发函数.完全不需要装饰器:
class BaseClass():
col_mapping = {}
def __init__(self):
self.handle = None
def __getattr__(self, name):
if name in col_mapping:
return self.handle.read(col_mapping[name])
raise AttributeError
def __setattr__(self, name, value):
if name in col_mapping:
self.handle.write(**{col_mapping[name]:value})
else:
object.__setattr__(self, name, value)
def write(self, **kwargs):
self.handle.write(**kwargs)
class subClass_1(BaseClass):
col_mapping = {
'property1a':'columnX',
'property1b':'columnY'
}
def __init__(self):
self.handle = db_connector("table1")
def some_bespoke_function():
pass
class subClass_2(BaseClass):
col_mapping = {
'property2a':'columnZ'
}
def __init__(self):
self.handle = db_connector("table2")
def another_bespoke_function():
pass
在 python 中,我有一个基础 class,我从中导出了一组子 class。每个 subclass 都有一些 subclass 特定的函数和一些独特的属性,这些属性具有使用 @property
装饰器定义的 getter 和 setter。但是如果我有很多属性,就会导致大量代码重复,因为 getter 和 setter 都具有相同的形式。这是一个粗略的示例草图,以数据库访问为例:
class BaseClass():
def __init__(self):
self.handle = None
def write(self, **kwargs):
self.handle.write(**kwargs)
class subClass_1(BaseClass):
def __init__(self):
self.handle = db_connector("table1")
@property
def property1a(self):
return self.handle.read('columnX')
@property1a.setter
def property1a(self, data):
self.write(columnX=data)
@property
def property1b(self):
return self.handle.read('columnY')
@property1b.setter
def property1b(self, data):
self.write(columnY=data)
def some_bespoke_function():
pass
class subClass_2(BaseClass):
def __init__(self):
self.handle = db_connector("table2")
@property
def property2a(self):
return self.handle.read('columnZ')
@property2a.setter
def property2a(self, data):
self.write(columnZ=data)
def another_bespoke_function():
pass
有没有什么方法可以在 BaseClass 中定义一些东西以避免必须编写所有那些 @property
装饰器?理想情况下,我只是在 subclass 中定义一些东西,比如 self.mapping = {'property1a':'columnX', 'property1a':'columnX'}
代表 SubClass_1 和 self.mapping = {'property2a':'columnZ'}
代表 SubClass_2.
回答我自己的问题:解决方案似乎是在基础 class 中使用 __getattr__
,仅当属性 时才调用(我没有意识到)不存在。所以这里的关键是(奇怪的是)不是来定义属性,而是捕获对这些不存在的"properties"的读写调用,并根据需要触发函数.完全不需要装饰器:
class BaseClass():
col_mapping = {}
def __init__(self):
self.handle = None
def __getattr__(self, name):
if name in col_mapping:
return self.handle.read(col_mapping[name])
raise AttributeError
def __setattr__(self, name, value):
if name in col_mapping:
self.handle.write(**{col_mapping[name]:value})
else:
object.__setattr__(self, name, value)
def write(self, **kwargs):
self.handle.write(**kwargs)
class subClass_1(BaseClass):
col_mapping = {
'property1a':'columnX',
'property1b':'columnY'
}
def __init__(self):
self.handle = db_connector("table1")
def some_bespoke_function():
pass
class subClass_2(BaseClass):
col_mapping = {
'property2a':'columnZ'
}
def __init__(self):
self.handle = db_connector("table2")
def another_bespoke_function():
pass