我有一个自动创建包装数据库表的Python类的方法,其类成员与表中的字段具有相同的名称。类文件如下所示:
class CpsaUpldBuildChrgResultSet(Recordset):
def __init__(self, connection):
super().__init__(connection)
self.DefaultTableName = 'cpsa_upld_build_chrg_result'
self._keyFields.append('j_trans_seq')
self._keyFields.append('j_index')
@property
def j_trans_seq(self):
return self.GetValue('j_trans_seq')
@j_trans_seq.setter
def j_trans_seq(self, value):
self.SetKeyValue('j_trans_seq', value)
@property
def j_index(self):
return self.GetValue('j_index')
@j_index.setter
def j_index(self, value):
self.SetKeyValue('j_index', value)
我刚刚发现,如果我尝试为不存在的类成员设置一个值,例如J_TRANS_SEQ,则不会抛出任何异常。我可以添加到此类中,以便尝试访问不存在的成员会引发异常吗?
答案 0 :(得分:2)
您可以向班级添加__setattr__
方法,只要分配了无效属性,该方法就会引发AttributeError
。我不确定您究竟想要确定哪些属性有效,哪些属性无效,但有一种方法可能是这样的:
def __setattr__(self, name, value):
if hasattr(self, name):
super().setattr(name, value)
else:
raise AttributeError("{} object has no attribute {!r}".format(type(self), name))
这假定可以查找的任何属性也可以分配给它。如果除了在getter之前调用setter之外,property
的getter不起作用,它可能会中断。它也可能过于宽松,因为它允许设置覆盖类属性的实例属性(例如__init__
)。另一种方法可能是根据已知属性的白名单检查名称(但一定要包括继承类机制所需的属性,如DefaultTableName
和_keyFields
)。
答案 1 :(得分:1)
我认为@Blckknght有正确的想法,但在他的答案中遗漏了一些重要的细节 - 这样就有类属性(班级成员)如何在他们不存在时首次设置,例如在类__init__()
方法执行时的典型场景。这是一个更完善的解决方案,适用于Python 3,解决了这个问题。
它还展示了如何最小化一堆重复属性的编码。
class Recordset(object):
def __init__(self, connection):
print('Recordset.__init__({!r}) called'.format(connection))
def SetKeyValue(self, name, value):
print('SetKeyValue({!r}, {!r}) called'.format(name, value))
def GetValue(self, name):
print('GetValue({!r}) called'.format(name))
def fieldname_property(name):
storage_name = '_' + name
@property
def prop(self):
return self.GetValue(storage_name)
@prop.setter
def prop(self, value):
self.SetKeyValue(storage_name, value)
return prop
class CpsaUpldBuildChrgResultSet(Recordset):
# define properties for valid fieldnames
j_trans_seq = fieldname_property('j_trans_seq')
j_index = fieldname_property('j_index')
def __init__(self, connection):
super().__init__(connection)
self._setter('DefaultTableName', 'cpsa_upld_build_chrg_result')
def __setattr__(self, name, value):
if hasattr(self, name):
self._setter(name, value)
else:
raise AttributeError("No field named %r" % name)
def _setter(self, name, value):
"""Provides way to intentionally bypass overloaded __setattr__."""
super().__setattr__(name, value)
print('start')
db_table = CpsaUpldBuildChrgResultSet('SomeConnection')
print('assigning attributes...')
db_table.j_trans_seq = 42 # OK
db_table.j_index = 13 # OK
db_table.J_TRANS_SEQ = 99 # -> AttributeError: No field named 'J_TRANS_SEQ'
print('done')