подставлять значения других элементов в качестве подстрок - PullRequest
1 голос
/ 10 октября 2019

В словаре

{'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}'}

как бы вы выполнили подстановку, производящую

{'A':'x', 'B':'y', 'C':'x_foo', 'D':'bar_y'}

? Нет вложений, таких как 'C':'{A}_foo', 'D':'oops_{C}', нет рекурсии.

(Это сокращенный пример. В действительности это должно происходить строка за строкой в ​​кадре данных панд)

Ответы [ 2 ]

3 голосов
/ 10 октября 2019

При понимании списка на d.items мы можем применить val.format(**d) к каждому значению, чтобы интерполировать значения с самим диктом:

>>> d = {'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}'}
>>> o = dict([ (key, val.format(**d)) for key,val in d.items() ])
>>> print (o)
{'A': 'x', 'B': 'y', 'C': 'x_foo', 'D': 'bar_y'}
3 голосов
/ 10 октября 2019

Просто используйте форматирование строки Python и разверните в качестве аргументов d1 dict:

d1={'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}'}
d2={k:v.format(v,**d1) for k,v in d1.items()}

>>> d2
{'A': 'x', 'B': 'y', 'C': 'x_foo', 'D': 'bar_y'}

Однако здесь есть потенциал ключевых ошибок. Обратите внимание:

>>> d1={'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}', 'E':'no-key_{Z}'}
>>> d2={k:v.format(v,**d1) for k,v in d1.items()}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
KeyError: 'Z'

Если вы хотите корректно обрабатывать ключевые ошибки, вы можете создать подкласс Formatter следующим образом:

import string
class PartialFormatter(string.Formatter):
    def __init__(self, missing='~~', bad_fmt='!!'):
        self.missing, self.bad_fmt=missing, bad_fmt

    def get_field(self, field_name, args, kwargs):
        # Handle a key not found
        try:
            val=super(PartialFormatter, self).get_field(field_name, args, kwargs)
            # Python 3, 'super().get_field(field_name, args, kwargs)' works
        except (KeyError, AttributeError):
            val=None,field_name 
        return val 

    def format_field(self, value, spec):
        # handle an invalid format
        if value==None: return self.missing
        try:
            return super(PartialFormatter, self).format_field(value, spec)
        except ValueError:
            if self.bad_fmt is not None: return self.bad_fmt   
            else: raise

d1={'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}', 'E':'no-key_{Z}'}
d2={}

fmt=PartialFormatter()
for k,v in d1.items():
    if '{' in v:
        d2[k]=fmt.format(v,**d1)
    else:
        d2[k]=v   

Тогда вы просто молча получаете ~~ вместо KeyError:

>>> d2
{'A': 'x', 'B': 'y', 'C': 'x_foo', 'D': 'bar_y', 'E': 'no-key_~~'}

Который может быть изменен в соответствии с желаемым вами поведением ...


Или, что еще лучше, используйте try / except:

d1={'A':'x', 'B':'y', 'C':'{A}_foo', 'D':'bar_{B}', 'E':'bad-fmt_{:B}', 'F':'no-key_{Z}'}
d2={}

for k,v in d1.items():
    if '{' in v:
        try:
            d2[k]=v.format(v,**d1)
        except (KeyError, ValueError):
            d2[k]=v 
    else:
        d2[k]=v    

>>> d2
{'A': 'x', 'B': 'y', 'C': 'x_foo', 'D': 'bar_y', 'E': 'bad-fmt_{:B}', 'F': 'no-key_{Z}'}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...