quotable7
quotable7

Reputation: 635

How to convert a dictionary into a subscriptable array?

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

Answers (6)

Kasravnd
Kasravnd

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:

  1. Dynamic View: It reflects real-time changes to the dictionary without creating a separate copy of the values.

  2. Memory Efficiency: As a view, it doesn't create a new list of values, saving memory.

  3. 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:

  1. Embedded Values: For small dictionaries, values might be stored directly in the object structure, improving access speed and memory usage.

  2. Capacity Management: The capacity field allows for efficient resizing operations.

  3. Valid Flag: Quick state checking for certain operations or consistency checks.

  4. 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

freshNfunky
freshNfunky

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

cottontail
cottontail

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

Devansh Bansal
Devansh Bansal

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

sharad jain
sharad jain

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

txsaw1
txsaw1

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

Related Questions