价值永远不会低于零
Value never below zero
是否可以将数值赋值给一个变量,使其限制在一定范围内?更具体地说,我想要一个永远不会低于零的变量,因为如果这种情况即将发生,则会引发异常。
虚构的例子:
>>> 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
张牌,而计算机却没有。我可以制作检查游戏中使用的所有值的函数,并在整个脚本的多个位置调用这些函数,看看是否出现任何奇怪的值。但我想知道是否有更简单的方法来做到这一点?
您可以从 int
子class 您自己的数据类型,并为它提供 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
等等。要使这样的 class 安全,需要进行大量的工作和调试,但它允许您在调试和发布模式之间进行很小的更改即可调试代码。
如果确实需要,Sub-classing 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)
或者,如果这些非负整数将是 class 的属性,您可以使用 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
是否可以将数值赋值给一个变量,使其限制在一定范围内?更具体地说,我想要一个永远不会低于零的变量,因为如果这种情况即将发生,则会引发异常。
虚构的例子:
>>> 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
张牌,而计算机却没有。我可以制作检查游戏中使用的所有值的函数,并在整个脚本的多个位置调用这些函数,看看是否出现任何奇怪的值。但我想知道是否有更简单的方法来做到这一点?
您可以从 int
子class 您自己的数据类型,并为它提供 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
等等。要使这样的 class 安全,需要进行大量的工作和调试,但它允许您在调试和发布模式之间进行很小的更改即可调试代码。
Sub-classing 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)
或者,如果这些非负整数将是 class 的属性,您可以使用 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