Dictionary keys with a dot does not work with update()? - python

I would like to use a dictionary parDict with keys that contain a dot and find that the update function does not interpret keys with dots, although the dictionary works fine.
The dot-notation is due to object-orientation of the set of parameters.
The following example illustrate the "inconsistency".
parDict = {}
parDict['a'] = 1
parDict['b'] = 2
parDict['group1.b'] = 3
Several updates of parDict can be done in one command which is important for me
parDict.update(a=4, b=4)
But the the following update is NOT recognized
parDict.update(group1.b=4)
and I get: "SyntaxError: expression cannot contain assignment, ..."
However,
parDict['group1.b'] = 4
works fine.
Is here a way to work around this "inconsistency" to use update() even for keys with a dot in the name?
Would be interesting to perhaps understand the wider context why update() does not work here.

First, let's take a look of how update can be called. According to the Python docs (emphasis mine),
update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).
But how can keyword arguments be defined? According to the Python docs (emphasis mine),
keyword argument: an argument preceded by an identifier (e.g. name=) in a function call or passed as a value in a dictionary preceded by **.
But what is an identifier? According to the Python docs,
The valid characters for identifiers are the same as in Python 2.x: the uppercase and lowercase letters A through Z, the underscore _ and, except for the first character, the digits 0 through 9.
Oh, ok. So you can have an identifier such as name or a, but you can't have an identifier such as group1.b since there is a dot in it.
Back to the update method, if you have a dictionary key that it's not an identifier, you can use a dictionary to update it:
parDict.update({"group1.b": 4})

I can think of following workaround. In this way you can consistently update parDict with a single command.
parDict = {}
parDict['a'] = 1
parDict['b'] = 2
parDict['group1.b'] = 3
parDict.update(a=4, b=4)
print(parDict)
parDict.update({"a":5, "group1.b":7})
print(parDict)
ouptut:
{'a': 4, 'b': 4, 'group1.b': 3}
{'a': 5, 'b': 4, 'group1.b': 7}

I am glad for the input I have got on my questions around parDict, although my original neglect of the difference between "keys" and "identifiers" is very basic. The purpose I have in mind is to simplify command-line interaction with an object-oriented parameter structure. It is a problem of some generality and perhaps here are better solutions than what I suggest below?
Using update() with tuples is attractive, more readable and avoid using a few signs as pointed out at the link #wjandrea posted.
But to use it this way we need to introduce another dictionary, i.e. we have parDict with short unique parameter names and use identifiers and corresponding values, and then introduce parLocation that is a dictionary that relates the short names parameter names to the location object-oriented string.
The solution
parDict = {}
parDict['a'] = 1
parDict['b'] = 2
parDict['group1_b'] = 3
and
parLocation = {}
parLocation['a'] = 'a'
parLocation['b'] = 'b'
parLocation['group1_b'] = 'group1.b'
For command line-interaction I can now write
parDict.update(b=4, group1_b=4)
And for the internal processing where parameter values are brought to the object-oriented system I write something like
for key in parDict.keys(): set(parLocation[key], parDict[key])
where set() is some function that take as arguments parameter "location" and "value".
Since the problem has some generality I though here might be some other better or more direct approach?

Related

Fast String "Startswith" Matching for Dict like object

