Ну, другим способом может быть реализация следующего:
Attr
- абстракция для «значения». Нам это нужно, поскольку в Python нет «перегрузки присваивания» (простая парадигма get / set используется в качестве самой чистой альтернативы). Attr
также действует как "Наблюдаемый".
AttrSet
- это «Наблюдатель» в течение Attr
с, который отслеживает изменения их значений, в то же время эффективно действуя как словарь Attr
-к-чему-либо (person
в нашем случае).
create_with_attrs
- это фабрика, производящая нечто похожее на именованный кортеж, перенаправляющее доступ к атрибуту через предоставленные Attr
с, так что person.name = "Ivan"
эффективно возвращает person.name_attr.set("Ivan")
и заставляет AttrSet
s наблюдать это person
name
соответствующим образом переставляют свои внутренние органы.
код (проверено):
from collections import defaultdict
class Attribute(object):
def __init__(self, value):
super(Attribute, self).__init__()
self._value = value
self._notified_set = set()
def set(self, value):
old = self._value
self._value = value
for n_ch in self._notified_set:
n_ch(old_value=old, new_value=value)
def get(self):
return self._value
def add_notify_changed(self, notify_changed):
self._notified_set.add(notify_changed)
def remove_notify_changed(self, notify_changed):
self._notified_set.remove(notify_changed)
class AttrSet(object):
def __init__(self):
super(AttrSet, self).__init__()
self._attr_value_to_obj_set = defaultdict(set)
self._obj_to_attr = {}
self._attr_to_notify_changed = {}
def add(self, attr, obj):
self._obj_to_attr[obj] = attr
self._add(attr.get(), obj)
notify_changed = (lambda old_value, new_value:
self._notify_changed(obj, old_value, new_value))
attr.add_notify_changed(notify_changed)
self._attr_to_notify_changed[attr] = notify_changed
def get(self, *attr_value_lst):
attr_value_lst = attr_value_lst or self._attr_value_to_obj_set.keys()
result = set()
for attr_value in attr_value_lst:
result.update(self._attr_value_to_obj_set[attr_value])
return result
def remove(self, obj):
attr = self._obj_to_attr.pop(obj)
self._remove(attr.get(), obj)
notify_changed = self._attr_to_notify_changed.pop(attr)
attr.remove_notify_changed(notify_changed)
def __iter__(self):
return iter(self.get())
def _add(self, attr_value, obj):
self._attr_value_to_obj_set[attr_value].add(obj)
def _remove(self, attr_value, obj):
obj_set = self._attr_value_to_obj_set[attr_value]
obj_set.remove(obj)
if not obj_set:
self._attr_value_to_obj_set.pop(attr_value)
def _notify_changed(self, obj, old_value, new_value):
self._remove(old_value, obj)
self._add(new_value, obj)
def create_with_attrs(**attr_name_to_attr):
class Result(object):
def __getattr__(self, attr_name):
if attr_name in attr_name_to_attr.keys():
return attr_name_to_attr[attr_name].get()
else:
raise AttributeError(attr_name)
def __setattr__(self, attr_name, attr_value):
if attr_name in attr_name_to_attr.keys():
attr_name_to_attr[attr_name].set(attr_value)
else:
raise AttributeError(attr_name)
def __str__(self):
result = ""
for attr_name in attr_name_to_attr:
result += (attr_name + ": "
+ str(attr_name_to_attr[attr_name].get())
+ ", ")
return result
return Result()
С данными, подготовленными с
name_and_email_lst = [("John","email1@dot.com"),
("John","email2@dot.com"),
("Jack","email3@dot.com"),
("Hack","email4@dot.com"),
]
email = AttrSet()
name = AttrSet()
for name_str, email_str in name_and_email_lst:
email_attr = Attribute(email_str)
name_attr = Attribute(name_str)
person = create_with_attrs(email=email_attr, name=name_attr)
email.add(email_attr, person)
name.add(name_attr, person)
def print_set(person_set):
for person in person_set: print person
print
следующая последовательность фрагментов псевдо-SQL дает:
ВЫБЕРИТЕ идентификатор из электронной почты
>>> print_set(email.get())
email: email3@dot.com, name: Jack,
email: email4@dot.com, name: Hack,
email: email2@dot.com, name: John,
email: email1@dot.com, name: John,
ВЫБЕРИТЕ ИД ИЗ электронной почты, ГДЕ email = "email1@dot.com"
>>> print_set(email.get("email1@dot.com"))
email: email1@dot.com, name: John,
ВЫБЕРИТЕ идентификатор из электронной почты, ГДЕ электронная почта = "email1@dot.com" ИЛИ электронная почта = "email2@dot.com"
>>> print_set(email.get("email1@dot.com", "email2@dot.com"))
email: email1@dot.com, name: John,
email: email2@dot.com, name: John,
ВЫБЕРИТЕ идентификатор ОТ ИМЕНИ ГДЕ имя = "Джон"
>>> print_set(name.get("John"))
email: email1@dot.com, name: John,
email: email2@dot.com, name: John,
ВЫБЕРИТЕ ИД ИЗ ИМЕНИ, электронная почта ГДЕ имя = "Джон" И электронная почта = "email1@dot.com"
>>> print_set(name.get("John").intersection(email.get("email1@dot.com")))
email: email1@dot.com, name: John,
ОБНОВЛЕНИЕ электронной почты, имя SET email = "jon@dot.com", имя = "Jon"
ГДЕ ИДЕНТИФИКАТОР
ВЫБЕРИТЕ идентификатор из электронной почты, ГДЕ email = "email1@dot.com"
>>> person = email.get("email1@dot.com").pop()
>>> person.name = "Jon"; person.email = "jon@dot.com"
>>> print_set(email.get())
email: email3@dot.com, name: Jack,
email: email4@dot.com, name: Hack,
email: email2@dot.com, name: John,
email: jon@dot.com, name: Jon,
УДАЛИТЬ ИЗ электронной почты, имя ГДЕ id =% s
ВЫБЕРИТЕ идентификатор из электронной почты
>>> name.remove(person)
>>> email.remove(person)
>>> print_set(email.get())
email: email3@dot.com, name: Jack,
email: email4@dot.com, name: Hack,
email: email2@dot.com, name: John,