区间表示法?

5

我希望创建一个用于表示实数区间的Python类。最接近数学符号的语法应该是Interval([a, b)),或者更好的方式是Interval[a, b),它用于构造满足a <= x < b的所有实数x的区间。

是否可能创建一个可以处理这种语法的类?


3
Python有自己的括号解释,所以你可以尝试在字符串中使用[a, b)。请注意,这并不是因为Python支持半开区间而成立的,而是因为您正在将一个包含逗号的元组放入字符串中。 - vishes_shell
1
实际上,程序员通常会假设半开区间(左闭右开),除非你另有说明。这就是range()对整数的处理方式,也是切片语法的正常解释方式。因此,完全开放、完全关闭或右侧闭合的情况是应该具有不寻常语法的特殊情况。 - Kevin
3个回答

5

无法通过创建自定义类来“修复”语法上无效的Python代码。

我认为在Python中最接近数学区间表示法的方式是:

Interval('[a, b)')

如果您将时间间隔作为函数的参数传递,并且该函数在使用这些参数之前将其转换为适当的类型,则该方法变得更加轻量级。例如:

def do_foo(interval, bar, baz):
    interval = Interval(interval)
    # do stuff

do_foo('[3,4)', 42, true)

可能的实现

import re

class Interval:
    def __init__(self, interval):
        """Initialize an Interval object from a string representation of an interval
           e.g: Interval('(3,4]')"""
        if isinstance(interval, Interval):
            self.begin, self.end = interval.begin, interval.end
            self.begin_included = interval.begin_included
            self.end_included = interval.end_included
            return
        number_re = '-?[0-9]+(?:.[0-9]+)?'
        interval_re = ('^\s*'
                       +'(\[|\()'  # opeing brecket
                       + '\s*'
                       + '(' + number_re + ')'  # beginning of the interval
                       + '\s*,\s*'
                       + '(' + number_re + ')'  # end of the interval
                       + '\s*'
                       + '(\]|\))'  # closing brecket
                       + '\s*$'
                      )
        match = re.search(interval_re, interval)
        if match is None:
            raise ValueError('Got an incorrect string representation of an interval: {!r}'. format(interval))
        opening_brecket, begin, end, closing_brecket = match.groups()
        self.begin, self.end = float(begin), float(end)
        if self.begin >= self.end:
            raise ValueError("Interval's begin shoud be smaller than it's end")
        self.begin_included = opening_brecket == '['
        self.end_included = closing_brecket == ']'
        # It might have been batter to use number_re = '.*' and catch exeptions float() raises instead

    def __repr__(self):
        return 'Interval({!r})'.format(str(self))

    def __str__(self):
        opening_breacket = '[' if self.begin_included else '('
        closing_breacket = ']' if self.end_included else ')'
        return '{}{}, {}{}'.format(opening_breacket, self.begin, self.end, closing_breacket)

    def __contains__(self, number):
        if self.begin < number < self.end:
            return True
        if number == self.begin:
            return self.begin_included
        if number == self.end:
            return self.end_included

@martineau 您有一个例子吗? - Андрей Беньковский
在“-?[0-9]+(?:[0-9]+)?”中,“:”的目的是什么? - blue01

1
你无法使这个确切的语法工作。但是你可以通过覆盖相关的比较方法来做类似的事情。
a <= Interval() < b

这整个表达式可以返回一个新的Interval对象,该对象包括大于或等于a且严格小于b的所有内容。Interval()本身可以被解释为从负无穷到正无穷的完全开放间隔(即所有实数的无界间隔),而Interval() < b则可以指上限有限但下限无限的区间。

NumPy在数组比较操作中使用类似的技术(其中A < B表示“返回一个由一和零组成的数组,对应于A的每个元素是否小于B的相应元素”)。


对我来说,这个解决方案在语法上看起来像 Interval[1](4)Interval(1)[4] 一样聪明和反直觉。 - Андрей Беньковский

1

你不能改变Python现有的语法规则(除非改变整个语言),但你可以尽可能地接近你想要的效果:

class Interval(object):
    def __init__(self, left_bracket, a, b, right_bracket):
        if len(left_bracket) !=1 or left_bracket not in '[(':
            raise ValueError(
                'Unknown left bracket character: {!r}'.format(left_bracket))
        if len(right_bracket) !=1 or right_bracket not in '])':
            raise ValueError(
                'Unknown right bracket character: {!r}'.format(right_bracket))

        if a < b:
            self.lower, self.upper = a, b
        else:
            self.lower, self.upper = b, a

        self.left_bracket, self.right_bracket = left_bracket, right_bracket

        if left_bracket == '[':
            if right_bracket == ']':
                self._contains = (
                    lambda self, val: self.lower <= val <= self.upper)
            else:
                self._contains = (
                    lambda self, val: self.lower <= val <  self.upper)
        else:
            if right_bracket == ']':
                self._contains = (
                    lambda self, val:  self.lower < val <= self.upper)
            else:
                self._contains = (
                    lambda self, val:  self.lower < val <  self.upper)

    __contains__ = lambda self, val: self._contains(self, val)

    def __str__(self):
        return '{}{}, {}{}'.format(self.left_bracket, self.lower, self.upper,
                                   self.right_bracket)

    def __repr__(self):
        return '{}({!r}, {}, {}, {!r})'.format(self.__class__.__name__,
                self.left_bracket, self.lower, self.upper, self.right_bracket)

if __name__ == '__main__':
    interval1 = Interval('[', 1, 3, ']')  # closed interval
    interval2 = Interval('[', 1, 3, ')')  # half-open interval

    print('{} in {}? {}'.format(3, interval1,  3 in interval1))
    print('{} in {}? {}'.format(3, interval2,  3 in interval2))

输出:

3 in [1, 3]? True
3 in [1, 3)? False

注意:参数ab可以是任何可比较的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接