Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/30.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 当我需要一本自指词典时,我该怎么办?_Python_Dictionary - Fatal编程技术网

Python 当我需要一本自指词典时,我该怎么办?

Python 当我需要一本自指词典时,我该怎么办?,python,dictionary,Python,Dictionary,我是Python新手,有点惊讶我不能这么做 dictionary = { 'a' : '123', 'b' : dictionary['a'] + '456' } 我想知道在我的脚本中,用什么样的Pythonic方法才能正确地做到这一点,因为我觉得我不是唯一一个尝试这么做的人 >>> dictionary = { ... 'a':'123' ... } >>> dictionary['b'] = dictionary['a'] + '456'

我是Python新手,有点惊讶我不能这么做

dictionary = {
    'a' : '123',
    'b' : dictionary['a'] + '456'
}
我想知道在我的脚本中,用什么样的Pythonic方法才能正确地做到这一点,因为我觉得我不是唯一一个尝试这么做的人

>>> dictionary = {
... 'a':'123'
... }
>>> dictionary['b'] = dictionary['a'] + '456'
>>> dictionary
{'a': '123', 'b': '123456'}
编辑:有足够多的人想知道我在用这个做什么,所以这里有我的用例的更多细节。假设我想保留dictionary对象来保存文件系统路径。路径是相对于字典中的其他值的。例如,这就是我的一本字典的样子

dictionary = {
    'user': 'sholsapp',
    'home': '/home/' + dictionary['user']
}
重要的是,我可以随时更改
字典['user']
,并让所有字典值反映更改。同样,这是我使用它的一个例子,所以我希望它能传达我的目标

根据我自己的研究,我认为我需要实现一个类来实现这一点

>>> dictionary = {
... 'a':'123'
... }
>>> dictionary['b'] = dictionary['a'] + '456'
>>> dictionary
{'a': '123', 'b': '123456'}
它工作得很好,但是当您试图使用
字典时,它还没有被定义(因为它必须首先计算该文本字典)


但是要小心,因为这会在赋值时将
'a'
键引用的值赋值给
'b'
键,并且不会每次都进行查找。如果这就是你想要的,那是可能的,但需要更多的工作。

最近的我没有做任何事情:

dictionary = {
    'user' : 'gnucom',
    'home' : lambda:'/home/'+dictionary['user'] 
}

print dictionary['home']()
dictionary['user']='tony'
print dictionary['home']()

不必担心创建新类- 您可以利用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"]

编写一个类,可能是具有属性的类:

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 

您在编辑中描述的是INI配置文件的工作方式。Python确实有一个名为的内置库,它应该适用于您所描述的内容。

这是一个有趣的问题。格雷格似乎有一个好主意。但这并不有趣;)

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的问题时,我问我为什么要这样命名这些类

“代理”一词通常与代表其他内容相关,因此它似乎是合适的,因为
subscratedict
类就是这样做的:实例替换字典文本中的“self”引用。话虽如此,(除了有时直截了当的愚蠢之外)命名对我来说可能是编码中最困难的事情之一。如果你(或其他任何人)能建议一个更好的名字,我洗耳恭听

我将提供一个简短的解释。贯穿始终,
S
是指代理ICT的一个实例,
d
是真正的字典

  • 参考
    S[key]
    会触发
    S.\uu getitem\uu
    代理ICTentry(key)
    以放置在
    d

  • 当构造
    S[key]=subscratedictentry(key)
    时,它存储
    key
    。这将是进入
    d
    键,该
    subrogatedictEntry
    项作为其替代项的值

  • 返回
    S[key]
    后,它要么输入
    d
    ,要么对其执行一些操作。如果对其执行操作,它将触发相对的
    \uuuuuuuuuuuuuuuuuuuuuuu
    方法,该方法简单地存储执行操作的值和操作的名称,然后返回自身。我们无法实际解决该操作,因为尚未构造
    d

  • 在构造
    d
    之后,它被传递给
    S.resolve
    。此方法通过
    d
    循环查找
    subscratedICTentry
    的任何实例,并将其替换为对实例调用
    resolve
    方法的结果

  • subscratedictentry.resolve
    方法接收现在构造的
    d
    作为参数,并可以使用它在构造时存储的
    key
    的值来获取它作为代理的值。如果在创建后对其执行了操作,则已使用所执行操作的名称设置了
    op
    属性。如果该类有一个
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    方法,那么它有一个
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。因此,现在我们有了逻辑(self.op_uuresolve)和所有必要的值(self.value,self.storaged_uvalue),最终得到
    d[key]
    的真实值。因此,我们返回步骤4在字典中的位置

  • 最后,
    subscratedict.resolve
    方法返回
    d
    ,并解析所有引用


  • 那是一幅草图。如果您还有任何问题,请随时提问。

    作为的扩展版本,您可以构建一个字典子类,如果其值是可调用的,则调用其值:

    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
    

    当然,这只有在您实际上不打算将可调用项存储为值时才可用。如果需要这样做,可以将lambda声明封装在一个函数中,该函数向生成的lambda添加一些属性,并在调用dict中检查它。但是在这一点上,它变得越来越复杂和冗长,以至于可能更容易首先为数据使用类。

    ,就像我在思考如何使用{}样式的替换一样,下面是示例代码(可能效率不高):

    我试着用
    .format(**self)
    简单地替换
    %self
    ,使它工作起来,但结果是不起作用
    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
    
    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"])