Reducing menu.add_command() clutter/repeat lines - python

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')

Related

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

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?

How can I pass each element of a set to a function?

I have a set with multiple tuples: set1 = {(1,1),(2,1)} for example.
Now I want to pass each tuple of the set to a method with this signature: process_tuple(self, tuple).
I am doing it with a for loop like this:
for tuple in set1:
process_tuple(tuple)
Is there a better way to do it?
Your question is basically "how can I loop without using a loop". While it's possible to do what you're asking with out an explicit for loop, the loop is by far the clearest and best way to go.
There are some alternatives, but mostly they're just changing how the loop looks, not preventing it in the first place. If you want to collect the return values from the calls to your function in a list, you can use a list comprehension to build the list at the same time as you loop:
results = [process_tuple(tuple) for tuple in set1]
You can also do set or dict comprehensions if those seem useful to your specific needs. For example, you could build a dictionary mapping from the tuples in your set to their processed results with:
results_dict = {tuple: process_tuple(tuple) for tuple in set1}
If you don't want to write out for tuple in set1 at all, you could use the builtin map function to do the looping and passing of values for you. It returns an iterator, which you'll need to fully consume to run the function over the full input. Passing the map object to list sometimes makes sense, for instance, to convert inputs into numbers:
user_numbers = list(map(int, input("Enter space-separated integers: ").split()))
But I'd also strongly encourage you to think of your current code as perhaps the best solution. Just because you can change it to something else, doesn't mean you should.

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.

More elegant / pythonic way to append to an array, or create it

For some reason the following code felt a bit cumbersome to me, given all the syntactic sugar I keep finding in Python, so I thought enquire if there's a better way:
pictures = list_of_random_pictures()
invalid_pictures = {}
for picture in pictures:
if picture_invalid(picture):
if not invalid_pictures.get(picture.album.id):
invalid_pictures[picture.album.id] = []
invalid_pictures[picture.album.id].append(picture)
So just to clarify, I'm wondering if there's a more readable way to take care of the last 3 lines above. As I'm repeating invalid_pictures[picture.album.id] 3 times, it just seems unnecessary if it's at all avoidable.
EDIT: Just realized my code above will KeyError, so I've altered it to handle that.
There is, in fact, an object in the standard library to deal exactly with that case: collections.defaultdict
It takes as its argument either a type or function which produces the default value for a missing key.
from collections import defaultdict
invalid_pictures = defaultdict(list)
for picture in pictures:
if picture_invalid(picture):
invalid_pictures[picture.album.id].append(picture)
Although I do find Joel's answer to be the usual solution, there's an often overlooked feature that is sometimes preferrable. when a default value isn't particularly desired is to use dict.setdefault(), when inserting the default is something of an exception (and collections.defaultdict is suboptimal. your code, adapted to use setdefault() would be
pictures = list_of_random_pictures()
invalid_pictures = {}
for picture in pictures:
if picture_invalid(picture):
invalid_pictures.setdefault(picture.album.id, []).append(picture)
although this has the downside of creating a lot of (probably unused) lists, so in the particular case of setting the default to a list, it's still often better to use a defaultdict, and then copy it to a regular dict to strip off the default value. This doesn't particularly apply to immutable types, such as int or str.
For example, pep-333 requires that the environ variable be a regular dict, not a subclass or instance of collections.Mapping, but a real, plain ol' dict. Another case is when you aren't so much as passing through an iterator and applying to a flat mapping; but each key needs special handling, such as when applying defaults to a configuration.

In Python 2.6, How Might You Pass a List Object to a Method Which Expects A List of Arguments?

I have a list full of various bits of information that I would like to pass to several strings for inclusion via the new string format method. As a toy example, let us define
thelist = ['a', 'b', 'c']
I would like to do a print statement like print '{0} {2}'.format(thelist) and print '{1} {2}'.format(thelist)
When I run this, I receive the message IndexError: tuple index out of range; when mucking about, it clearly takes the whole list as a single object. I would, of course, rather it translate thelist to 'a', 'b', 'c'.
I tried using a tuple and received the same error.
What on Earth is this particular technique called? If I knew the name, I could have searched for it. "Expand" is clearly not it. "Explode" doesn't yield anything useful.
My actual use is much longer and more tedious than the toy example.
.format(*thelist)
It's part of the calling syntax in Python. I don't know the name either, and I'm not convinced it has one. See the tutorial.
It doesn't just work on lists, though, it works for any iterable object.
'{0} {2}'.format(*thelist)
docs

Categories

Resources