Python: Iterating through tuples which are related as subclassed tuples - python

I have the following tuples:
ReadElement = namedtuple('ReadElement', 'address value size')
LookupElement = namedtuple('LookupElement', ReadElement._fields[0:2] + ('lookups', ReadElement._fields[2]))
and I want to iterate through them as follows:
mytuples = [ReadElement(1,2,3), LookupElement(1,2,3,4)]
for address, value, lookups?, size in mytuples
if lookups is not None:
addLookups(lookups)
print address, value, lookups?, size
def addLookups(*items):
return sum(items)
How could I iterate through similar tuples using the same piece of code?
I think what I am looking for is a Union type of the two named tuples, so that that union type preserves the order of the tuples in the loop.
From laike9m post I can see how I can use the isinstance operator without having to unpack the tuples in the loop however I would like to avoid special casing the data and just go straight through without any if statements.
If these were objects I could do something like mytuples[0].execute() without having to worry about what type they were as long as they were were subclassed from the same parent and had that method implemented.
It seems that my question maybe a variant of the following Why are Super-class and Sub-class reversed? . In the case above I only have two items one subclass and one superclass where they are very similar to each other and therefore could also be made into a single class.

First, your namedtuple definition is wrong, should be:
LookupElement = namedtuple('LookupElement', ReadElement._fields[0:2] + ('lookups', ReadElement._fields[2]))
Second, you don't need to do worry about all that:
>>> for nt in mytuples:
print(nt)
ReadElement(address=1, value=2, size=3)
LookupElement(address=1, value=2, lookups=3, size=4)
I'm going to sleep so maybe I can't answer your futher question. I think the best way is to check whether the field you want exists before using it.
I don't know exactly what you want, here's what I'll do:
mytuples = [ReadElement(1,2,3), LookupElement(1,2,3,4)]
for nt in mytuples
if 'lookups' in nt._fields:
print nt.address, nt.value, nt.lookups, nt.size
else:
print nt.address, nt.value, nt.size

Related

Why is my recursion leading to self referencing dict values?

