如何为不存在的类成员引发异常?

时间:2017-02-03 16:41:54

标签: python class

我有一个自动创建包装数据库表的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,则不会抛出任何异常。我可以添加到此类中,以便尝试访问不存在的成员会引发异常吗?

2 个答案:

答案 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')