【问题标题】:Proper way in Python to raise errors while setting variables在 Python 中设置变量时引发错误的正确方法
【发布时间】:2011-02-01 07:43:37
【问题描述】:

在课堂上进行错误检查的正确方法是什么?引发异常?设置一个包含所有错误的实例变量字典“错误”并返回它?

从一个类中打印错误是不是很糟糕? 如果我提出异常,是否必须返回 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()

【问题讨论】:

  • 该代码中有太多名为 password 的东西。 (第一个函数、第二个函数和第二个函数的参数。)pyflakes 将成为你的朋友。
  • @keturn:这是property() 函数的文档中显示的模式。看看code example in the docs that uses .setter decorator。在setter方法password里面是一个局部变量(参数)。其他名称位于不同的命名空间中(它们属于该类)。

标签: python error-handling


【解决方案1】:

您的代码脱离了上下文,因此显然不是正确的选择。遵循一些提示:

  • 不要使用NameError异常,它只用于当一个名称,正如异常本身所说,在本地或全局范围内找不到,如果异常使用ValueErrorTypeError涉及参数的值或类型;

  • 不打印错误消息。使用有意义的错误消息引发有意义的异常:

    raise ValueError("password must be longer than 6 characters")
    
  • 当赋值不是表达式时,从 setter 返回值是没有意义的,即你不能检查赋值的值:

    if (user.password = 'short'): ...
    
  • 只需在 setter 中引发异常,然后让设置属性的代码处理它。

例子:

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

再看看 this forms handling library,那里的验证器 here an example 是它们自己的实体:它们可以通过更高的控制和更少的耦合代码进行动态设置,但也许这比你需要的要多得多。

【讨论】:

  • @mg,总体而言,很棒的帖子(以及很棒的首字母缩写!),但我担心最后的示例。你好像编了一个password 装饰器,它的用法很奇怪;你的意思是@property 并命名第一个方法password?此外,如果要使用属性,则必须使用新样式类,即将示例的第一行更改为 class Test(object): 或从其他一些新样式类继承。 (即使你还没有使用属性,你也应该使用新样式的类。)
  • @Mike Graham:该死的,疲倦可能会开玩笑。您对属性命名是正确的,确切的形式是 ensare 最初使用的形式,我更喜欢旧的 password = property(...) 但我会在不真正了解新的 2.6 语法的情况下与 ensare 保持一致。第二个观察结果不正确:属性可以与旧样式类一起使用
  • @mg,糟糕,我记错了,实现描述符协议的类(如property)必须是新型类。尽可能使用新型类还有其他一些微妙的优势。
  • “你不能检查赋值的值”你可以使用海象运算符:= 从 Python 3.8 开始
【解决方案2】:

在 python 中发出错误信号的标准方法是引发异常并让调用代码处理它。要么让 NameError 和 TypeError 向上继续,要么捕获它们并引发您定义的 InvalidPassword 异常。

虽然可以像您所做的那样从函数返回成功/失败标志或错误代码,但不建议这样做 - 调用者很容易忘记检查返回值并丢失错误。此外,您正在从属性设置器返回一个值 - 这在 Python 中毫无意义,因为赋值不是表达式并且不能返回值。

您也不应该在异常处理中为用户打印消息 - 如果您以后想在 GUI 程序中使用函数或类怎么办?在这种情况下,您的打印语句将无处可打印。将错误记录到日志文件(使用 Python 的日志记录模块)通常有助于调试。

【讨论】:

    【解决方案3】:

    通常,您应该指出通过使用异常传播的错误。如果您发现刚刚检查的内容有错误并且可以立即处理,则无需引发异常。

    例如,在 setter 的特定情况下,返回 False 或其他任何内容都无济于事。设置你必须检查的实例变量是非常次优的,因为那样你可能会在意外中错过一个错误。

    print 通常不是对错误的良好响应。在这种情况下,听起来您想告诉最终用户他们需要使用不同的密码。听起来您应该调用一个方法,使带有表单的网页向用户解释出了什么问题;您可以调用在您的类中执行此操作的方法,或者引发一个异常,该异常将传播并最终被捕获并用于该目的。 (这是一般性建议。我对 Pylons 的了解还不够,无法告诉你它希望你如何做到这一点。)

    您不应提出自己的NameError 异常。 NameError 几乎总是表明您的程序中有错字,因此您通常不想抓住它。通过捕捉它,您将不必要的不​​确定性引入程序。这看起来可能更像是 ValueError 或其子类 (class InvalidPasswordError(ValueError): pass)。

    我不明白你为什么要查看TypeError。您应该始终了解导致您捕获的异常的原因。如果您在这种情况下这样做,那就太好了;我不知道会引发什么错误TypeError,您可以通过提示用户来明智地处理。

    您以明文形式接收密码并存储其 md5 哈希的技术不是很安全。您应该研究 AuthKit 之类的东西,它可以使这个过程更加安全和抽象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      • 2017-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      相关资源
      最近更新 更多