I have some issue trying to define keywords_aguments.
I'm trying to define a function that returns all the objects with *_control in the scene, when nothing is specified, but i'd like to choose which ones about 'left' or 'right' it has to return.
Below you can find my function. I don't understand where the error is.
from maya import cmds
def correct_value(selection=None, **keywords_arguments):
if selection is None:
selection = cmds.ls ('*_control')
if not isinstance(selection, list):
selection = [selection]
for each in keywords_arguments:
keywords_list = []
if each.startswith('right','left'):
selection.append(each)
return selection
correct_value()
Keyword arguments are dictionaries. You can print them or could have verified the type with the type() function. This allows you to try use of dictionary in isolated context on your own and finding out how to solve your problem yourself.
Now, when you have a dictionary x = {1:2}, iterating over it with for will give you just one, i.e. it will only iterate over the keys(!), not the according values. For that, use for key, value in dictionary.items() and then use the value if key in ('right', 'left').
The code you have would add 'right' or 'left' on to the end of the list.
I think you want something like this:
def find_controls(*selection, **kwargs): # with *args you can pass one item, several items, or a list
selection = selection or cmds.ls("*_control") or [] # supplied objects, or the ls command, or an empty list
if not kwargs:
return list(selection) # no flags? reutrn the whole list
lefty = lambda ctrl: ctrl.lower().startswith("left") # this will filter for items with left
righty = lambda ctrl: ctrl.lower().startswith("right") # this will filter for items with left
filters = []
if kwargs.get('left'): # safe way to ask 'is this key here and does it have a true value?'
filters.append(lefty)
if kwargs.get('right'):
filters.append(righty)
result = []
for each_filter in filters:
result += filter (each_filter, selection)
return result
find_controls (left=True, right=True)
# Result: [u'left_control', u'right_control'] #
find_controls (left=True, right =False) # or just left=True
# Result: [u'left_control'] #
find_controls()
# Result: [u'left_control', u'middle_control', u'right_control'] #
The trick here is to use the lambdas (which are basically just functions in a shorter format) and the built in filter function (which applies a function to everything in a list and returns things where the function true a non-zero, non-false answer. It's easy to see how you could extend it just by adding more keywords and corresponding lambdas
Related
Using a Python lambda can you check whether an element exists in another list (of maps) and also increment a variable? I'm attempting to optimise/refactor my code using a lambda but I've gone and confused myself.
Below is my existing code that I want to convert to a lambda. Is it possible to do this using one lambda or will I need to use 2 lambdas? Any advice how can I convert it to a lambda/s?
current_orders = auth.get_orders()
# returns [{'id': 'foo', 'price': 1.99, ...}, ...]
deleted_orders = auth.cancel_orders()
# returns id's of all cancelled orders [{'id': 'foo'}, {'id': 'bar'}, ...]
# Attempting to convert to lambda
n_deleted = 0
for del_order in deleted_orders:
for order in current_orders:
if del_order['id'] == order['id']:
n_deleted += 1
# lambda
n_deleted = filter(lambda order, n: n += order['id'] in current_orders, deleted_orders)
# end
if n_deleted != len(orders):
logger.error("Failed to cancel all limit orders")
Note: I know I can say if len(deleted_orders) < len(current_orders): logger.error("Failed to delete ALL orders") but I want to expand my lambda eventually to say ...: logger.error("Failed to delete ORDER with ID: %s")
You can't use += (or assignment of any kind) in a lambda at all, and using filter for side-effects is a terrible idea (this pattern looks kind of like how reduce is used, but it's hard to tell what you're trying to do).
It looks like you're trying to count how many order['id'] values appear in current_orders. You shouldn't use a lambda for this at all. To improve efficiency, get the ids from out as a set and use set operations to check if all the ids were found in both list:
from future_builtins import map # Only on Py2, to get generator based map
from operator import itemgetter
... rest of your code ...
getid = itemgetter('id')
# Creating the `set`s requires a single linear pass, and comparison is
# roughly linear as well; your original code had quadratic performance.
if set(map(getid, current_orders)) != set(map(getid, deleted_orders)):
logger.error("Failed to cancel all limit orders")
If you want to know which orders weren't canceled, a slight tweak, replacing the if check and logger output with:
for oid in set(map(getid, current_orders)).difference(map(getid, deleted_orders)):
logger.error("Failed to cancel order ID %s", oid)
If you want the error logs ordered by oid, wrap the set.difference call in sorted, if you want it in the same order returned in current_orders, change to:
from itertools import filterfalse # On Py2, it's ifilterfalse
# Could inline deletedids creation in filterfalse if you prefer; frozenset optional
deletedids = frozenset(map(getid, deleted_orders))
for oid in filterfalse(deletedids.__contains__, map(getid, current_orders)):
logger.error("Failed cancel order ID %s", oid)
It is possible to hack around it but lambdas should not mutate, they should return a new result. Also you should not overcomplicate lambdas, they are meant for short quick functions such a key for a sort method
Probably you should be using a list comprehension. eg
current_order_ids = {order['id'] for order in current_orders}
not_del = [order for order in deleted_orders if order['id'] not in current_order_ids]
for order in not_del:
logger.error("Failed to delete ORDER with ID: %s", order['id'])
So I made this method to set parameters from a text file:
def set_params(self, params, previous_response=None):
if len(params) > 0:
param_value_list = params.split('&')
self.params = {
param_value.split()[0]: json.loads(previous_response.decode())[param_value.split()[1]] if
param_value.split()[0] == 'o' and previous_response else param_value.split()[1]
for param_value in param_value_list
}
When i call this method for example like this:
apiRequest.set_params("lim 5 & status active")
//now self.params={"lim" : 5, "status" : "active"}
it works well. Now I want to be able to add the same parameter multiple times, and when that happens, set the param like a list:
apiRequest.set_params("lim 5 & status active & status = other")
//I want this: self.params={"lim" : 5, "status" : ["active", "other"]}
How can I modify this method beautifully? All I can think of is kinda ugly... I am new with python
Just write it as simple and straightforward as you can. That is usually the best approach. In my code, below, I made one change to your requirements: all values are a list, some may have just one element in the list.
In this method I apply the following choices and techniques:
decode and parse the previous response only once, not every time it is referenced
start with an empty dictionary
split each string only once: this is faster because it avoids redundant operations and memory allocations, and (even more importantly) it is easier to read because the code is not repetitive
adjust the value according to the special-case
use setdefault() to obtain the current list of values, if present, or set a new empty list object if it is not present
append the new value to the list of values
def set_params(self, params, previous_response=None):
if len(params) <= 0:
return
previous_data = json.loads(previous_response.decode())
self.params = {}
for param_value in params.split('&'):
key, value = param_value.split()
if key == 'o' and previous_response:
value = previous_data[value]
values = self.params.setdefault(key, [])
values.append(value)
# end set_params()
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by
definition, not smart enough to debug it.
— Brian W. Kernighan and P. J. Plauger in The Elements of Programming Style.
Reference: http://quotes.cat-v.org/programming/
I'm trying to work out if my problem is solvable using the builtin sorted() function or if I need to do myself - old school using cmp would have been relatively easy.
My data-set looks like:
x = [
('business', Set('fleet','address'))
('device', Set('business','model','status','pack'))
('txn', Set('device','business','operator'))
....
The sort rule should be basically for all value of N & Y where Y > N, x[N][0] not in x[Y][1]
Although I'm using Python 2.6 where the cmp argument is still available I'm trying to make this Python 3 safe.
So, can this be done using some lambda magic and the key argument?
-== UPDATE ==-
Thanks Eli & Winston! I didn't really think using key would work, or if it could I suspected it would be a shoe horn solution which isn't ideal.
Because my problem was for database table dependencies I had to make a minor addition to Eli's code to remove an item from its list of dependencies (in a well designed database this wouldn't happen, but who lives in that magical perfect world?)
My Solution:
def topological_sort(source):
"""perform topo sort on elements.
:arg source: list of ``(name, set(names of dependancies))`` pairs
:returns: list of names, with dependancies listed first
"""
pending = [(name, set(deps)) for name, deps in source]
emitted = []
while pending:
next_pending = []
next_emitted = []
for entry in pending:
name, deps = entry
deps.difference_update(set((name,)), emitted) # <-- pop self from dep, req Py2.6
if deps:
next_pending.append(entry)
else:
yield name
emitted.append(name) # <-- not required, but preserves original order
next_emitted.append(name)
if not next_emitted:
raise ValueError("cyclic dependancy detected: %s %r" % (name, (next_pending,)))
pending = next_pending
emitted = next_emitted
What you want is called a topological sort. While it's possible to implement using the builtin sort(), it's rather awkward, and it's better to implement a topological sort directly in python.
Why is it going to be awkward? If you study the two algorithms on the wiki page, they both rely on a running set of "marked nodes", a concept that's hard to contort into a form sort() can use, since key=xxx (or even cmp=xxx) works best with stateless comparison functions, particularly because timsort doesn't guarantee the order the elements will be examined in. I'm (pretty) sure any solution which does use sort() is going to end up redundantly calculating some information for each call to the key/cmp function, in order to get around the statelessness issue.
The following is the alg I've been using (to sort some javascript library dependancies):
edit: reworked this greatly, based on Winston Ewert's solution
def topological_sort(source):
"""perform topo sort on elements.
:arg source: list of ``(name, [list of dependancies])`` pairs
:returns: list of names, with dependancies listed first
"""
pending = [(name, set(deps)) for name, deps in source] # copy deps so we can modify set in-place
emitted = []
while pending:
next_pending = []
next_emitted = []
for entry in pending:
name, deps = entry
deps.difference_update(emitted) # remove deps we emitted last pass
if deps: # still has deps? recheck during next pass
next_pending.append(entry)
else: # no more deps? time to emit
yield name
emitted.append(name) # <-- not required, but helps preserve original ordering
next_emitted.append(name) # remember what we emitted for difference_update() in next pass
if not next_emitted: # all entries have unmet deps, one of two things is wrong...
raise ValueError("cyclic or missing dependancy detected: %r" % (next_pending,))
pending = next_pending
emitted = next_emitted
Sidenote: it is possible to shoe-horn a cmp() function into key=xxx, as outlined in this python bug tracker message.
I do a topological sort something like this:
def topological_sort(items):
provided = set()
while items:
remaining_items = []
emitted = False
for item, dependencies in items:
if dependencies.issubset(provided):
yield item
provided.add(item)
emitted = True
else:
remaining_items.append( (item, dependencies) )
if not emitted:
raise TopologicalSortFailure()
items = remaining_items
I think its a little more straightforward than Eli's version, I don't know about efficiency.
Over looking bad formatting and this strange Set type... (I've kept them as tuples and delimited the list items correctly...) ... and using the networkx library to make things convenient...
x = [
('business', ('fleet','address')),
('device', ('business','model','status','pack')),
('txn', ('device','business','operator'))
]
import networkx as nx
g = nx.DiGraph()
for key, vals in x:
for val in vals:
g.add_edge(key, val)
print nx.topological_sort(g)
This is Winston's suggestion, with a docstring and a tiny tweak, reversing dependencies.issubset(provided) with provided.issuperset(dependencies). That change permits you to pass the dependencies in each input pair as an arbitrary iterable rather than necessarily a set.
My use case involves a dict whose keys are the item strings, with the value for each key being a list of the item names on which that key depends. Once I've established that the dict is non-empty, I can pass its iteritems() to the modified algorithm.
Thanks again to Winston.
def topological_sort(items):
"""
'items' is an iterable of (item, dependencies) pairs, where 'dependencies'
is an iterable of the same type as 'items'.
If 'items' is a generator rather than a data structure, it should not be
empty. Passing an empty generator for 'items' (zero yields before return)
will cause topological_sort() to raise TopologicalSortFailure.
An empty iterable (e.g. list, tuple, set, ...) produces no items but
raises no exception.
"""
provided = set()
while items:
remaining_items = []
emitted = False
for item, dependencies in items:
if provided.issuperset(dependencies):
yield item
provided.add(item)
emitted = True
else:
remaining_items.append( (item, dependencies) )
if not emitted:
raise TopologicalSortFailure()
items = remaining_items
I have a dictionary that looks like this:
reply = {icon:[{name:whatever,url:logo1.png},{name:whatever,url:logo2.png}]}
how do i access logo1.png ?
I tried :
print reply[icon][url]
and it gives me a error:
list indices must be integers, not str
EDIT:
Bear in mind sometimes my dictionary changes to this :
reply = {icon:{name:whatever,url:logo1.png}}
I need a general solution which will work for both kinds of dictionaries
EDIT2:
My solution was like this :
try:
icon = reply['icon']['url']
print icon
except Exception:
icon = reply['icon'][0]['url']
print ipshit,icon
This works but looks horrible. I was wondering if there was an easier way than this
Have you tried this?
reply[icon][0][url]
If you know for sure all the different kinds of responses that you will get, you'll have to write a parser where you're explicitly checking if the values are lists or dicts.
You could try this if it is only the two possibilities that you've described:
def get_icon_url(reply):
return reply['icon'][0]['url']\
if type(reply['icon']) is list else reply['icon']['url']
so in this case, icon is the key to a list, that has two dictionaries with two key / value pairs in each. Also, it looks like you might want want your keys to be strings (icon = 'icon', name='name').. but perhaps they are variables in which case disregard, i'm going to use strings below because it seems the most correct
so:
reply['icon'] # is equal to a list: []
reply['icon'][0] # is equal to a dictionary: {}
reply['icon'][0]['name'] # is equal to 'whatever'
reply['icon'][0]['url'] # is equal to 'logo1.png'
reply['icon'][1] # is equal to the second dictionary: {}
reply['icon'][1]['name'] # is equal to 'whatever'
reply['icon'][1]['url'] # is equal to 'logo2.png'
you can access elements of those inner dictionaries by either knowing how many items are in the list, and reference theme explicitly as done above, or you can iterating through them:
for picture_dict in reply['icon']:
name = picture_dict['name'] # is equal to 'whatever' on both iterations
url = picture_dict['url'] #is 'logo1.png' on first iteration, 'logo2.png' on second.
Cheers!
Not so different, but maybe it looks better (KeyError gives finer control):
icon_data = reply['icon']
try:
icon = icon_data['url']
print icon
except KeyError:
icon = icon_data[0]['url']
print ipshit,icon
or:
icon_data = reply['icon']
if isinstance(icon_data, list):
icon_data = icon_data[0]
icon = icon_data['url']
I am trying to compare values from 2 Dictionaries in Python. I want to know if a value from one Dictionary exists anywhere in another Dictionary. Here is what i have so far. If it exists I want to return True, else False.
The code I have is close, but not working right.
I'm using VS2012 with Python Plugin
I'm passing both Dictionary items into the functions.
def NameExists(best_guess, line):
return all (line in best_guess.values() #Getting Generator Exit Error here on values
for value in line['full name'])
Also, I want to see if there are duplicates within best_guess itself.
def CheckDuplicates(best_guess, line):
if len(set(best_guess.values())) != len(best_guess):
return True
else:
return False
As error is about generator exit, I guess you use python 3.x. So best_guess.values() is a generator, which exhaust for the first value in line['full name'] for which a match will not be found.
Also, I guess all usage is incorrect, if you look for any value to exist (not sure, from which one dictinary though).
You can use something like follows, providing line is the second dictionary:
def NameExists(best_guess, line):
vals = set(best_guess.values())
return bool(set(line.values()).intersection(vals))
The syntax in NameExists seems wrong, you aren't using the value and best_guess.values() is returning an iterator, so in will only work once, unless we convert it to a list or a set (you are using Python 3.x, aren't you?). I believe this is what you meant:
def NameExists(best_guess, line):
vals = set(best_guess.values())
return all(value in vals for value in line['full name'])
And the CheckDuplicates function can be written in a shorter way like this:
def CheckDuplicates(best_guess, line):
return len(set(best_guess.values())) != len(best_guess)