Словарь без учета регистра
Я хотел бы, чтобы мой словарь был нечувствительным к регистру.
у меня есть этот код:
text = "practice changing the color"
words = {'color': 'colour',
'practice': 'practise'}
def replace(words,text):
keys = words.keys()
for i in keys:
text= text.replace(i ,words[i])
return text
text = replace(words,text)
print text
выход = практика изменения цвета
Я хотел бы другую строку,"practice changing the Color", (где Color начинается с прописной) также дать тот же результат.
Я считаю, что есть общий способ конвертировать в нижний регистр с помощью
mydictionary[key.lower()] но я не уверен, как наилучшим образом интегрировать это в мой существующий код. (Если это будет разумный, простой подход в любом случае.)
8 ответов:
Если я правильно понимаю вас, и вы хотите, чтобы способ ключевых словарей в не чувствительном к регистру моде, один из способов было бы подкласс dict и перегрузить сеттер / геттер:
class CaseInsensitiveDict(dict): def __setitem__(self, key, value): super(CaseInsensitiveDict, self).__setitem__(key.lower(), value) def __getitem__(self, key): return super(CaseInsensitiveDict, self).__getitem__(key.lower())
The в настоящее время утвержден ответ не работает большое случаев, поэтому его нельзя использовать в качестве drop-in
dictзамена. Некоторые хитрые моменты в получении правильногоdictзамена:
- перегрузка всех методов, которые включают ключи
- правильная обработка нестроковых ключей
- правильная обработка конструктора класса
следующее должно работать много лучше:
class CaseInsensitiveDict(dict): @classmethod def _k(cls, key): return key.lower() if isinstance(key, basestring) else key def __init__(self, *args, **kwargs): super(CaseInsensitiveDict, self).__init__(*args, **kwargs) self._convert_keys() def __getitem__(self, key): return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key)) def __setitem__(self, key, value): super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value) def __delitem__(self, key): return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key)) def __contains__(self, key): return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key)) def has_key(self, key): return super(CaseInsensitiveDict, self).has_key(self.__class__._k(key)) def pop(self, key, *args, **kwargs): return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs) def get(self, key, *args, **kwargs): return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs) def setdefault(self, key, *args, **kwargs): return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs) def update(self, E={}, **F): super(CaseInsensitiveDict, self).update(self.__class__(E)) super(CaseInsensitiveDict, self).update(self.__class__(**F)) def _convert_keys(self): for k in list(self.keys()): v = super(CaseInsensitiveDict, self).pop(k) self.__setitem__(k, v)
просто для протокола. Я нашел удивительный impementation на запросы:
https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
не могли бы вы рассмотреть возможность использования
string.lower()на ваших входах и с помощью полностью строчного словаря? Это немного хаки решение, но оно работает
в моем конкретном случае мне нужен был поиск без учета регистра, однако я не хотел изменять исходный случай ключа. Например:
>>> d = {} >>> d['MyConfig'] = 'value' >>> d['myconfig'] = 'new_value' >>> d {'MyConfig': 'new_value'}вы можете видеть, что словарь по-прежнему имеет исходный ключ, однако он доступен без учета регистра. Вот простое решение:
class CaseInsensitiveKey(object): def __init__(self, key): self.key = key def __hash__(self): return hash(self.key.lower()) def __eq__(self, other): return self.key.lower() == other.key.lower() def __str__(self): return self.keyпереопределения __hash__ и __eq__ требуются как для получения, так и для установки записей в словаре. Это создает ключи, которые хэшируются в одну и ту же позицию в словарь, если они нечувствительны к регистру равны.
Теперь либо создать пользовательский словарь, который инициализирует CaseInsensitiveKey с помощью предоставленного ключа:
class CaseInsensitiveDict(dict): def __setitem__(self, key, value): key = CaseInsensitiveKey(key) super(CaseInsensitiveDict, self).__setitem__(key, value) def __getitem__(self, key): key = CaseInsensitiveKey(key) return super(CaseInsensitiveDict, self).__getitem__(key)или просто убедитесь, что всегда передавать экземпляр CaseInsensitiveKey в качестве ключа при использовании словаря.
хотя словарь без учета регистра является решением, и есть ответы на то, как этого достичь, в этом случае есть, возможно, более простой способ. Достаточно поиска без учета регистра:
import re text = "Practice changing the Color" words = {'color': 'colour', 'practice': 'practise'} def replace(words,text): keys = words.keys() for i in keys: exp = re.compile(i, re.I) text = re.sub(exp, words[i], text) return text text = replace(words,text) print text
Я изменил простое, но хорошее решение от pleasemorebacon (спасибо!) что делает его немного более компактным, автономным и с небольшими обновлениями, чтобы позволить строительство от
{'a':1, 'B':2}и протокол. Наконец, так какCaseInsensitiveDict.Keyожидается строка (Что еще может быть чувствительным к регистру или нет), это хорошая идея, чтобы получитьKeyклассstr, то можно, например, на свалкуCaseInsensitiveDictСjson.dumpsиз коробка.# caseinsensitivedict.py class CaseInsensitiveDict(dict): class Key(str): def __init__(self, key): str.__init__(key) def __hash__(self): return hash(self.lower()) def __eq__(self, other): return self.lower() == other.lower() def __init__(self, data=None): super(CaseInsensitiveDict, self).__init__() if data is None: data = {} for key, val in data.items(): self[key] = val def __contains__(self, key): key = self.Key(key) return super(CaseInsensitiveDict, self).__contains__(key) def __setitem__(self, key, value): key = self.Key(key) super(CaseInsensitiveDict, self).__setitem__(key, value) def __getitem__(self, key): key = self.Key(key) return super(CaseInsensitiveDict, self).__getitem__(key)вот базовый тестовый скрипт для тех, кто любит проверять вещи в действии:
# test_CaseInsensitiveDict.py import json import unittest from caseinsensitivedict import * class Key(unittest.TestCase): def setUp(self): self.Key = CaseInsensitiveDict.Key self.lower = self.Key('a') self.upper = self.Key('A') def test_eq(self): self.assertEqual(self.lower, self.upper) def test_hash(self): self.assertEqual(hash(self.lower), hash(self.upper)) def test_str(self): self.assertEqual(str(self.lower), 'a') self.assertEqual(str(self.upper), 'A') class Dict(unittest.TestCase): def setUp(self): self.Dict = CaseInsensitiveDict self.d1 = self.Dict() self.d2 = self.Dict() self.d1['a'] = 1 self.d1['B'] = 2 self.d2['A'] = 1 self.d2['b'] = 2 def test_contains(self): self.assertIn('B', self.d1) d = self.Dict({'a':1, 'B':2}) self.assertIn('b', d) def test_init(self): d = self.Dict() self.assertFalse(d) d = self.Dict({'a':1, 'B':2}) self.assertTrue(d) def test_items(self): self.assertDictEqual(self.d1, self.d2) self.assertEqual( [v for v in self.d1.items()], [v for v in self.d2.items()]) def test_json_dumps(self): s = json.dumps(self.d1) self.assertIn('a', s) self.assertIn('B', s) def test_keys(self): self.assertEqual(self.d1.keys(), self.d2.keys()) def test_values(self): self.assertEqual( [v for v in self.d1.values()], [v for v in self.d2.values()])
Я просто создал функцию для обработки этого:
def setLCdict(d, k, v): k = k.lower() d[k] = v return d myDict = {}так вместо
myDict['A'] = 1 myDict['B'] = 2вы можете:
myDict = setLCdict(myDict, 'A', 1) myDict = setLCdict(myDict, 'B', 2)затем вы можете либо в нижнем регистре значение, прежде чем искать его или написать функцию для этого.
def lookupLCdict(d, k): k = k.lower() return d[k] myVal = lookupLCdict(myDict, 'a')вероятно, не идеально, если вы хотите сделать это глобально, но хорошо работает, если его просто подмножество вы хотите использовать его.
Comments