2017-02-03 34 views
1

我有一个自动创建包装数据库表的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)) 

这假定可以查找任何属性也有效分配给。如果你的吸气器不工作,它可能会中断,除非在吸气剂之前调用吸气器。它也可能过于宽松,因为它允许设置覆盖类属性的实例属性(例如__init__)。另一种方法可能是根据已知属性的白名单检查名称(但请确保包括继承类设备所需的属性,如DefaultTableName_keyFields)。

+0

非常感谢!我会尝试这种方法。 –

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') 
+0

非常感谢您花时间向我展示这些! –

+0

我不确定我可以使用这个答案,现在我正在更仔细地寻找。在我原来的文章中,请注意j_trans_seq的setter在类的基类中调用一个方法。在上面的解决方案中,setter调用__setattr __()。我如何调用GetKeyValue()? –

+0

您应该可以在属性设置器中调用'self.SetKeyValue()'。我的答案的主要观点只是为了说明如何绕过重载的'__setattr __()'。如果没有这样的做法,最初创建属性是不可能的 - 通常需要完成的事情,例如,在创建类实例时,类的'__init __()'方法......以及@ Blckknght回答。 – martineau