Проанализируйте список yaml и сохраните его в переменных на python

#python #yaml

Вопрос:

У меня есть файл yaml, который я хотел бы повторить и сохранить все из списка как новую переменную с тем же именем.

 - app1:
   name: example1
   host: example1
   run: myscript1.sh
   tags: 
    - "user"  
    - "age"  
    - "gender"             
 
- app2:
   name: example2
   host: example4
   tags: 
    - "user"  
    - "age"  
    - "height"   
 

Код:

 for i in range(0, len(data)):
    for key, value in data[i].items():
        print(value.get('tags'))
        for n in tags:
            print(n)
 

выход:

 ['user', 'age', 'gender']
user
age
user
['user', 'age', 'height']
user
age
height
 

Хотелось бы в новой итерации сохранить эти значения в качестве переменных,

 user = 'user'
age = 'age'
height = 'height'
 

и в следующей итерации:

   user = 'user'
  age = 'age'
  gender = 'gender'
 

Таким образом, я могу передать эти переменные некоторым другим элементам кода.

Редактировать

переменная yaml (например, хост) будет заполнена значениями из Elasticsearch. Если мы определим в тегах «возраст», я буду искать в Elasticsearch «возраст» и извлекать значение из документа, чтобы оно стало «возрастом»: «20». После этого этот тег «возраст» будет вызван в скрипте sh, что-то вроде:

 for doc1 in resp['hits']['hits']:
            command = f"{value.get('run')} --age {age} #and other tags"
            p = subprocess.Popen(command.split(), shell=False, stdout=subprocess.PIPE)
 

Ответ №1:

Я не совсем уверен, чего вы хотите, но я думаю, что вам нужно что-то вроде этого:

 # test.yml
- app1:
   name: example1
   host: example1
   tags: 
    - "user"  
    - "age"  
    - "gender"             
 
- app2:
   name: example2
   host: example4
   tags: 
    - "user"  
    - "age"  
    - "height"   
 
 import yaml
from pathlib import Path

with Path("test.yml").open() as f:
    data = yaml.load(f)

out = []

for entry in data:
    app, *_ = entry.keys()
    tags = {k: k for k in entry[app]["tags"]}
    out.append(tags)

print(out)
 

Вместо этого вы можете легко сохранить их по названию приложения, оформив диктант.

Обратите внимание, что я не знаю, почему вам нужны избыточные пары ключ/значение, и если tags на самом деле предполагается, что они содержат данные (в отличие от вашего примера), вам придется сделать что-то вроде:

 tags = {k:v for k, v in entry[app]["tags"].items()}
 

Наконец, вы, конечно, можете вернуться в yaml с yaml.dump(out) помощью .

** Распаковка

Мне приходит в голову, что вы, возможно, захотите распаковать этот диктант в функцию. Вы можете сделать это вот так:

 def my_fn(user=None, age=None, gender=None, height=None):
    print("user", user, "age", age, "gender", gender, "height", height)

out = [{'user': 'user', 'age': 'age', 'gender': 'gender'},
 {'user': 'user', 'age': 'age', 'height': 'height'}]

for row in out:
    my_fn(**row)
 

Таким образом, * * распаковывает диктант в аргументы ключевых слов, которые могут быть тем, что вы искали. (Во всяком случае, для меня это имеет больше смысла, чем генерирование переменных с программными именами). Обратите app, *_ внимание на то, что распаковано entry.keys() , чтобы мы могли получить первый элемент без необходимости приводить его к индексируемому типу. dict.keys() это не генератор, так что вы не можете им пользоваться next(dict.keys()) . Эта конкретная распаковка является недавним (и очень полезным) дополнением к python.

ИЗМЕНИТЬ: использование этих переменных

переменная yaml (например, хост) будет заполнена значениями из Elasticsearch. Если мы определим в тегах «возраст», я буду искать в Elasticsearch «возраст» и извлекать значение из документа, чтобы оно стало «возрастом»: «20».

Верно. Поэтому вы хотите посмотреть , в каких записях tags содержатся данные, а затем заполнить их правильными значениями. Итак, вы хотите сделать что-то вроде этого:

 for entry in data:
    tags = {}
    app, *_ = entry.keys()
    for tag in in entry[app]["tags"]:
        val = get_tag_value_from_elasticsearch(tag)
        tags[tag] = val
 

После этого этот тег «возраст» будет вызван в скрипте sh

Так что—все еще в том же цикле для простоты, но вы всегда можете добавить tags в список, а затем повторить этот список позже:

     cmd = ["command"]
    for param, val in tags.items():
        cmd  = [f"--{param}", str(val)]
    res = subprocess.run(cmd, capture_output=True)
    print(res.stdout)
 

Обратите внимание на несколько вещей здесь: я избегал использования .get() для поиска значения в дикте, когда я могу просто использовать [] доступ; Я использовал subprocess.run вместо Popen (если вы хотите создать фон, вам придется вернуться к Popen), и я создал команду непосредственно в списке, а не строил строку и разделял ее.

На данный момент все, что вы хотите с этим сделать, — это, я думаю, еще один вопрос, поэтому я оставлю этот ответ здесь.

Комментарии:

1. Привет, спасибо за ваши вводы, дело в том, что мне нужно это сделать, потому что на основе параметров, определенных в yaml, я выполняю поисковый запрос в elasticsearch, и мне нужно сохранить эти «конкретные» значения в качестве переменных. Например, я буду искать рост и пол, а затем сохраню эти 2 -> после этого мне нужно вызвать какой-нибудь скрипт sh с этими параметрами. Не уверен, что я ясно выразился, попытаюсь объяснить в следующем комментарии

2. Посмотрите, что я делаю в последней части (попробуйте запустить это). Я думаю, вы можете получить необходимые вам данные. Но это определенно помогло бы, если бы вы привели пример того, как вы хотите использовать эти данные в вопросе. Я сохраняю данные в переменной-в частности, в dict, которую затем можно распаковать в функцию при вызове.

3. Thnx @2e0byo , я внес правки, надеюсь, теперь вы понимаете, что я делал

4. Верно, я обновил этот ответ (но он настолько невеселый, что я собираюсь оставить его там). Надеюсь, это даст вам инструменты для ее решения 🙂

5. tnx, просто получаю ошибку имени: имя «get_tag_value_from_elasticsearch» не определено, я не уверен, что это такое