Интерпретация числовых диапазонов в Python
В веб-приложении Pylons мне нужно взять строку типа "<3, 45, 46, 48-51, 77" и создать список целых чисел (которые фактически являются идентификаторами объектов) для поиска.
Любые предложения о том, как это сделать? Я новичок в Python, и я не нашел ничего такого, что могло бы помочь с такими вещами.
Список будет: [1, 2, 3, 45, 46, 48, 49, 50, 51, 77]
5 ответов
Используйте parseIntSet отсюда
Мне также нравится реализация pyparsing в комментариях в конце.
ParseIntSet был изменен здесь, чтобы обрабатывать записи типа "<3" и выдавать недопустимые строки, только если они есть.
#! /usr/local/bin/python
import sys
import os
# return a set of selected values when a string in the form:
# 1-4,6
# would return:
# 1,2,3,4,6
# as expected...
def parseIntSet(nputstr=""):
selection = set()
invalid = set()
# tokens are comma seperated values
tokens = [x.strip() for x in nputstr.split(',')]
for i in tokens:
if len(i) > 0:
if i[:1] == "<":
i = "1-%s"%(i[1:])
try:
# typically tokens are plain old integers
selection.add(int(i))
except:
# if not, then it might be a range
try:
token = [int(k.strip()) for k in i.split('-')]
if len(token) > 1:
token.sort()
# we have items seperated by a dash
# try to build a valid range
first = token[0]
last = token[len(token)-1]
for x in range(first, last+1):
selection.add(x)
except:
# not an int and not a range...
invalid.add(i)
# Report invalid tokens before returning valid selection
if len(invalid) > 0:
print "Invalid set: " + str(invalid)
return selection
# end parseIntSet
print 'Generate a list of selected items!'
nputstr = raw_input('Enter a list of items: ')
selection = parseIntSet(nputstr)
print 'Your selection is: '
print str(selection)
И вот вывод из примера запуска:
$ python qq.py
Generate a list of selected items!
Enter a list of items: <3, 45, 46, 48-51, 77
Your selection is:
set([1, 2, 3, 45, 46, 77, 48, 49, 50, 51])
Я создал версию решения @vartec, которую я считаю более читаемой:
def _parse_range(numbers: str):
for x in numbers.split(','):
x = x.strip()
if x.isdigit():
yield int(x)
elif x[0] == '<':
yield from range(0, int(x[1:]))
elif '-' in x:
xr = x.split('-')
yield from range(int(xr[0].strip()), int(xr[1].strip())+1)
else:
raise ValueError(f"Unknown range specified: {x}")
В процессе функция превратилась в генератор:)
rng = "<3, 45, 46, 48-51, 77"
ids = []
for x in map(str.strip,rng.split(',')):
if x.isdigit():
ids.append(int(x))
continue
if x[0] == '<':
ids.extend(range(1,int(x[1:])+1))
continue
if '-' in x:
xr = map(str.strip,x.split('-'))
ids.extend(range(int(xr[0]),int(xr[1])+1))
continue
else:
raise Exception, 'unknown range type: "%s"'%x
Во-первых, вам нужно выяснить, какой синтаксис вы принимаете. У вас есть три в вашем примере:
Одиночный номер: 45, 46
Меньше чем оператор
Тире: 48-51
После этого нужно просто разбить строку на токены и проверить формат токена.
Я также должен был сделать нечто подобное для приложения в последнее время.
Если вам не нужны конкретные числа, а просто способ узнать, находится ли данное число в диапазоне, вы можете рассмотреть его синтаксический анализ в выражении Python, которое можно перевести в лямбду. Например <3, 5-10, 12
может быть func=(lambda x:x<3 or (5 <= x <= 10) or x==12))
, Тогда вы можете просто позвонить в лямбду, func(11)
чтобы увидеть, если 11 принадлежит там.
>>> print range.__doc__
range([start,] stop[, step]) -> list of integers
Вернуть список, содержащий арифметическую прогрессию целых чисел. range(i, j) возвращает [i, i+1, i+2, ..., j-1]; start (!) по умолчанию равен 0. Когда задан шаг, он определяет приращение (или уменьшение). Например, range(4) возвращает [0, 1, 2, 3]. Конечная точка опущена! Это в точности действительные индексы для списка из 4 элементов.
>>> range(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]
>>> range(1,3)
[1, 2]
Я полагаю, вы могли бы перебрать свой список и соответствующим образом назвать диапазон.
>>> def lessThan(n) :
... return range(n+1)
...
>>> lessThan(4)
[0, 1, 2, 3, 4]
>>> def toFrom(n,m):
... return range(n,m)
...
>>> toFrom(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]
Затем разбейте строку на запятые и для каждого бита проанализируйте его достаточно, чтобы выяснить, какую функцию вызывать, объединяя возвращаемые списки.
Что-нибудь еще, и я бы написал это для вас.