在Python中设置变量时正确抛出错误的方法

43 投票
3 回答
89657 浏览
提问于 2025-04-15 20:54

在一个类里,检查错误的正确方法是什么呢?是抛出异常吗?还是设置一个叫“errors”的实例变量字典,把所有错误都放里面,然后返回这个字典呢?

从一个类里打印错误信息是不是不太好?如果我抛出异常,是否一定要返回False呢?

我只是想确认一下自己做的是否正确。下面是一些示例代码:

@property
def password(self):
    return self._password

@password.setter
def password(self,password):
    # Check that password has been completed
    try:
        # Check that password has a length of 6 characters
        if (len(password) < 6):
            raise NameError('Your password must be greater \
                             than 6 characters')

    except NameError:
        print 'Please choose a password'
        return False

    except TypeError:
        print 'Please choose a password'
        return False                                                                                                                                

    #Set the password
    self._password = password

    #Encrypt the password
    password_md5 = md5.new()
    password_md5.update(password)
    self._password_md5 = password_md5.hexdigest()

3 个回答

4

一般来说,当出现错误时,你应该用异常来表示这些错误。如果你通过检查发现了一个错误,并且可以立即处理它,那就没必要抛出异常。

以设置器为例,返回 False 或其他任何东西都没有帮助。检查实例变量的设置是非常不理想的,因为这样你可能会不小心漏掉一个错误。

print 来应对错误通常不是个好主意。在这种情况下,听起来你想告诉最终用户他们需要使用不同的密码。你应该调用一个方法,让网页上的表单向用户解释发生了什么错误;你可以在你的类中调用这个方法,或者抛出一个异常,让它传播出去,最终被捕获并用于这个目的。(这是一般性的建议。我对 Pylons 了解不够,无法告诉你它希望你怎么做。)

你不应该自己抛出 NameError 异常。NameError 通常表示你的程序中有拼写错误,因此你通常不想捕获它。捕获它会给程序带来不必要的不确定性。这种情况更像是 ValueError 或它的子类(class InvalidPasswordError(ValueError): pass)。

我不明白你为什么要检查 TypeError。你应该始终理解导致你捕获的异常的原因。如果你在这种情况下理解了,那很好;但我搞不清楚是什么错误会引发 TypeError,而你又能通过提示用户来合理处理。

你以明文形式接收密码并存储其 md5 哈希的做法并不是很安全。你应该考虑使用像 AuthKit 这样的工具,可以让这个过程更安全、更抽象。

10

在Python中,标准的错误处理方式是抛出一个异常,让调用这个函数的代码来处理这个错误。你可以选择让像NameError和TypeError这样的错误继续向上抛出,或者捕获这些错误,然后抛出一个你自己定义的InvalidPassword异常。

虽然你可以像现在这样从函数返回一个成功或失败的标志,或者错误代码,但其实不太推荐这样做。因为调用这个函数的人可能会忘记检查返回值,导致错误被忽略。而且,你是在一个属性设置器中返回值,这在Python中是没有意义的,因为赋值不是表达式,不能返回值。

另外,在处理异常时,你也不应该直接给用户打印信息。如果你以后想在一个图形界面程序中使用这个函数或类,那你的打印语句就没有地方可以输出了。不过,把错误记录到日志文件中(使用Python的日志模块)通常对调试是很有帮助的。

70

你的代码缺少上下文,所以很难判断哪个选择是正确的。这里有一些建议:

  • 不要使用 NameError 异常,这个异常只在找不到名字时使用,正如它的名字所说的那样。如果异常是关于参数的值或类型,应该使用 ValueErrorTypeError;

  • 不要打印错误信息。应该抛出有意义的异常,并附上清晰的错误信息:

    raise ValueError("password must be longer than 6 characters")
    
  • 从一个设置器(setter)返回值是没有意义的,因为赋值不是一个表达式,也就是说你不能检查赋值的结果:

    if (user.password = 'short'): ...
    
  • 在设置器中直接抛出异常,让设置属性的代码来处理这个异常。

示例:

class Test:

    minlen = 6

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, value):
        if not isinstance(value, basestring):
            raise TypeError("password must be a string")
        if len(value) < self.minlen:
            raise ValueError("password must be at least %d character len" % \
                                 self.minlen)
        self._password = value

你还可以看看 这个表单处理库,在这里,验证器 这里有一个例子 是独立的实体:它们可以动态设置,提供更高的控制力和更少的耦合代码,但这可能超出了你的需求。

撰写回答