我是Python新手,有点惊讶我不能这样做。
dictionary = {
'a' : '123',
'b' : dictionary['a'] + '456'
}
我想知道Pythonic在我的脚本中正确执行此操作的方法是什么,因为我觉得我不是唯一一个尝试过这样做的人。
编辑:有足够的人想知道我在做什么,所以这里有更多关于我的用例的细节。让我们说我想保持字典对象来保存文件系统路径。路径相对于字典中的其他值。例如,这就是我的一本词典。
dictionary = {
'user': 'sholsapp',
'home': '/home/' + dictionary['user']
}
重要的是,在任何时候我都可以更改dictionary['user']
,并让所有词典值反映出变化。同样,这是我正在使用它的示例,所以我希望它能传达我的目标。
根据我自己的研究,我认为我需要实施一个类来完成这项工作。
答案 0 :(得分:46)
不用担心创建新课程 - 您可以利用Python的字符串格式化功能 并简单地做:
class MyDict(dict):
def __getitem__(self, item):
return dict.__getitem__(self, item) % self
dictionary = MyDict({
'user' : 'gnucom',
'home' : '/home/%(user)s',
'bin' : '%(home)s/bin'
})
print dictionary["home"]
print dictionary["bin"]
答案 1 :(得分:13)
最近我没有做对象而出现了:
dictionary = {
'user' : 'gnucom',
'home' : lambda:'/home/'+dictionary['user']
}
print dictionary['home']()
dictionary['user']='tony'
print dictionary['home']()
答案 2 :(得分:8)
>>> dictionary = {
... 'a':'123'
... }
>>> dictionary['b'] = dictionary['a'] + '456'
>>> dictionary
{'a': '123', 'b': '123456'}
它工作正常,但是当你尝试使用dictionary
时,它尚未被定义(因为它必须首先评估该文字字典)。
但要小心,因为这会分配'b'
'a'
的密钥在分配时指定的值,并且不会进行查找每次。如果这正是您所寻找的,那么它可能会有更多的工作。
答案 3 :(得分:5)
您在编辑中描述的内容是INI配置文件的工作原理。 Python确实有一个名为ConfigParser的内置库,它应该适用于你所描述的内容。
答案 4 :(得分:5)
这是一个有趣的问题。格雷格似乎有一个good solution。但那没什么好玩的;)
jsbueno为very elegant solution,但仅适用于字符串(如您所要求的那样)。
'通用'自引用字典的技巧是使用代理对象。它需要一些(低估)代码来实现,但用法与你想要的一致:
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
并解析所有引用。
那是一个粗略的草图。如果您还有其他问题,请随时提出。
答案 5 :(得分:3)
如果你像我一样徘徊如何让@jsbueno snippet使用{}样式替换,下面是示例代码(虽然可能效率不高):
import string
class MyDict(dict):
def __init__(self, *args, **kw):
super(MyDict,self).__init__(*args, **kw)
self.itemlist = super(MyDict,self).keys()
self.fmt = string.Formatter()
def __getitem__(self, item):
return self.fmt.vformat(dict.__getitem__(self, item), {}, self)
xs = MyDict({
'user' : 'gnucom',
'home' : '/home/{user}',
'bin' : '{home}/bin'
})
>>> xs["home"]
'/home/gnucom'
>>> xs["bin"]
'/home/gnucom/bin'
我尝试使用% self
简单替换.format(**self)
,但事实证明它不适用于嵌套表达式(如上面列出的'bin',它引用了'home ',它有自己对'用户'的引用)因为评估顺序(**扩展在实际格式调用之前完成,并且没有像原始%版本那样延迟)。
答案 6 :(得分:2)
写一个类,也许是属性的东西:
class PathInfo(object):
def __init__(self, user):
self.user = user
@property
def home(self):
return '/home/' + self.user
p = PathInfo('thc')
print p.home # /home/thc
答案 7 :(得分:1)
作为@Tony's answer的扩展版本,你可以构建一个字典子类,如果它们是callables则调用它的值:
class CallingDict(dict):
"""Returns the result rather than the value of referenced callables.
>>> cd = CallingDict({1: "One", 2: "Two", 'fsh': "Fish",
... "rhyme": lambda d: ' '.join((d[1], d['fsh'],
... d[2], d['fsh']))})
>>> cd["rhyme"]
'One Fish Two Fish'
>>> cd[1] = 'Red'
>>> cd[2] = 'Blue'
>>> cd["rhyme"]
'Red Fish Blue Fish'
"""
def __getitem__(self, item):
it = super(CallingDict, self).__getitem__(item)
if callable(it):
return it(self)
else:
return it
当然,只有当你不打算将callable存储为值时,这才有用。如果你需要能够做到这一点,你可以将lambda声明包装在一个函数中,该函数将一些属性添加到生成的lambda中,并在CallingDict.__getitem__
中检查它,但此时它变得复杂,并且啰嗦,足以让你首先使用一个类来获取数据。
答案 8 :(得分:1)
这在懒惰的评估语言(haskell)中非常容易。
由于严格评估了Python,因此我们可以做一些技巧来使事情变得懒惰:
Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
d1 = lambda self: lambda: {
'a': lambda: 3,
'b': lambda: self()['a']()
}
# fix the d1, and evaluate it
d2 = Y(d1)()
# to get a
d2['a']() # 3
# to get b
d2['b']() # 3
从语法角度来看,这不是很好。这是因为我们需要使用lambda: ...
显式构造惰性表达式,并使用...()
显式评估惰性表达式。在需要严格注释的惰性语言中,这是相反的问题,在Python中,我们最终需要惰性注释。
我认为通过更多的元编程和更多的技巧,可以使以上内容更易于使用。
请注意,这基本上是let-rec在某些功能语言中的工作方式。
答案 9 :(得分:0)
Python 3中的jsbueno答案:
class MyDict(dict):
def __getitem__(self, item):
return dict.__getitem__(self, item).format(self)
dictionary = MyDict({
'user' : 'gnucom',
'home' : '/home/{0[user]}',
'bin' : '{0[home]}/bin'
})
print(dictionary["home"])
print(dictionary["bin"])
她的母羊使用带花括号{}
和.format()
方法的python 3字符串格式。