4

Lets say I have a dictionary and list like the below:

l: list = [1,2,3,4]
d: dict = {"Hello": "World"}

I want to copy this dictionary exactly len(l) times.

I want to eventually create a list of tuples that looks like:

[(1, {"Hello": "World"}),(2, {"Hello": "World"}),(3, {"Hello": "World"}),(4, {"Hello": "World"})]

To create this I imagine I could do:

output: list = list(zip(l, repeated_dicts))

but need to replicate the dictionary the specified number of times in the list.

I tried using itertools.islice and itertools.cycle but couldn't quite get it. Any ideas?

BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
Coldchain9
  • 1,373
  • 11
  • 31
  • 2
    perhaps, the important question here is whether you intend to modify these dictionaries, and whether these changes should be reflected in all copies – Marat Jun 08 '22 at 01:20
  • 1
    @Marat Good point. I am actually just using them as a lookup to a multiprocessing Pool. E.g. the first element of each tuple will be a param and the 2nd might be say the headers of an web API call, for example. – Coldchain9 Jun 08 '22 at 01:22

5 Answers5

3

You're looking for itertools.repeat():

Make an iterator that returns object over and over again. Runs indefinitely unless the times argument is specified.

Note that the dictionaries returned by itertools.repeat() will share the same memory; changes to one dictionary will be shared across all of the elements of the list. That can lead to some pretty confusing behavior.

To avoid this, you can use map() with copy.copy() (if everything in the dictionary is immutable) or copy.deepcopy() to ensure that the memory for each dictionary is separate:

from itertools import repeat
from copy import copy

lst = [1, 2, 3, 4]
d = {"Hello": "World"}

list(zip(lst, map(copy, repeat(d))))

This outputs:

[
 (1, {'Hello': 'World'}), (2, {'Hello': 'World'}),
 (3, {'Hello': 'World'}), (4, {'Hello': 'World'})
]
BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
3

If you need to perform an actual copy, you cannot simply use the same d N times, otherwise all elements of your list would refer to the same object (and modifying any of them would modify all the elements).

In your example, .copy() with a generator expression should suffice:

l = [1,2,3,4]
d = {"Hello": "World"}
output = list((x, d.copy()) for x in l)

You will need copy.deepcopy if the dictionary also contains other complex objects like sets/lists/classes/dictionaries:

from copy import deepcopy

output = list((x, deepcopy(d)) for x in l)
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
3

Given that these dictionaries are never modified, copy/deepcopy is not necessary. The simplest way to do this (also, least overhead) is:

output = [(i, d) for i in l]
Marat
  • 15,215
  • 2
  • 39
  • 48
  • Extremely clever... all of the answers so far have been great but this is so clean. Is there any downside to this? – Coldchain9 Jun 08 '22 at 01:32
  • @Coldchain9 If you don't see the downside, then that's a downside :-) – Kelly Bundy Jun 08 '22 at 01:48
  • @KellyBundy Care to elaborate? – Coldchain9 Jun 08 '22 at 01:56
  • 1
    @Coldchain9 The possible dangers of using the same object that other answers already explained. If that truly doesn't matter in your case, then good, but if you're not even aware of the issue or don't understand it (as your comment question indicates), then this unawareness might still bite you in other cases. – Kelly Bundy Jun 08 '22 at 02:00
  • @KellyBundy I was "knowingly unaware" of it because these dictionaries arent going to change in my use case. I just need them as headers to feed in to a parallel process. I do appreciate your comment though. – Coldchain9 Jun 08 '22 at 02:02
  • @Coldchain9 Ah, ok, and I think I somewhat understand your (not shown) usage now. Btw the reason everybody else copied the dict is... because you asked them to. The very first word in your title is "Copy" and you repeat it in the question as well. – Kelly Bundy Jun 08 '22 at 02:07
  • check this out : https://www.mycompiler.io/view/JgeW9FySeSm – Jupri Jun 08 '22 at 02:15
  • 1
    @Jupri yes, this is expected behavior. As discussed in the comments to the question, the dictionary is not expected to be modified. It is also explicitly stated in the answer: "Given that these dictionaries are never modified, ..." – Marat Jun 08 '22 at 02:23
1

It seems you're only copying the dictionary once in your example. Perhaps you can use a list comprehension with tuples?

l = [1, 1, 1]
d = {"Hello": "World"}

output = [(i, d.copy()) for i in range(1, len(l)+1)]
>>> output
[(1, {'Hello': 'World'}), (2, {'Hello': 'World'}), (3, {'Hello': 'World'})]

Or for each number in the list:

>>> l = [1, 1, 1]
>>> [(i, d.copy()) for i in l]
[(1, {'Hello': 'World'}), (1, {'Hello': 'World'}), (1, {'Hello': 'World'})]

Or copying the list i times:

>>> l = [1, 2, 1]
>>> [(i, [d.copy() for j in range(i)]) for i in l]
[(1, [{'Hello': 'World'}]),
 (2, [{'Hello': 'World'}, {'Hello': 'World'}]),
 (1, [{'Hello': 'World'}])]
Freddy Mcloughlan
  • 4,129
  • 1
  • 13
  • 29
1
 l: list = [1,2,3,4]
 d: dict = {"Hello": "World"}
 res = []
 for num in l:
   res.append((num, d.copy()))


>>>(res)
[(1, {'hello': 'World'}), (2, {'hello': 'World'}), (3, {'hello': 'World'})]