2015-09-07 60 views
0

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

虚构的例子:

>>> 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 

我想问的原因是因为我调试游戏我写信。在人类明确理解的地方,你手中永远不会有卡,计算机不会。我可以制作检查游戏中使用的所有值的函数,并在整个脚本中的多个位置调用这些函数,并查看是否出现任何奇怪的值。但我想知道是否有更简单的方法来做到这一点?

+0

你使用什么语言? –

+0

我很抱歉,我没有指定.. Python 2.7.10 –

+1

如果它将是一个属性,你可以使用描述符来强制执行此操作。 – jonrsharpe

回答

1

子分类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.) 

这样就可以构建一个PositiveInteger就像一个普通的int

>>> 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 
+0

谢谢。不确定我是否会使用它,但它增强了我对Python的理解。 –

+0

不幸的是,'int'派生忽略了覆盖操作符方法的有趣细节,这是实际棘手的部分... – dhke

+0

@dhke不是真的;他们中的大多数将是完全无足轻重的('__add__'是'return PositiveInteger(self + other)','__sub__'是'return PositiveInteger(self - other)'等)! – jonrsharpe

-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 

依此类推。为了使这样一个类安全,这是相当多的工作和调试,但它将允许您在调试模式和发布模式之间进行非常小的调整,从而调试代码。

+1

请注意,您还没有更新'__init__'中的'super'行... – jonrsharpe

+0

@jonrsharpe谢谢!更新它 – Vovanrock2002

+0

注意'Alwayspositive(-1)'或'--Alwayspositive(-1)'的结果 - 这是一个非常天真的实现 – jonrsharpe

相关问题