Это интересная проблема.Похоже, у Грега есть хорошее решение .Но это не весело;)
jsbueno как очень элегантное решение , но это относится только к строкам (как вы и просили).
Трюк с «общим» яСсылочный словарь должен использовать суррогатный объект.Требуется несколько (преуменьшенных) строк кода, но использование соответствует тому, что вы хотите:
S = SurrogateDict(AdditionSurrogateDictEntry)
d = S.resolve({'user': 'gnucom',
'home': '/home/' + S['user'],
'config': [S['home'] + '/.emacs', S['home'] + '/.bashrc']})
Код для этого не так уж и короток.Он живет в трех классах:
import abc
class SurrogateDictEntry(object):
__metaclass__ = abc.ABCMeta
def __init__(self, key):
"""record the key on the real dictionary that this will resolve to a
value for
"""
self.key = key
def resolve(self, d):
""" return the actual value"""
if hasattr(self, 'op'):
# any operation done on self will store it's name in self.op.
# if this is set, resolve it by calling the appropriate method
# now that we can get self.value out of d
self.value = d[self.key]
return getattr(self, self.op + 'resolve__')()
else:
return d[self.key]
@staticmethod
def make_op(opname):
"""A convience class. This will be the form of all op hooks for subclasses
The actual logic for the op is in __op__resolve__ (e.g. __add__resolve__)
"""
def op(self, other):
self.stored_value = other
self.op = opname
return self
op.__name__ = opname
return op
Далее идет конкретный класс.достаточно просто.
class AdditionSurrogateDictEntry(SurrogateDictEntry):
__add__ = SurrogateDictEntry.make_op('__add__')
__radd__ = SurrogateDictEntry.make_op('__radd__')
def __add__resolve__(self):
return self.value + self.stored_value
def __radd__resolve__(self):
return self.stored_value + self.value
Вот последний класс
class SurrogateDict(object):
def __init__(self, EntryClass):
self.EntryClass = EntryClass
def __getitem__(self, key):
"""record the key and return"""
return self.EntryClass(key)
@staticmethod
def resolve(d):
"""I eat generators resolve self references"""
stack = [d]
while stack:
cur = stack.pop()
# This just tries to set it to an appropriate iterable
it = xrange(len(cur)) if not hasattr(cur, 'keys') else cur.keys()
for key in it:
# sorry for being a duche. Just register your class with
# SurrogateDictEntry and you can pass whatever.
while isinstance(cur[key], SurrogateDictEntry):
cur[key] = cur[key].resolve(d)
# I'm just going to check for iter but you can add other
# checks here for items that we should loop over.
if hasattr(cur[key], '__iter__'):
stack.append(cur[key])
return d
В ответ на вопрос gnucoms о том, почему я назвал классы так, как я это сделал.
Слово «суррогат» обычно ассоциируется с заменой чего-то другого, поэтому оно кажется уместным, потому что именно так поступает класс SurrogateDict
: экземпляр заменяет ссылки «self» в литерале словаря.Это, как говорится, (кроме того, чтобы быть просто глупым иногда) именование, вероятно, одна из самых сложных вещей для меня в кодировании.Если вы (или кто-либо еще) можете предложить более подходящее имя, я весь слух.
Я приведу краткое объяснение.В течение S
относится к экземпляру SurrogateDict, а d
- настоящий словарь.
Ссылка S[key]
вызывает S.__getitem__
и SurrogateDictEntry(key)
для размещения в d
.
Когда построено S[key] = SurrogateDictEntry(key)
, оно хранит key
.Это будет key
в d
для значения, для которого эта запись SurrogateDictEntry
выступает в качестве суррогата.
После возврата S[key]
это либовведен в d
, или на нем выполнены некоторые операции.Если над ним выполняется операция, он запускает относительный метод __op__
, который просто сохраняет значение, над которым выполняется операция, и имя операции, а затем возвращает себя.На самом деле мы не можем разрешить операцию, потому что d
еще не было построено.
После создания d
оно передается S.resolve
.Этот метод перебирает d
, находя все экземпляры SurrogateDictEntry
и заменяя их результатом вызова метода resolve
для этого экземпляра.
Метод SurrogateDictEntry.resolve
получаеттеперь строится d
в качестве аргумента и может использовать значение key
, которое оно хранило во время построения, чтобы получить значение, для которого оно действует как суррогат.Если операция была выполнена над ним после создания, атрибут op
будет установлен с именем операции, которая была выполнена.Если у класса есть метод __op__
, то у него есть метод __op__resolve__
с реальной логикой, которая обычно была бы в методе __op__
.Так что теперь у нас есть логика (self. op__resolve ) и все необходимые значения (self.value, self.stored_value), чтобы наконец получить реальное значение d[key]
.Таким образом, мы возвращаем то, что шаг 4 помещает в словарь.
наконец, метод SurrogateDict.resolve
возвращает d
с разрешением всех ссылок.
Это грубый набросок.Если у вас есть еще вопросы, не стесняйтесь спрашивать.