I currently have some code which needs to be very performant, where I am essentially doing a string dictionary key lookup:
class Foo:
def __init__(self):
self.fast_lookup = {"a": 1, "b": 2}
def bar(self, s):
return self.fast_lookup[s]
self.fast_lookup has O(1) lookup time, and there is no try/if etc code that would slow down the lookup
Is there anyway to retain this speed while doing a "startswith" lookup instead? With the code above calling bar on s="az" would result in a key error, if it were changed to a "startswith" implementation then it would return 1.
NB: I am well aware how I could do this with a regex/startswith statement, I am looking for performance specifically for startswith dict lookup
An efficient way to do this would be to use the pyahocorasick module to construct a trie with the possible keys to match, then use the longest_prefix method to determine how much of a given string matches. If no "key" matched, it returns 0, otherwise it will say how much of the string passed exists in the automata.
After installing pyahocorasick, it would look something like:
import ahocorasick
class Foo:
def __init__(self):
self.fast_lookup = ahocorasick.Automaton()
for k, v in {"a": 1, "b": 2}.items():
self.fast_lookup.add_word(k, v)
def bar(self, s):
index = self.fast_lookup.longest_prefix(s)
if not index: # No prefix match at all
raise KeyError(s)
return self.fast_lookup.get(s[:index])
If it turns out the longest prefix doesn't actually map to a value (say, 'cat' is mapped, but you're looking up 'cab', and no other entry actually maps 'ca' or 'cab'), this will die with a KeyError. Tweak as needed to achieve precise behavior desired (you might need to use longest_prefix as a starting point and try to .get() for all substrings of that length or less until you get a hit for instance).
Note that this isn't the primary purpose of Aho-Corasick (it's an efficient way to search for many fixed strings in one or more long strings in a single pass), but tries as a whole are an efficient way to deal with prefix search of this form, and Aho-Corasick is implemented in terms of tries and provides most of the useful features of tries to make it more broadly useful (as in this case).
I dont fully understand the question, but what I would do is try and think of ways to reduce the work the lookup even has to do. If you know the basic searches the startswith is going to do, you can just add those as keys to the dictionary and values that point to the same object. Your dict will get pretty big pretty fast, however it will greatly reduce the lookup i believe. So maybe for a more dynamic method you can add dict keys for the first groups of letters up to three for each entry.
Without activly storing the references for each search, your code will always need to get each dict objects value until it gets one that matches. You cannot reduce that.

Python convert named string fields to tuple

