2017-01-23 3 views
1

У нас есть 10-20 словарей, которые следуют за этим основной формат, ключ значение, которое должно быть назначено и значение является регулярное выражение:Может ли словарь python использовать re.compile в качестве ключа?

osTypeRE = collections.OrderedDict([ 
    ('WINDOWS', re.compile('^.*(windows|WIN2008|WIN2003).*$', re.MULTILINE+re.IGNORECASE)), 
    ('LINUX/UNIX', re.compile('^.*(unix|linux|ubuntu|red hat|redhat|RHEL|CentOS|CENT OS|Debian|SLES|SUSE|freebsd|free bsd|AIX|Solaris|SunOS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('MAC', re.compile('^.*(mac os x).*$', re.MULTILINE+re.IGNORECASE)), 
    ('STRATUS VOS', re.compile('^.*(VOS|Stratus).*$', re.MULTILINE+re.IGNORECASE)), 
    ('MAINFRAME', re.compile('^.*(OS400|AS400).*$', re.MULTILINE+re.IGNORECASE)), 
    ('CISCO IOS', re.compile('^.*(IOS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('NETWARE/OES', re.compile('^.*(NETWARE|OES|Open Enterprise Server).*$', re.MULTILINE+re.IGNORECASE)), 
    ('OPENVMS', re.compile('^.*(VMS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('HYPERVISOR', re.compile('^.*(ESX).*$', re.MULTILINE+re.IGNORECASE)), 
    ('HP NONSTOP', re.compile('^.*(NONSTOP|Tandem|NON STOP).*|.*(H06.20).*$', re.MULTILINE+re.IGNORECASE)), 
    ('EMBEDDED', re.compile('^.*(QNX).*$', re.MULTILINE+re.IGNORECASE)) 
]) 

Эти типы словарей оказались очень полезными, поскольку это позволяет стандартизировать компьютерную информацию из нескольких систем управления, чтобы затем можно было согласовать информацию.

Проблема только в том, что она слишком медленная. Вот функция, которую мы используем, чтобы нормализовать данные, используя этот тип словаря:

def reg_lookup(lookup, re_dict): 
    value = "INDETERMINATE" 
    for key, reg_ex in re_dict.items(): 
     matched = reg_ex.search(lookup) 
     if matched: 
      value = key.upper() 
      break 
    return value 

Поэтому в основном мы выполняем цикл через словарные значения (которые являются регулярным выражением), и когда мы находим матч мы берем ключ и это становится новое стандартизованное значение.

Но поскольку мы перебираем словарь, мы теряем скорость, связанную с хеш-таблицей словарного типа. Но как мы справимся? Могу ли я просто поменять пары ключей и значений в этих словарях? Но тогда как моя функция reg_lookup должна измениться, и будет ли она быстрее?

Другой пример словарь:

osVerRE = collections.OrderedDict([ 
    ('WINDOWS', collections.OrderedDict([ 
     ('WINDOWS SERVER 2000 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2000).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER|.* SR)(?=.*2003)(?=.*ENTERPRISE|.* Ent).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003).*|(?=.*WIN2003).*|(?=.*Windows)(?=.*SERVER)(?=.*2k3).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*ENTERPRISE|.* ENT).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8)(?=.*ENTERPRISE|.* ENT).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*STANDARD|.*STD).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 DATACENTER', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*DATACENTER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8).*|(?=.*WIN2008).*|(?=.*WINDOWS 2K8 R2).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 DATACENTER', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012)(?=.*DATACENTER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012)(?=.*STANDARD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012).*|(?=.*WINDOWS)(?=.*2012 R2).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS NT 4.0', re.compile('^(?=.*WINDOWS)(?=.*NT)(?=.*4\.0).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS XP PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*XP)(?=.*PROFESSIONAL).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 10 PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*10)(?=.*PRO).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*ENTERPRISE).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*PROFESSIONAL).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 ULTIMATE', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*ULTIMATE).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER LINE', re.compile('^(?=.*WINDOWS)(?=.*SERVER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS XP LINE', re.compile('^(?=.*WINDOWS)(?=.*XP).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 LINE', re.compile('^(?=.*WINDOWS)(?=.*7).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 8 LINE', re.compile('^(?=.*WINDOWS)(?=.*8).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 10 LINE', re.compile('^(?=.*WINDOWS)(?=.*10).*$', re.MULTILINE+re.IGNORECASE)) 
    ])), 
. 
. 
. 
+0

Ну, если вам нужно зацикливать, вам нужно петли ... Зачем использовать словарь в первую очередь? –

+0

Если вам все равно нужно проверить каждое выражение для соответствия, какая разница? – TigerhawkT3

+0

@ juanpa.arrivillaga: вам не нужно зацикливаться, так как лексер выполняет эти задачи и работает в длине содержимого для разбора, а не в количестве потенциальных токенов. –

ответ

0

Вместо пробегает по словарю с желаемым результатом в качестве ключа и выражения каждого возможного результата в качестве значения, то почему бы не сделать один поиск регулярных выражений (так как ваши выражения очень простые), а затем найти результат в словаре?

def reg_lookup(lookup, expression=re.compile('windows|WIN2008|WIN2003|unix|linux|ubuntu|red hat|redhat|RHEL|CentOS|CENT OS|Debian|SLES|SUSE|freebsd|free bsd|AIX|Solaris|SunOS|mac os x|VOS|Stratus|OS400|AS400|IOS|NETWARE|OES|Open Enterprise Server|VMS|ESX|NONSTOP|Tandem|NON STOP|H06.20|QNX', re.MULTILINE|re.IGNORECASE), dct={'windows':'WINDOWS', 'WIN2008':'WINDOWS', 'WIN2003':'WINDOWS', 'unix':'LINUX/UNIX', 'linux':'LINUX/UNIX'}): 
    result = expression.search(lookup) 
    if result: 
     return dct[result.group()] 
    else: 
     return 'INDETERMINATE' 

Обратите внимание, что H06.20 соответствует любому символу для .. Если вам нужна буквальная точка, используйте H06\.20.

+0

Вы также должны поразрядно или аргумент flags - 're.MULTILINE | re.IGNORECASE' вместо 're.MULTILINE + re.IGNORECASE' –

+0

Хороший ответ, единственное, что регулярное выражение становится совершенно ненужным при таком подходе. то есть для всех 250 тыс. компьютерных активов я мог бы просто создать словарь с каждым наблюдаемым изменением в качестве ключа и стандартизованного значения в качестве значения. Это будет в полной мере использовать скорость хеш-таблицы, но я боюсь, что она не будет такой гибкой и динамичной по своей природе. Спасибо за . коррекция. – gunslingor

+0

@JonClements - моя ошибка; Спасибо. – TigerhawkT3

0

Вот решение, которое я использую, по крайней мере пока. Он использует скорость таблицы хеширования для элементов, которые ранее были сопоставлены с использованием поиска в словарях и формирует более быстрый словарь во время поиска новых элементов.

Я испытываю увеличение скорости на 50% до 75% в зависимости от стандартизованного источника данных. Его немного запутанное, но не плохое решение ... кажется, обеспечивает лучшее из обоих миров. Мысли?

fast_lookup = {"mfr":{}, "model":{}, "dev_type":{}, "os_type":{}, "os_ver":{}, "country":{}, "state":{}, } 

def reg_lookup(lookup, re_dict, dict_name): 
    global fast_lookup 
    try: 
     #First try the faster dynamically generated dictionary 
     return fast_lookup[dict_name][lookup] 
    except: 
     #Otherwise, use the lookup dictionaries 
     for key, reg_ex in re_dict.items(): 
      matched = reg_ex.search(lookup) 
      if matched: 
       #Should a match be found, add it to the faster dictionary 
       fast_lookup[dict_name][lookup] = key.upper() 
       return key.upper() 
     return "INDETERMINATE" 
0

Это несколько довольно жутких регулярных выражений. Похоже, все они сводятся к горстке фиксированных строк, некоторые с вариантами и требуемыми комбинациями, ни с порядком. Моя первая реакция заключалась в том, чтобы обернуть их в разделы (?P<name>...) и объединить их в гигантское регулярное выражение, после чего вы можете запросить группу; поскольку внутри группы (не говоря уже о названных) нет групп захвата, вы даже можете использовать lastgroup (но вы хотите, чтобы порядок тестов был отменен, и это не было бы сокращено). Но это сделает код еще более уродливым. Я предпочел бы подход, когда подстроки не обернуты в ^.*(?=.*...).*$, но просто перечислены; есть слишком много шума, как есть.

Вот метод сырой преобразования для извлечения Струны себя и производить некоторые Matchable наборы:

import re, itertools 

l=[('WINDOWS SERVER 2000 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2000).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER|.* SR)(?=.*2003)(?=.*ENTERPRISE|.* Ent).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003).*|(?=.*WIN2003).*|(?=.*Windows)(?=.*SERVER)(?=.*2k3).*$', re.MULTILINE+re.IGNORECASE)),] 

def re2wordsets(e): 
    # Extracts the words, and converts alternatives into multiple sets. 
    groups=map(str.lower,re.findall(r'\(\?=\.\*([^)]*)\)', e.pattern)) 
    groups=[g.split('|.*') for g in groups] 
    for words in itertools.product(*groups): 
     yield set(words) 

# Convert to set form 
l2 = [(k,list(re2wordsets(e))) for (k,e) in l] 
# Collect all words that are used 
allwords = reduce(set.union, itertools.chain(*(i[1] for i in l2))) 
# Build a search regex to find any of them 
allwordsre = re.compile('|'.join(allwords), re.IGNORECASE | re.MULTILINE) 

# Use that regex once to find all relevant words in some string 
foundwords = set(map(str.lower, allwordsre.findall(somestring))) 
# Then see if we find a matching set 
# Note: this could be simplified if we flatten the list of sets 
# e.g. [(k,v1), (k,v2)] instead of [(k,[v1,v2])] 
for k,vs in l2: 
    for v in vs: 
     if v.issubset(foundwords): 
      print k 
      break 
    else:  # none of our sets matched, check next entry 
     continue 
    break 

Кстати, ответ на титульной вопрос, да в CPython 2.7.10, повторно шаблон объектов (SRE_Pattern) являются хешируемыми и могут использоваться как ключи, хотя это может быть зависимым от реализации, поскольку оно явно не указано в документации. Хотя я не уверен, что действительно относится к проблеме.

+0

Основная причина вопроса - ускорить процесс. то есть, как это сейчас, я должен зацикливаться до значений словаря, и регулярное выражение сравнивается по одному. Занимает около 50 секунд 150 000 записей, и мне бы хотелось что-то быстрее. поэтому я думал об изменении ключа и ценности ... но не знаю, как это будет работать. – gunslingor

Смежные вопросы