【问题标题】:Value never below zero值永远不会低于零
【发布时间】:2015-12-02 18:30:31
【问题描述】:

是否可以将数值分配给变量以使其限制在某个范围内?更具体地说,我想要一个永远不会低于零的变量,因为如果这即将发生,则会引发异常。

想象的例子:

>>> var = AlwaysPositive(0)
>>> print var 
0
>>> var += 3
>>> print var 
3
>>> var -= 4 
Traceback (most recent call last):   
   File "<stdin>", line 1, in <module> 
AlwaysPositiveError: dropping AlwaysPositive integer below zero

我问的原因是因为我正在调试我正在编写的游戏。在人类隐含地理解的地方,您永远不可能拥有-1 卡片,而计算机则没有。我可以创建检查游戏中使用的所有值的函数,并在整个脚本的多个位置调用这些函数,看看是否出现了任何奇怪的值。但我想知道是否有更简单的方法可以做到这一点?

【问题讨论】:

  • 你用什么语言?
  • 很抱歉我没有指定.. Python 2.7.10
  • 如果它是一个属性,你可以使用描述符来强制执行。

标签: python python-2.7 exception numeric zero


【解决方案1】:

您可以从int 继承您自己的数据类型,并为其提供a bunch of magic methods 重载您需要的运算符。

class Alwayspositive(int):
    def __init__(self, *args, **kwargs):
        super(Alwayspositive, self).__init__(*args, **kwargs)

    def __neg__(self):
        raise AlwayspositiveError()

    def __sub__(self, other):
        result = super(Alwayspositive, self).__sub__(other)
        if result < 0:
            raise AlwayspositiveError()
        return result

等等。要使这样的类安全,需要做大量的工作和调试,但它可以让您在调试和发布模式之间稍作更改即可调试代码。

【讨论】:

  • 注意Alwayspositive(-1)-Alwayspositive(-1)的结果——这是一个非常幼稚的实现
  • @Vovanrock2002 很好的答案,但我认为暂时有点过头了。尽管从阅读魔术方法开始,我会陷入困境。以及阅读其他继承 int 的示例。
【解决方案2】:

如果您真的需要,子类化int 可能是最好的方法,但到目前为止显示的实现是幼稚的。我会这样做:

class NegativeValueError(ValueError):
    pass


class PositiveInteger(int):

    def __new__(cls, value, base=10):
        if isinstance(value, basestring):
            inst = int.__new__(cls, value, base)
        else:
            inst = int.__new__(cls, value)
        if inst < 0:
            raise NegativeValueError()
        return inst

    def __repr__(self):
        return "PositiveInteger({})".format(int.__repr__(self))

    def __add__(self, other):
        return PositiveInteger(int.__add__(self, other))

    # ... implement other numeric type methods (__sub__, __mul__, etc.)

这使您可以像普通的int 一样构造PositiveInteger

>>> PositiveInteger("FFF", 16)
PositiveInteger(4095)
>>> PositiveInteger(5)
PositiveInteger(5)
>>> PositiveInteger(-5)

Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    PositiveInteger(-5)
  File "<pyshell#17>", line 8, in __new__
    raise NegativeValueError()
NegativeValueError

参见例如the datamodel docs on numeric type emulation 了解您需要实现的方法的详细信息。请注意,在大多数这些方法中,您不需要显式检查负数,因为当您 return PositiveInteger(...) 时,__new__ 会为您执行此操作。使用中:

>>> i = PositiveInteger(5)
>>> i + 3
PositiveInteger(8)

或者,如果这些非负整数是类的属性,您可以使用 descriptor protocol 强制执行正值,例如:

class PositiveIntegerAttribute(object):

    def __init__(self, name):
        self.name = name

    def __get__(self, obj, typ=None):
        return getattr(obj, self.name)

    def __set__(self, obj, val):
        if not isinstance(val, (int, long)):
            raise TypeError()
        if val < 0:
            raise NegativeValueError()
        setattr(obj, self.name, val)

    def __delete__(self, obj):
        delattr(obj, self.name)

然后您可以按如下方式使用它:

>>> class Test(object):
    foo = PositiveIntegerAttribute('_foo')


>>> t = Test()
>>> t.foo = 1
>>> t.foo = -1

Traceback (most recent call last):
  File "<pyshell#34>", line 1, in <module>
    t.foo = -1
  File "<pyshell#28>", line 13, in __set__
    raise NegativeValueError()
NegativeValueError
>>> t.foo += 3
>>> t.foo
4
>>> t.foo -= 5

Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    t.foo -= 5
  File "<pyshell#28>", line 13, in __set__
    raise NegativeValueError()
NegativeValueError

【讨论】:

  • 谢谢。不确定我是否会使用它,但无论哪种方式,它都能提高我对 python 的理解。
  • 不幸的是,int-derivation 忽略了重写操作符方法的有趣细节,这是真正棘手的部分......
  • @dhke 不是真的;它们中的大多数将完全是微不足道的(__add__return PositiveInteger(self + other)__sub__return PositiveInteger(self - other),等等)!
  • self + other 不会再次调用__add__() 吗?
  • @dhke 是的,你是对的 - 你必须访问 super/int。我将添加一个示例
猜你喜欢
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2014-07-07
相关资源
最近更新 更多