Similar to this question: Tuple declaration in Python
I have this function:
def get_mouse():
# Get: x:4631 y:506 screen:0 window:63557060
mouse = os.popen( "xdotool getmouselocation" ).read().splitlines()
print mouse
return mouse
When I run it it prints:
['x:2403 y:368 screen:0 window:60817757']
I can split the line and create 4 separate fields in a list but from Python code examples I've seen I feel there is a better way of doing it. I'm thinking something like x:= or window:=, etc.
I'm not sure how to properly define these "named tuple fields" nor how to reference them in subsequent commands?
I'd like to read more on the whole subject if there is a reference link handy.
It seems it would be a better option to use a dictionary here. Dictionaries allow you to set a key, and a value associated to that key. This way you can call a key such as dictionary['x'] and get the corresponding value from the dictionary (if it exists!)
data = ['x:2403 y:368 screen:0 window:60817757'] #Your return data seems to be stored as a list
result = dict(d.split(':') for d in data[0].split())
result['x']
#'2403'
result['window']
#'60817757'
You can read more on a few things here such as;
Comprehensions
Dictionaries
Happy learning!
try
dict(mouse.split(':') for el in mouse
This should give you a dict (rather than tuples, though dicts are mutable and also required hashability of keys)
{x: 2403, y:368, ...}
Also the splitlines is probably not needed, as you are only reading one line. You could do something like:
mouse = [os.popen( "xdotool getmouselocation" ).read()]
Though I don't know what xdotool getmouselocation does or if it could ever return multiple lines.

How to test if any string is in a list?

I'm trying to make an AI. The AI knows to say 'Hello' to 'hi' and to stop the program on 'bye', and if you say something it doesn't know it will ask you to define it. For example, if you say 'Hello' it will ask what that means. You type 'hi' and from then on when you say 'Hello' it will say 'Hello' back. I store everything in a list called knowledge. It works like this:
knowledge = [[term, definition], [term, definition], [term, definition]]
I am trying to add an edit function, where you type edit foo and it will ask for you to input a string, to change the definition of foo. However, I'm stuck. First, of course, I need to test if it already has a definition for foo. But I can't do that. I need to be able to do it regardless of the definition. In other languages, there is typeOf(). However type() doesn't seem to work. Here's what I have, but it doesn't work:
if [term, type(str)] in knowledge:
Can someone help?
As noted by tehhowch in the comments, a dictionary would be more appropriate as these are "key: value" pairs.
Using a dictionary...
knowledge = {'foo': 'foo def', 'bar': 'bar def', 'baz': 'baz def'}
searchTerm= 'foo'
searchTerm in knowledge
Out[1]: True
Storing knowledge as a list of lists fails because each item in knowledge is itself a list. Therefore, searching those lists for a string type (your term) fails. Instead, you could pull the terms out as a separate list and then check that one list for the term you're looking for.
knowledge = [["foo", "foo definition"], ["bar", "bar definition"], ["baz", "baz
definition"]]
terms = [item[0] for item in knowledge]
searchTerm= "foo"
searchTerm in terms
Out[1]: True
As others have mentioned, Python would typically use a dict for this kind of associative array. You approach is analogous to a Lisp data structure called an Association List. These are less efficient than the hashtable structures used by dicts, but they still have some useful properties.
For example, if you look up a key by scanning through the pairs and getting the first one, this means that you can insert another pair with the same key at the front and it will shadow the old value. You don't have to remove it. This makes insertions fast (at least with Lisp-style linked lists). You can also "undo" this operation by deleting the new one, and the old one will then be found by the scanner.
Your check if [term, type(str)] in knowledge: could be made to work as
if [term, str] in ([term, type(definition)] for term, definition in knowledge):
This uses a generator expression to convert your term, definition pairs into term, type(definition) pairs on the fly.
You can use dictionary to store definitions rather than list of lists and python's isinstance function will help you check if term belongs to specific class or not. see below example:
knowledge = {'Hello':'greeting','Hi':'greeting','Bye':'good bye'}
term = "Hello"
if isinstance(term, str):
if term in knowledge:
print("Defination exist")
else:
print("Defination doesn't exist")
else:
print("Entered term is not string")

How to use Python sets and add strings to it in as a dictionary value

I am trying to create a dictionary that has values as a Set object. I would like a collection of unique names associated with a unique reference). My aim is to try and create something like:
AIM:
Dictionary[key_1] = set('name')
Dictionary[key_2] = set('name_2', 'name_3')
Adding to SET:
Dictionary[key_2].add('name_3')
However, using the set object breaks the name string into characters which is expected as shown here. I have tried to make the string a tuple i.e. set(('name')) and Dictionary[key].add(('name2')), but this does not work as required because the string gets split into characters.
Is the only way to add a string to a set via a list to stop it being broken into characters like
'n', 'a', 'm', 'e'
Any other ideas would be gratefully received.
You can write a single element tuple as #larsmans explained, but it is easy to forget the trailing comma. It may be less error prone if you just use lists as the parameters to the set constructor and methods:
Dictionary[key_1] = set(['name'])
Dictionary[key_2] = set(['name_2', 'name_3'])
Dictionary[key_2].add(['name_3'])
should all work the way you expect.
('name') is not a tuple. It's just the expression 'name', parenthesized. A one-element tuple is written ('name',); a one-element list ['name'] is prettier and works too.
In Python 2.7, 3.x you can also write {'name'} to construct a set.

Reducing menu.add_command() clutter/repeat lines

I would like to do the following (just an example, the real code has more menu's and more add_command's):
editmenu.add_command(label="Cut",state="disabled")
editmenu.add_command(label="Copy",state="disabled")
editmenu.add_command(label="Paste",state="disabled")
editmenu.add_command(label="Delete",state="disabled")
But on fewer lines, In fact, just one line if possible. I have menus that are taking up a considerable amount of space in my program and would like to reduce the clutter. Plus the programmer in me sees a bunch of similar lines and feels there must be a way to reduce them.
I tried the following code to no avail; I obviously got a nameerror because label and state aren't defined...
for labeldic in [{label:"Cut"},{label:"Copy"},{label:"Paste"},{label:"Delete"}]: editmenu.add_command(labeldic+{state:"disabled"})
Thanks in advance for any suggestions!
Here's a translation of what you wanted to do:
for labeldic in [{"label":"Cut"},{"label":"Copy"},{"label":"Paste"},{"label":"Delete"}]:
labeldic.update({"state": "disabled"})
editmenu.add_command(**labeldic)
There were three problems I fixed.
The first is that dictionary keys need to be quoted if they are strings. If you want a dict mapping the string 'label' to the the string 'cut', you can do it using the dict literal {'label': 'cut'}, or else possibly with the dict() constructor which expands keyword arguments that way: dict(label='cut'). As you discovered, {label: 'cut'} wouldn't work, because it tries to use a variable's value for the key, but there is no such variable.
The second is that you can't merge dictionaries using the + operator. It doesn't work, unfortunately. There is, however, an update method that mutates the dict it's called on. Since it doesn't return a merged dict, it can't be used inline the way you used +.
The third problem is that passing a dict is not the same as passing in keyword arguments. foo(bar='baz') is not the same as foo({'bar':'baz'}), but it is the same as foo(**{'bar':'baz'}). The ** syntax in function calling "unpacks" a dictionary into keyword arguments.
Regardless it's sort of weird style. Here's what I would do instead:
for label in ['Cut', 'Copy', 'Paste', 'Delete']:
editmenu.add_command(label=label, state='disabled')

Categories

Resources