在Python中处理具有未知默认值的类属性

时间:2018-11-01 17:42:23

标签: python python-3.x oop

我有一个像下面这样的课程:

class Invoice
    def __init__(self, invoice_id):
        self.invoice_id = invoice_id
        self._amount = None

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, amount):
        self._amount = amount

在上面的示例中,每当我尝试在不设置发票金额的情况下尝试获取发票金额时,都将显示“无”,如下所示:

invoice = Invoice(invoice_id='asdf234')
invoice.amount
>> None

但是在这种情况下,“ None”不是正确的金额默认值。我应该能够区分金额值,因为根本没有设置任何值。所以问题如下:

  1. 如何处理类属性没有正确的默认值的情况?在上面的示例中,如果我从初始化中删除self._amount = None,我将为self._amount获取AttributeError,而self.amount仅在调用invoice.amount = 5之后才返回有效值。这是正确的处理方式吗?但这也会导致对象状态不一致,因为应用程序将在运行时更改实例属性。
  2. 我将金额保留为发票的属性,以更好地理解和读取发票及其属性。仅当我们知道类的init /默认值时,才应该使用类属性吗?

3 个答案:

答案 0 :(得分:2)

在这种情况下,请勿使用property。您的获取器和设置器除了返回并设置值之外,什么都不做。只需使用常规属性。如果以后要控制访问,则可以稍后再添加该属性,而无需更改类的界面!

对于处理未设置的值,只需创建自己的对象来表示从未设置过的值即可。它可以很简单:

>>> NOT_SET = object()
>>> class Invoice:
...     def __init__(self, invoice_id):
...         self.invoice_id = invoice_id
...         self.amount = NOT_SET
...
>>> inv = Invoice(42)
>>> if inv.amount is NOT_SET:
...     inv.amount = 1
...

如果想要更好的打字支持,也可以使用enum

答案 1 :(得分:2)

None通常用于不存在值的情况,但是有时您实际上需要将其作为允许的值。如果您希望能够区分None和未设置的值,只需为此定义您自己的单例。

class Undefined:
    __str__ = __repr__ = lambda self: "Undefined"

Undefined = Undefined()

class Invoice
    def __init__(self, invoice_id):
        self.invoice_id = invoice_id
        self._amount = Undefined

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, amount):
        if amount is Undefined:
            raise ValueError("amount must be an actual value")
        self._amount = amount

当然,您现在可能需要使用其他方法测试Undefined,以确保在实例正确初始化之前未使用它们。更好的方法可能是在初始化期间设置属性,并要求将其值传递到__init__()中。这样,可以避免使Invoice处于无效(未完全初始化)状态。有人仍然可以将_amount设置为无效值,但是他们只会遇到他们想要的麻烦。我们都是成年人。

答案 2 :(得分:1)

您可以创建一个静态类,并使用它来确定是否设置了一个值。

class AmountNotSet(object):
    pass


class Invoice(object):
    def __init__(self, invoice_id):
        self.invoice_id = invoice_id
        self._amount = AmountNotSet
    # ...etc...

然后您可以像这样检查发票是否已设置:

invoice1 = Invoice(1)
invoice2 = Invoice(2)
invoice2.amount = None

invoice1.amount is AmountNotSet  # => True
invoice2.amount is None          # => True