Dict() Syntax error

Hi all,

Q:

How would you correct this script’s syntax error?

keys = {'key1': x1 for x1 in range(7), 'key2': x2to4 for x2to4 in range(7), 'key3': x5 for x5 in range(7)}

print(keys)

What is it supposed to do?

If it’s supposed to have a range for each value, just use the range - don’t try to iterate over it.

Unfortunately Python’s built-in error messages usually don’t (often probably cannot) give hints about how to fix a SyntaxError.

>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1
    [x, x**2 for x in range(6)]
     ^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
>>> [x1 for x1 in range(7), x2 for x in range(7)]
  File "<stdin>", line 1
    [x1 for x1 in range(7), x2 for x in range(7)]
                          ^
SyntaxError: invalid syntax

If you just want the range as value, you could do (as Matthew suggested):

keys = {'key1': range(7), 'key2': range(7), 'key3': range(7)} 

If you want particular lists, you could do:

keys = {'key1': list(range(7)), 
        'key2': [x for x in range(7) if 2 <= x < 4]} 

Main thing is that list comprehensions need to be enclosed by square brackets to make a list.
(…Or by round brackets if you just wanted to get the generator expression.)

2 Likes

Thanks all,

@hansgeunsmeyer Your scripts make sense to me.

Another question:

What would you write after the if() statement in your final script?

keys = {'key1': list(range(7)), 
        'key2': [x for x in range(7) if 2 <= x < 4]}

"if … " is not a statement here, it’s a conditional expression, conditioning the list comprehension

Not sure you what you mean. In this case you would probably only use conditionals on x (assuming that’s the only variable in play here), so you would have sth like

# selecting all even integers between 2 and 16
[x for x in my_range if 2 <= x < 16 and x & 1 == 0]  

You could of course use other variables inside the ‘if’ expression if those would be make sense.
Anyway it all depends on what you want, but this kind of code can quickly become unreadable (-> unmanageable, hard to debug) if it becomes too complex.

1 Like

Now, … imagine creating a def() and return() back to an if() conditional statement within a dict() function, which could produce a loop similar to Python’s for() loop. What do you think?

Another question:

Can the print() function be included within the dict() function? Or, can output from the dict() function be rendered within the dict() function?

Sure, the print function can be used - But it doesn’t make much sense to use it:

>>> dd = dict(k=print("abc"))
abc
>>> dd
{'k': None}

A nice way to render a dict is to use json:

import json
>>> dd = dict(a=1, b=2, c=dict(A=11, B=22))
>>> print(json.dumps(dd, indent=2))
{
  "a": 1,
  "b": 2,
  "c": {
    "A": 11,
    "B": 22
  }
}

Btw I’m not sure what you mean by “rendering the output from dict() within the dict()”. It is possible to add a self-reference as value inside a dict, but that makes the dict (as a whole) unrenderable, since it will then have a circular reference.

>>> dd["self"] = dd
>>> dd["self"] == dd
True
>>> dd
{'a': 1, 'b': 2, 'c': {'A': 11, 'B': 22}, 'self': {...}}
2 Likes

Thank you.
Very informative and insightful dialogue. :slightly_smiling_face:

  1. Simply don’t want to waste time on typing the print() statement to check logic.
  2. Now, this problem becomes minor.

Thank you again.

Alternatively it can be done with using dict.fromkeys if value is not mutable:

>>> d = dict.fromkeys(['key1', 'key2', 'key3'], range(7))
>>> d
{'key1': range(0, 7), 'key2': range(0, 7), 'key3': range(0, 7)}

Function can be value in dictionary:

>>> d = {'p': print}
>>> d['p']('Hello world')
Hello world
>>> p = d['p']
>>> p('Hello world')
Hello world
2 Likes

I apologize; I have more questions…


Would you help me detect the cause of the errors? ( “name ‘b’ is not defined”?? really?)

Update:
Get it now about the error.

Q: (Solved)

Is it possible to rewrite the code in dict() format rather than {}? (I personally think that it’s not possible… Maybe a dict() within a dict() works…)

a = dict(b = print)
a[b]('cool')

####

NameError: name 'b' is not defined

asd = {'a': print, 'b': range}

asd['a']['b']('cool')(20)

asd['a']('cool')]['b'](20)

###

TypeError: 'builtin_function_or_method' object is not subscriptable
>>> a = dict(b = print)

b is a keyword argument.

>>> a[b]('cool')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined

b is a variable. If you haven’t defined it then it doesn’t exist.

>>> asd = {'a': print, 'b': range}
>>> asd
{'a': <built-in function print>, 'b': <class 'range'>}
>>> asd['a']
<built-in function print>
>>> asd['a']['b']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'builtin_function_or_method' object is not subscriptable

You’re trying to subscript a function.

1 Like

Thanks.

print() is a function and therefore cannot be an argument.

The argument is singular, and the function must establish a connection.

When you write dict(b = print), you are calling dict in order to create a dict, which means you use the same syntax as for functions. In this syntax, the b is a keyword argument. But when you write a[b], the b is an ordinary variable name.

The code dict(b = print) made a dictionary with one key, where the string 'b' is that key, and the print function is the value. So we must write a['b'] to access it.

Doing asd['a'] gives us the print function. We can’t subscript that again with 'b' or anything else. If you want something to be printed, then you should call the function that you just got out of the dictionary - e.g. asd['a']('cool'), like you show. If you want to pass more arguments for that call, then they go in between the (), just like they ordinarily would for any other function call.

If you want to make a range(20) by looking up range inside the dict first, then the same logic applies: we end up with asd['b'](20).

If you want to print the range, then that whole thing that created the range - asd['b'](20) - is what you pass when calling print. So, we simply substitute that in: asd['a'](asd['b'](20)). See how asd['b'](20) takes the place of the 'cool' string?

But this result is perhaps not what you want:

>>> asd = {'a': print, 'b': range}
>>> asd['a'](asd['b'](20))
range(0, 20)

It just shows a representation of the range object, itself. It doesn’t show you the numbers in that range.


There are a lot more ideas that can be explained here, but the simplest route to the goal is a little syntax trick:

>>> asd['a'](*asd['b'](20))
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

The * means to take every value that is in the sequence asd['b'](20), and use them as separate arguments for calling print. The print function lets you pass as many arguments as you want, and prints some representation of each one, one after the other. By default, it separates them with spaces, and adds a newline at the end, as you see here. You can also change those results with keyword arguments:

>>> # Let's show it without using the dict, to focus on the new idea
>>> print(*range(20), sep=':')
0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19
>>> print(*range(20), end=', oops, I almost forgot 20\n')
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19, oops, I almost forgot 20

If the end doesn’t have a newline, you may run in to some problems, although it can still be very useful:

1 Like

OMG… That’s too much information…