Is there a way to create a dict object with a space in the key?
# This way works
>>> d = {'a b': 1}
>>> d
{'a b': 1}
# Is it possible to create the same using this method?
>>> d = dict('a b'=1)
File "<stdin>", line 1
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
No, this manner of constructing a dictionary cannot handle a key with a space.
https://docs.python.org/3/library/stdtypes.html#dict shows numerous methods for constructing dictionaries. The first one is what you're trying to do:
a = dict(one=1, two=2, three=3)
But the following note says:
Providing keyword arguments as in the first example only works for
keys that are valid Python identifiers.
A string value, as you're trying, is not a valid identifier. And an identifier cannot includes spaces, so a b without quotes will not work either.
Related
I've just recently started learning programming in python as a beginner. I was wondering: are keys in dictionary also treated as variables?
students = {"Jake": 12, "Rachel":12, "Ross":15}
the code contains the student names and their age.
For example: Is "Jake" a variable that contains the value 12? or is it treated as a variable?
While you can use a named value (or what you might think of as a 'variable') to construct a dictionary:
>>> x=22
>>> di={x:x}
>>> di
{22: 22}
You can also demonstrate that the value of the named value (if that value is immutable) is used at the time of construction and not dynamic:
>>> x=5
>>> di
{22: 22}
The keys of a dict must be hashable (ie, unchanging; immutable) which would preclude the use of a list, set, or other named value that can change:
>>> changeable_list=[22]
>>> di={changeable_list:changeable_list}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
But the value of a dict can be dynamic and mutable:
>>> di={tuple(changeable_list):changeable_list}
>>> di
{(22,): [22]}
And that list can be modified:
>>> changeable_list.append(44)
>>> changeable_list
[22, 44]
>>> di
{(22,): [22, 44]}
Notice how the value of the dict changes as the list changes because they are the same object.
As has been said, No, the keys of a dictionary are not variables. However, variables can sometimes key thought of as keys:
students = {"Jake": 12, "Rachel":12, "Ross":15}
class Students:
pass
s = Students()
s.Jake = 12
print(s.Jake, students['Jake'])
print(getattr(s, 'Jake'))
Output:
12 12
12
There is no syntax that I'm aware of that would allow you to access the value 12 from students in this form: students.Jake
However, modify the above code:
class Students:
def __getitem__(self, key):
return getattr(self, key)
... # as before
print(getattr(s, 'Jake'), s['Jake'])
Output:
12 12
Now students has an operator[] a little like a dictionary (Other operators might also be necessary.)
So, an object can become like a dictionary because they have an underlying dictionary to make them work as objects.
TL;DR - it depends on what you mean by a "variable". dicts act like python namespaces but allow more types of variable names.
Python objects have no inherent name. They can be referenced by one or more other objects and when their reference count goes to zero, they are deleted. When you assign an object to a variable, some data structure adds a reference to the the object and associates that object with the name. That data structure is the variable's namespace (that is, the context where the variable name is valid). And for most objects, that data structure is a dict.
Lets look at two examples:
class Students:
pass
student_obj = Students()
and
student_dct = {}
I could treat Jake as a a variable
>>> student_obj.Jake = 12
>>> student_obj.Jake
12
>>> student_obj.__dict__
{'Jake': 12}
Or add it to the dict
>>> student_dct["Jake"] = 12
>>> student_dct["Jake"]
12
>>> student_dct
{'Jake': 12}
That's really close to the first example! The advantage to a variable is that it is parsed by python and python does the lookup for you. Python turns student_obj.Jake into student_obj.__getattribute__("Jake"). For normal class objects, python will check the object __dict__ for the name then fall back to containing namespaces. Classes that use __slots__ or are implemented in C follow different rules.
But variable assignment is a disadvantage if you want to use names that don't fit python's sytax rules.
>>> student_obj.Jim Bob = 12
File "<stdin>", line 1
student_obj.Jim Bob = 12
^
SyntaxError: invalid syntax
Here, you want "Jim Bob" to be a variable but it can't be used directly as a variable because it breaks python. So, you put it into a dict
>>> student_dct["Jim Bob"] = 12
So, dictionary items are "variables" (the value can be referenced and reassigned) but are not "python variables" because python doesn't implement the lookup for you.
Let us have the following example:
def fun(**args):
print(str(args))
dic = {'name': 'Pulkit'}
fun(**dic)
This code works fine and I have the following output:
{'name': 'Pulkit'}
Now lets pass the value as bytes:
dic_vb = {'name': b'Pulkit'}
fun(**dic_vb)
This also works fine and have the following output:
{'name': b'Pulkit'}
But things change when I try to have the key as bytes:
dic_kb = {b'name': 'Pulkit'}
This results in TypeError saying:
TypeError: fun() keywords must be strings
Is there any way we can pass bytes as keyword arguments. I also checked CPython code at repo which deals with keyword arguments and seems like we can't pass. Is there any workaround or do I need to make sure unicodes are passed.
I am dealing with a codebase where we have lot of such instances on Python 2 and need to be ported to Python 3. So is only way possible is to convert all of the keyword arguments to unicodes?
No, keywords need to be valid identifiers, it is the same reason why you cannot provide dictionaries that have numbers as keys to a function expecting **kwargs:
>>> dic = {1: 'Pulkit'}
>>> fun(**dic)
TypeErrorTraceback (most recent call last)
<ipython-input-54-83c29ed0f08e> in <module>()
3
4 dic = {1: 'Pulkit'}
----> 5 fun(**dic)
TypeError: fun() keywords must be strings
That is also what you saw in the source, with the negation (!) of PyUnicode_Check acting as the enforcer.
It is required; you need to provide keys that are able to act as names. To see what is and what is not allowed, just try assigning it to a name in the interactive interpreter and see what errs:
>>> b'my_list' = [...] # err
>>> 23 = [21, 22] # err
A viable (and sole, if decode is not allowed) option is passing the dictionary as just another argument and then accessing the values through with normal dictionary look-ups.
Of course, if decoding the bytes to strings before invoking the function is a viable alternative I would suggest you pursue that option:
>>> dic = {b'name': 'Pulkit'}
>>> fun(**{b.decode(): v for b, v in dic.items()})
I am learning how to use the string format method, and I've found something I don't understand. When I do:
>>> s = "My {comp[b]}"
>>> s.format(comp = {'a': 'laptop', 'b': 'desktop'})
'My desktop'
I get the expected results. But when I try to define the dictionary out of the method, and just used the name inside:
>>> comp = {'a': 'laptop', 'b': 'desktop'}
>>> s = "My {comp[b]}"
>>> s.format(comp)
I get KeyError: 'comp'. Why?
In your second example you're not naming the parameter that you're passing to format. Your final line should be:
>>> s.format(comp=comp)
format differentiates the use between positional and named arguments.
By using the name of the variable inside the format string, you are required to give a named argument with that specific name.
In the first you are because you are calling "".format(<name>=<var>) while in the second case your are just giving a positional argument (position 0) that is filled by the comp dictionary.
If you have read the documentation you have noticed that format can use positional arguments in this way: "Hello {0}!".format(<arg0>).
In your second case, instead of giving the required name argument comp you are giving the position argument 0.
Very simple question from a Python newbie:
My understanding is that the keys in a dict are able to be just about any immutable data type. Is it possible to pass an immutable object (e.g., a member of an enum class) as a key in the **kwargs dictionary for a function or a class? I have tried it and the answer seems to be "no":
from enum import Enum
class MyEnum(Enum):
X= 'X'
Y= 'Y'
def func(*args,**kwargs):
pass
func(MyEnum.X = 1)
Output:
"SyntaxError: keyword can't be an expression"
However, there may be something I am missing.
EDIT: Note that I am not trying to make the key equal to MyEnum.X.value (which is a string in this case); I want the key to be the actual Enum object, e.g. MyEnum.X.
You're doing:
func(MyEnum.X = 1)
Here, the problem is MyEnum.X = 1 -- Your keyword (MyEnum.X) is actually an expression (getattr(MyEnum, 'X')), and expressions can't be used as keywords in function calls. In fact, only identifiers can be used as keywords.
To get your call to work, you'll need to use dictionary unpacking like this:
func(**{MyEnum.X.name: 1})
Note, to get the name of the attribute, I needed to do MyEnum.X.name or MyEnum.X.value, depending on how you set up your enum -- In your case, I they are the same thing.
>>> from enum import Enum
>>> class Foo(Enum):
... X = 'X'
...
>>> Foo.X.value
'X'
>>> Foo.X.name
'X'
This won't work, because of the way keyword arguments are being processed. The documentation says:
[...] Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on) [...]
So there must be a way to match the key from the dictionary to the formal parameter name. The exception:
keywords must be strings
when you try to pass something that's not a string:
func(**{MyEnum.X: 1})
suggest the simplest case is required: keys must be strings.
A possible workaround is to make implicit things explicit: just create a class that contains all the necessary information you want to pass in its attributes and pass it. The code will surely be more readable.
The answer to my original question is indeed "no". However, thanks to the input from mgilson and BartoszKP and others, the following work around I came up with is not a bad solution, and solves my current problem. I offer it for others to look at who are trying to do something similar:
from enum import Enum
class MyEnum(Enum):
X= 'X'
Y= 'Y'
def func(*args,**kwargs):
#replace kwargs with kwargsNew
kwargsNew = {}
for kwkey, kwvalue in kwargs.items():
try: kwargsNew[MyEnum(kwkey)] = kwvalue
except ValueError: kwargsNew[kwkey] = kwvalue
doStuffWithKwargs(kwargsNew)
def doStuffWithKwargs(k):
for K in k:
print(K)
#Pass the name X or Y as the key;
#all other keys not found in `MyEnum` are treated normally
func(X = 1, Y = 2, Z = 3)
Output:
Z
MyEnum.X
MyEnum.Y
(no errors)
Do you actually want to create an instnace of MyEnum?
myenum = MyEnum()
func(myenum.X = 1)
One alternative I have found is to pass a dict into *args instead of **kwargs, or to assign a dict to kwargs[0] directly:
func({MyEnum.X: 1})
func(kwargs = {MyEnum.X: 1})
(No errors produced)
However, I really don't like either of these methods.
EDIT: See my second answer for a much better solution.
abc = {}
abc[int: anotherint]
Then the error came up. TypeError: unhashable type? Why I received this?
I've tried str()
This seems to be a syntax issue:
>>> abc = {}
>>> abc[1] = 2
>>> abc
{1: 2}
>>> abc = {1:2, 3:4}
>>> abc
{1: 2, 3: 4}
>>>
At least the syntax of following is incorrect
abc[int: anotherint]
I guess you want to say
abc = [int: anotherint]
Which is incorrect too. The correct way is
abc = {int: anotherint}
unless abc is already defined in which case:
abc[int] = anotherint
is also a valid option.
There are two things wrong - first you have a logic error - I really don't think you want the slice of the dictionary between int (the type, which is unhashable [see below]) and the number anotherInt. Not of course that this is possible in python, but that is what you are saying you want to do.
Second, assuming you meant x[{int:anotherInt}]:
What that error means is that you can't use that as a key in a dictionary, as a rule python doesn't like you using mutable types as keys in dictionaries - it complicates things if you later add stuff to the dictionary or list... consider the following very confusing example:
x={}
x[x]=1
what would you expect this to do, if you tried to subscript that array which would you expect to return 1?
x[{}]
x[{x:x}]
x[{{}:x}]
x[x]
basicly when hashing mutable types you can either say, {} != {} with respect to hashes because they are stored in different places in memory or you end up with the weird recursive situation above
Since the title says appending and none of the answers provided a solution to append things to the dictionary I give it a try:
abc = {}
abc[1]= 2
abc['a'] = [3,9,27]
==> abc = {1:2, 'a':[3,9,27]}