Reputation: 635
I know how to convert a dictionary into a list in Python, but somehow when I try to get, say, the sum of the resulting list, I get the error 'dict_values' object is not subscriptable
. Also, I plan to sum only several items in the list.
dict = {A:1, B:2, C:3, D:4}
arr = dict.values()
the_sum = sum(arr[1:3])
Upon closer inspection, I noticed that when the resulting list is printed out, it always gives dict_values(......)
as the output which I can't remove. How do I get around this?
Upvotes: 63
Views: 122277
Reputation: 107347
In Python 3, dict.values()
returns a dict_values
object, which is a view object providing a dynamic representation of the dictionary's values. This differs from Python 2, where it returned a list. The dict_values
object offers several advantages and characteristics such as the followings:
Dynamic View: It reflects real-time changes to the dictionary without creating a separate copy of the values.
Memory Efficiency: As a view, it doesn't create a new list of values, saving memory.
No Indexing or Slicing: Unlike lists, dict_values
objects don't support indexing or slicing operations.
While dict_values
objects don't have specific optimizations for membership checking, the underlying PyDictValues
structure in CPython contributes to efficient dictionary operations:
typedef struct {
uint8_t size;
uint8_t embedded : 1;
uint8_t valid : 1;
uint8_t capacity;
PyObject *values[1];
} PyDictValues;
Key optimizations in the CPython implementation include:
Embedded Values: For small dictionaries, values might be stored directly in the object structure, improving access speed and memory usage.
Capacity Management: The capacity
field allows for efficient resizing operations.
Valid Flag: Quick state checking for certain operations or consistency checks.
Size Limitation: The assertion assert(size < 256)
suggests an upper limit on the number of embedded values, balancing between optimization and general use cases.
If you need list-like functionality, you can convert a dict_values
object to a list:
d = {'a': 1, 'b': 2, 'c': 3}
values_list = list(d.values())
print(sum(values_list[1:]))
This conversion creates a new list object, allowing for indexing and slicing, but at the cost of additional memory usage and losing the dynamic view property.
Upvotes: 90
Reputation: 69
Using list(dictObj.values())
to wrap a dict_values
object is not always successful, especially if the resulting dict_values
list contains different data.
It will throw the Error:
{TypeError} 'generator' object is not callable
If you receive this error try to generate a iterable list using a for comprehension:
[x for x in dictObj.values()]
It will do the job
Upvotes: 0
Reputation: 23321
Creating a new list using list()
constructor, only to slice it is wasteful. Using islice
from the built-in itertools
module will be more efficient.
from itertools import islice
dct = {'A':1, 'B':2, 'C':3, 'D':4}
arr = dct.values()
the_sum = sum(islice(arr, 1, 3))
the_sum
# 5
The difference is efficiency is noticeable especially if the dictionary is very large but the slice is small relative to it. For example, for a dictionary of 10mil key-value pairs, if you're slicing 20 pairs and summing their values, islice
is ~39400x faster than constructing a list and slicing it.
n = 10_000_000
dct = dict(zip(range(n), [0,1,2,3,4]*(n//5)))
%timeit sum(list(dct.values())[10:30])
# 120 ms ± 813 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit sum(islice(dct.values(), 10, 30))
# 3.04 µs ± 19.4 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
Even if the slice is large, islice
is still faster (though not as dramatic as a small slice)
%timeit sum(list(dct.values())[1:n])
# 277 ms ± 2.24 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit sum(islice(dct.values(), 1, n))
# 181 ms ± 4.9 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
Upvotes: 2
Reputation: 177
By assigning dict.values()
to a list you are not converting it to a list; you are just storing it as dict_values (may be an object). To convert it write value_list=list(dict.values())
. Now even while printing the value_list
you will get the list elements and not dict_values(......)
.
And as mentioned before don't use Python built-in names as your variable names; it may cause conflicts during execution and confusion while reading your code.
Upvotes: 12
Reputation: 9
@mgilson is right. A dictionary , by its intrinsic nature, isn't ordered.
Still you can do this :
alpha = {"A":1,"B":2,"C":3,"D":4,"E":5} # dictionary
my_list = []
for key,value in alpha.items() :
my_list.append(value)
You can access your "values" from my_list
, but it will not be in order.
Upvotes: 0
Reputation: 121
Yes, it did appear like a list on the older Python questions asked here. But as @Kasramvd said, assuming you are using python 3.X, dict.values
is a dictionary view object.
(Also, you definitely came up with this example hastily as you have four dictionary entries but want 10 list items, which is redundant.)
Upvotes: 1