【问题标题】:Pythonic use of properties and exceptions [closed]Pythonic 使用属性和异常 [关闭]
【发布时间】:2018-03-23 00:49:07
【问题描述】:

我有一个名为 Divider1 的类,它将分子除以分母,在初始化时设置:

class Divider1:
    def __init__(self, numerator=0, denominator=1):
        self.numerator = numerator
        self.denominator = denominator

    def divide(self):
        return self.numerator / self.denominator

类很容易被错误的初始化滥用,但直到真正发生划分时才立即清楚:

>>> x = Divider1('hello', 5)
>>> x.divide()
TypeError: unsupported operand type(s) for /: 'str' and 'int'

看来提早引发异常对调试和维护很有好处,所以这是我的下一个合乎逻辑的步骤:

class Divider2:
    def __init__(self, numerator=0, denominator=1):
        try:
            numerator = float(numerator)
        except ValueError:
            raise ValueError("Numerator must be numeric!")

        try:
            denominator = float(denominator)
        except ValueError:
            raise ValueError("Denominator must be numeric!")

        if denominator == 0:
            raise ValueError("Denominator must be non-zero!")

        self.numerator = numerator
        self.denominator = denominator

    def divide(self):
        return self.numerator / self.denominator

现在我们有了一些安全感:

>>> x = Divider2('hello', 5)
ValueError: Numerator must be numeric!

我很清楚,初始化时出了什么问题,但初始化后更改属性仍然可以滥用:

>>> x = Divider2(10, 5)
>>> x.numerator = 'hello'
>>> x.divide()
TypeError: unsupported operand type(s) for /: 'str' and 'int'

我正在尝试找出处理这些问题的 Pythonic 方式。我可能只是将分子和分母命名为 _numerator_denominator,以表明它们应该被视为私有的。

或者,我可以使用属性:

class Divider4:
    def __init__(self, numerator=0, denominator=1):
        self.numerator = numerator
        self.denominator = denominator

    @property
    def numerator(self):
        return self._numerator

    @numerator.setter
    def numerator(self, value):
        try:
            value = float(value)
        except ValueError:
            raise ValueError("Denominator must be numeric!")

        self._numerator = value

    @property
    def denominator(self):
        return self._denominator

    @denominator.setter
    def denominator(self, value):
        try:
            value = float(value)
        except ValueError:
            raise ValueError("Denominator must be numeric!")

        if value == 0:
            raise ValueError("Denominator must be non-zero!")

        self._denominator = value

    def divide(self):
        return self.numerator / self.denominator

那么现在:

>>> x = Divider4(10, 5)
>>> x.numerator = 'hello'
ValueError: Numerator must be numeric!
>>> x = Divider4(10, 0)
ValueError: Denominator must be non-zero!

它显然更安全,而且我立即知道错误的原因,无论该类如何使用 - Divider 都不可能达到不可能的状态。但是我必须编写更多的代码来实现这一点,尤其是与 Divider1 相比。但是我仍然认为应该始终达到这种安全性,尽管我没有看到人们在编写 Python 代码时一直这样做。我是否以意想不到的方式使用 Python?总是写这么繁琐的属性来保证安全,是不是 Pythonic?有没有应该做或不应该做的时候?

【问题讨论】:

  • 投票结束是基于意见,这不是Stack Overflow 的主题。话虽如此,我的看法是:不要做任何事情。让.divide() 中的除法引发异常。使用您的类的程序员将按预期使用它或将收到异常。如果您确实希望早点发生异常,请从self.__init__() 调用self.divide(),不带try,也不带except
  • 如果您想以不同的方式处理异常,我可以理解为什么要捕获异常。但在我看来,您仍在提出异常并终止程序,尽管是立即并提供更具体的信息(例如ValueError: Numerator must be numeric! 而不是通用的TypeError: unsupported operand type(s) for /: 'float' and 'str')。但这是否证明所有额外的 try except 声明是合理的?我认为 Python 的想法是尽可能地依赖内置的异常处理。通用异常信息有什么不好>?
  • “我有一个名为 Divider1 的类,它将分子除以分母”——这不是一个类的工作。如果要执行计算,请编写一个函数。如果你想表示一个事物,写一个类。
  • 感谢 cmets。 Divider 类是一个简化的示例,我一直在考虑更普遍的问题,更复杂的类还有很多事情要做。

标签: python python-3.x exception


【解决方案1】:

很多丑陋来自value = float(value)。您可以使用抽象基类numbers.Number 来检查您的输入是否为数字。

from numbers import Number

class Divider4:
    def __init__(self, numerator=0, denominator=1):
        self.numerator = numerator
        self.denominator = denominator   
    @property
    def numerator(self):
        return self._numerator   
    @numerator.setter
    def numerator(self, value):
        if not isinstance(value, Number):
            raise ValueError("Bad numerator: {}".format(value))  
        self._numerator = value
    @property
    def denominator(self):
        return self._denominator    
    @denominator.setter
    def denominator(self, value):
        if not isinstance(value, Number) or value == 0:
            raise ValueError('Bad denominator: {}'.format(value))
        self._denominator = value    
    def divide(self):
        return self.numerator / self.denominator

数字的类型层次结构在PEP 3141中定义

【讨论】:

    猜你喜欢
    • 2021-12-18
    • 2014-02-05
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 2017-01-05
    相关资源
    最近更新 更多