I aim to write a function that splits a budget across options by comparing options based on their benefit/cost ratio and stores them in a list of nested dicts. When multiple options with the same benefit/cost ratio are available, each option shall be pursued separately (as downstream element which in turn may have multiple downstream elements) and reflected as a list of dicts for its upstream dict. There is no limitation as to how many options may occur.
def get_all_allocation_proposals(budget, options, upstream_element=dict()):
# accepts:
# budget to be allocated
# list of options
# returns:
# a list of allocation proposals
# filter options for affordable options and sort by cost benefit ratio
options = [x for x in options if x['cost'] <= budget]
options = sorted(options, key=lambda x: (
x['benefit_cost_ratio'], x['benefit']), reverse=True)
if (len(options) > 0):
# select the best options
best_bc_ratio = options[0]['benefit_cost_ratio']
best_options = [
x for x in options if x['benefit_cost_ratio'] == best_bc_ratio]
upstream_element['downstream_elements'] = []
for current_element in best_options:
downstream_options = remove_conflicting_options(
current_element, options)
downstream_budget = budget - current_element['cost']
current_element['donstream_budget'] = downstream_budget
downstream_elements = get_all_allocation_proposals(downstream_budget,
downstream_options,
current_element)
if downstream_elements is not None:
current_element['downstream_elements'] = downstream_elements
upstream_element['downstream_elements'].append(current_element)
return upstream_element
else:
return None
In the code above, when the elements are appended, self referencing dict values are created. Why is that the case and how can I avoid that? All I want to do is to pass on all downstream elements to the first call stack.
Is there something fundamentally flawed with my recursion pattern?
I think the issue is probably because you are passing mutable objects into your recursive call. Specifically downstream_options and current_element are dicts and when you modify them within a given recursion of the function, you are also modifying them at the level above, which in this case seems to leave you attempting to assign a value in the dict to itself (or some such impossibility, I haven't quite managed to follow the logic through).
A quick solution might be (I'm not sure if this will break your logic) to make a copy of these dicts at each recursion:
from copy import deepcopy
...
downstream_elements = get_all_allocation_proposals(downstream_budget,
deepcopy(downstream_options),
deepcopy(current_element))
Additionally, as identified in the comments you should avoid having a mutable default argument, i.e. upstream_element=dict(). This can produce some very confusing behaviour if you actually use the default (which you don't appear to in your code)

Indexing a namedtuple nested in a dictionary

I'm looking to store a named tuple inside a dictionary. That parts easy. I don't know how to reference an individual bit in the namedtuple following that though.
I know that I could just use a dictionary and make life easier, but in the case where you have values you know you don't want to change, it'd be nice to use a namedtuple here (more so just out of interest - I realize strings are immutable as well).
from collections import namedtuple
Rec = namedtuple('name', ['First', 'Middle', 'Last'])
name = Rec('Charles', 'Edward', 'Bronson')
info = dict(identity=name)
print(name.First)
print(info['identity'])
print(type(info['identity']))
Results:
Charles
name(First='Charles', Middle='Edward', Last='Bronson')
<class '__main__.name'>
I expect to be able to access name.First through calling info['identity'][name.First] or something similar, but I can't seem to index inside the nested namedtuple.
as you probably know, namedtuples support multiple types of indexing
tuple[0] returns the first field (order is defined from the list of fields you gave during the namedtuple definition)
tuple.field_name returns the field named field_name
info['identity'] is the namedtuple you want to index, so let's go :)
print(info['identity'] == name)
# True
print(info['identity'].First)
# Charles
# or
print(info['identity'][0])
# Charles
# OR, splitting the operations
name_field = info['identity']
print(name_field.First)

django: order a QuerySet

I have a view like this:
def profile (request):
articles = Post.thing.all()
newSet = set()
def score():
for thing in articles:
Val = some calculation...
....
newSet.update([(thing,Val)])
score()
context = {
'articles': articles.order_by('id'),
'newSet':newSet,
}
return render(request,'my_profile/my_profile.html',context)
and the outcome is a Queryset which looks like this:
set([(<thing: sfd>, 1), (<thing: quality>, 0), (<thing: hello>, -1), (<thing: hey>, 4), (<thing: test>, 0)
I am now trying to order the set by the given Values so its a list which starts with the highest Value, but when i do newSet.order_by/filter/split/join
it does not work since 'set' object has no attribute join/filter/split.
Can anybody give me a hint how to sort the querySet i could not find anything helpful on my own.
I need this to work out in the view so it cannot/should not be done in the model. Thanks for any advise.
the outcome is a Queryset which looks like this
Actually this is a set (python builtin type), not a QuerySet (django's orm type).
set is an unordered collection type. To "sort" it you first have to turn it into a list - which is actually as simple as newlist = list(newset),
then you can sort the list in-place with newlist.sort(). Since you want this list to be sorted on it's items second elements, you'll need to use the key argument to tell sort on what you want to sort:
newlist.sort(key=lambda item: item[1])
or you can just change your score() function to store (score, obj) tuples instead in which case list.sort() will naturally do the RightThing (it will sort your tuples on their first item).
While we're at it: instead of calling newSet.update() with a list of a single item, you can just use newSet.add() instead, ie:
def score():
for thing in articles:
val = some calculation...
....
newset.add((thing, val))
# or if you don't want to specify a callback
# for `list.sort()`:
# newset.add((val, thing))
And finally: do you really need a set at all here ? Why not using a list right from the start ?
I think you might be slightly confused here between a set, a list and a QuerySet? A set is unordered, while a list is not. Sets don't expose the methods you listed above (filter, order_by, split, join). A QuerySet is a Django-specific class which has many useful methods.
I think it would be simpler to make newSet a list of tuples, and then to order that list by value using list.sort(key=lambda x: x[1]).
If your calculation of val is eligible for it though, I'd recommend using annotate and then doing away with newDict or newSet, and simply pass back the queryset of articles, which would be much simpler, maybe faster, and orderable by using articles.order_by('value'). If you post the calculation of val, I'll try to tell you if that's feasible.

Python : is there an alternate to: "except IndexError:" something lighter?

If mysql has no output...
if record[0][0]:
will return an error
IndexError: tuple index out of range
the only solution i know to fix this issue is:
try:
if record[0][0]:
# Do stuff
except IndexError:
pass
but this looks like a very heavy wrapper script
only to find out if
record[0][0]
has no data. ( no value )
is there something lighter that can be done such as..
if record[0][0] = ""
?
UPDATE:
This is my MYSQL code:
a = _mysql.escape_string(a)
db=b()
db.query("select * from b where a='" + a + "' limit 1")
result = db.store_result()
record = result.fetch_row()
UPDATE:
turns out what worked is:
if record:
rather than
if record[0]:
or
if record[0][0]:
In the general case, if you want to check if an item exists in a list, just check that it exists. Exceptions are considered Pythonic code. Using another construct for access checking is likely to be less readable and suffer from performance problems.
However, if you're really interested in something else.. how about this?
>>> if record[0]:
... field = record[0][0]
This works because an empty list ([]) evaluates as False in an if statement.
>>> record = [[]]
>>> if record[0]: # returns an empty list, e.g. []
... field = record[0][0] # is not executed
A simpler alternative:
import MySQLdb
conn = MySQLdb.connect(passwd="sekret",db="foo")
cur = conn.cursor()
cur.execute("select * from b where a=%s limit 1", (a,))
for result in cur:
print(result)
Note the changes:
Use MySQLdb, not the underlying _mysql* API
Don't concatenate variables into SQL query strings, this will lead to SQL injection.
Iterate over the cursor to get the results
In Python, there is a way to get a default value from a dict but not from a list. E.g. in a dict:
x = mydict.get('key') # x will be None if there is no 'key'
(you can also provide a different default as a 2nd arg to get() method)
Now, it would be convenient to have something like that for lists. Getting an item from a list
is in some ways very similar to getting an item from a dict, but not exactly the same. Python
made a design decision to not have a similar method for lists.
Of course, you can make your own very easily. I sometimes use a function in my own library
called 'getitem', which returns a default arg; however it only looks up one level of a list,
because I feel multiple levels is too much of a corner case and it's probably worth using
an exception with multiple levels. But, for one level you can do:
def getitem(seq, index, default=None):
"""Get item from a `seq` at `index`, return default if index out of range."""
try : return seq[index]
except IndexError : return default
Note that there's a gotcha: you can't assume that getting None back means there is no
item, if your list may itself contain None values. Seems obvious but that's something
you have to remember.
You can easily extend this function to accept multiple indexes and handle multiple
levels of lists, but then you may ask: how do I know at which level there was an
IndexError?

Is it possible to create a python iterator over pre-defined mutable data?

I might be doing this wrong, if I am, let me know, but I'm curious if the following is possible:
I have a class that holds a number of dictionaries, each of which pairs names to a different set of objects of a given class. For example:
items = {"ball" : ItemInstance1, "sword" : ItemInstance2}
people = {"Jerry" : PersonInstance1, "Bob" : PersonInstance2, "Jill" : PersonInstance3}
My class would then hold the current items and people that are availible, and these would be subject to change as the state changes:
Class State:
def __init__(self, items, people):
self.items = items
self.people = people
I would like to define a iter() and next() method such that it iterates through all of the values in its attributes. My first question is whether or not this is possible. If it is, will it be able to support a situation as follows:
I define items and people as above then:
state = State(items, people)
for names, thing in state:
print name + " is " + thing.color
items[cheese] = ItemInstance3
for names, thing in state:
print name + " weighs " + thing.weight
While I feel like this would be usefull in the code I have, I don't know if it's either possible or the right approach. Everything I've read about user defined iterators has suggested that each instance of them is one use only.
If I understand you question correctly then adding the following method to your class should do it:
def __iter__(self):
import itertools
return itertools.chain(self.items.itervalues(), self.people.itervalues())
This chains together two iterators, and the ones chosen here are for the values of items and the values of people.
To make your later code work though, you'll want to iterate over the items - the key-value pairs. In which case this would do it:
def __iter__(self):
import itertools
return itertools.chain(self.items.iteritems(), self.people.iteritems())
There are lots of ways to do what you want. You can indeed have such a State class, and implement the iter() method (http://docs.python.org/library/stdtypes.html).
You could also create a generator function:
def iterate_two(l1, l2):
for k in l1:
yield k, l1[k]
for k in l2:
yield k, l2[k]
You can use itertools.chain. You can use list comprehensions and generator expressions. Etc.
I, personally, wouldn't create the State class as you suggest, if all it is is an iterator mechanism - I'd probably use a list comprehension.

Categories