简体   繁体   中英

How to add properties to a list of dicts via a list comprehension in python3?

I have a list of dicts. For each dict, I need to pass one of its properties to a function, and then assign a new property based on the result of that function.

For example: I have a list of pages of a site. I need to loop through them and, based on the URL, assign the author name to a property in the dict.

for index, page in enumerate(pages):
    pages[index]['author'] = get_author(page['url'])

This works. But's cluttered and doesn't feel pythonic. pages[index] feels like a thing that I shouldn't have to do in Python.

Is there a way to do this via a list comprehension? Or some other more pythonic way?

pages = [??? for page in pages]

You could use such a list comprehension:

result = [{**page, 'author': get_author(page['url'])} 
          for page in pages]

# This works too:

result = [dict(**page, author=get_author(page['url'])) 
          for page in pages]

# but is less preferred because it will fail for input containing non-string keys

This creates a new dict for each original dict with an extra key, author , based on the value of get_author as applied to the value corresponding to the url key.

Note that it does not modify the original list .

Example:

def get_author(i):
    if i == 1:
        return 'hello'

    else:
        return 'bye'

pages = [{'url': 1},
         {'url': 2}]

result = [{**page, **{'author': get_author(page['url'])}} for page in pages]
print(result)

Output:

[{'url': 1, 'author': 'hello'}, {'url': 2, 'author': 'bye'}]

A list comprehension builds a list from an existing list. In your case, you want to update existing list items. A list comprehension is inappropriate for this job.

Your solution can be somewhat improved, though:

for page in pages:
    page['author'] = get_author(page['url'])

I can see two "pythonic" solutions:

  • Creating a new dict out of unpacked page and the new value of 'author':
pages = [{**p, 'author': get_author(p['url'])} for p in pages]
  • A little trick using 'or' operator:
pages = [p.update(author=get_author(p['url'])) or  p for p in pages]

Since p.update() returns None, None or p will always be the updated version of p.

The first one seems to be more readable but I believe that the second one beats the first one in performance.

我不确定,但是以下方法可能有效:

[pages[index]['author'] = get_author(page['url']) for index, page in enumerate(pages)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM