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

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?

Related

Dynamically look for index() in list from set of options

def _parse_options(productcode_array):
if not self._check_productcode_has_options(productcode_array):
return None
possible_options = {"UV1", "UV2", "Satin", "Linen", "Unco", "Natural"}
option_index = productcode_array.index()
Example value of productcode_array:
["BC", "1.5x3.5", "100lb", "Linen", "Q100"]
My initial thought was to maybe try/except with a list comprehension but I feel there's probably a cleaner way I don't know about.
What I'm trying to achieve is getting the index position within my list productcode_array where any 1 of the possible_options exist. I know there will always only be 1 of the options present. The reason I need this is because the index position within the productcode is dependent on a number of factors.
What would be a clean and effective way to use index() with each of the values of my possible_options set?
>>> next(i for i, code in enumerate(productcode_array) if code in possible_options)
3
or
>>> productcode_array.index(possible_options.intersection(productcode_array).pop())
3
Example with try/except:
for x in possible_options:
try:
option_index = productcode_array.index(x)
except ValueError:
pass
This does work, but it feels dirty, so open to cleaner options.
You could use set.intersection and then assign to option_index (assuming there's only one common value, as stated in the comments):
For example:
possible_options = {"UV1", "UV2", "Satin", "Linen", "Unco", "Natural"}
productcode_array = ["BC", "1.5x3.5", "100lb", "Linen", "Q100"]
for v in possible_options.intersection(productcode_array):
option_index = productcode_array.index(v)
print(option_index)
Prints:
3

Django/ Python - max() of list returns a tuple

Hi I have a Django function:
def get_spans(angle):
spans = Spans.objects.values_list('span').filter(
max_roof_angle=angle,
)
try:
max_span = max(spans)
except ValueError:
max_span = 0
return max_span
My question is - why does this return a tuple? How do I ensure I am getting a single, integer value back?
Any help much appreciated.
From the documentation: you can get a flat list using the arg flat=True. If you try to get the max from a list you would get a single value which you need
def get_spans(angle):
spans = Spans.objects.values_list('span', flat=True).filter(
max_roof_angle=angle,
)
max_span = max(spans)
return max_span
Raunak already gave the proper answer for how to get the integer using value_list(), but I thought I might add that the reason why it returns a tuple is so that you can query multiple fields. Because Python doesn't treat single-element tuples like scalars, it would be inconsistent to return a scalar in some cases but a tuple in others.
But also, a better way to get the max would be to let the database calculate it for you, using aggregation. That way you can add db_index=True to the span field in your model and have the DB calculate the max in O(1) time. I can't really test it, but something like this should do the trick, I think:
from django.db.models import Max
def get_spans(angle):
return Spans.objects.filter(max_roof_angle_exact=angle).aggregate(Max('span'))['span__max']
You can return max_span[0]
I would suggest checking that it isn't an empty tuple. So do the proper exception handling too.

Skip keys without Type checking in Python (pymssql)

I need to access all the non-integer keys for a dict that looks like:
result = {
0 : "value 1",
1 : "value 2",
"key 1" : "value 1",
"key 2" : "value 2",
}
I am currently doing this by:
headers = [header for header in tmp_dict.keys() if not isinstance(header, int)]
My question:
Is there a way to do this without type checking?
This tmp_dict is coming out of a query using pymssql with the as_dict=True attribute, and for some reason it returns all the column names with data as expected, but also includes the same data indexed by integers. How can I get my query result as a dictionary with only the column values and data?
Thanks for your help!
PS - Despite my issues being resolved by potentially answering 2, I'm curious how this can be done without type checking. Mainly for the people who say "never do type checking, ever."
With regard to your question about type checking, the duck-type approach would be to see whether it can be converted to or used as an int.
def can_be_int(obj):
try:
int(obj)
except (TypeError, ValueError):
return False
return True
headers = [header for header in tmp_dict.keys() if not can_be_int(header)]
Note that floats can be converted to ints by truncating them, so this isn't necessarily exactly equivalent.
A slight variation on the above would be to use coerce(0, obj) in place of int(obj). This will allow any kind of object that can be converted to a common type with an integer. You could also do something like 0 + obj and 1 * obj which will check for something that can be used in a mathematical expression with integers.
You could also check to see whether its string representation is all digits:
headers = [header for header in tmp_dict.keys() if not str(header).isdigit()]
This is probably closer to a solution that doesn't use type-checking, although it will be slower, and it's of course entirely possible that a column name would be a string that is only digits! (Which would fail with many of these approaches, to be honest.)
Sometimes explicit type-checking really is the best choice, which is why the language has tools for letting you check types. In this situation I think you're fine, especially since the result dictionary is documented to have only integers and strings as keys. And you're doing it the right way by using isinstance() rather than explicitly checking type() == int.
Looking at the source code of pymssql (1.0.2), it is clear that there is no option for the module to not generate data indexed by integers. But note that data indexed by column name can be omitted if the column name is empty.
/* mssqldbmodule.c */
PyObject *fetch_next_row_dict(_mssql_connection *conn, int raise) {
[...]
for (col = 1; col <= conn->num_columns; col++) {
[...]
// add key by column name, do not add if name == ''
if (strlen(PyString_AS_STRING(name)) != 0)
if ((PyDict_SetItem(dict, name, val)) == -1)
return NULL;
// add key by column number
if ((PyDict_SetItem(dict, PyInt_FromLong(col-1), val)) == -1)
return NULL;
}
[...]
}
Regarding your first question, filtering result set by type checking is surely the best way to do that. And this is exactly how pymssql is returning data when as_dict is False:
if self.as_dict:
row = iter(self._source).next()
self._rownumber += 1
return row
else:
row = iter(self._source).next()
self._rownumber += 1
return tuple([row[r] for r in sorted(row.keys()) if type(r) == int])
The rationale behind as_dict=True is that you can access by index and by name. Normally you'd get a tuple you index into, but for compatibility reasons being able to index a dict as though it was a tuple means that code depending on column numbers can still work, without being aware that column names are available.
If you're just using result to retrieve columns (either by name or index), I don't see why you're concerned about removing them? Just carry on regardless. (Unless for some reason you plan to pickle or otherwise persist the data elsewhere...)
The best way to filter them out though, is using isinstance - duck typing in this case is actually unpythonic and inefficient. Eg:
names_only = dict( (k, v) for k,v in result.iteritems() if not isinstance(k, int) )
Instead of a try and except dance.
>>> sorted(result)[len(result)/2:]
['key 1', 'key 2']
This will remove the duplicated integer-keyed entrys. I think what you're doing is fine though.

Reading from Python dict if key might not be present

I am very new to Python and parsing data.
I can pull an external JSON feed into a Python dictionary and iterate over the dictionary.
for r in results:
print r['key_name']
As I walk through the results returned, I am getting an error when a key does not have a value (a value may not always exist for a record). If I print the results, it shows as
'key_name': None, 'next_key':.................
My code breaks on the error. How can I control for a key not having a value?
Any help will be greatly appreciated!
Brock
The preferred way, when applicable:
for r in results:
print r.get('key_name')
this will simply print None if key_name is not a key in the dictionary. You can also have a different default value, just pass it as the second argument:
for r in results:
print r.get('key_name', 'Missing: key_name')
If you want to do something different than using a default value (say, skip the printing completely when the key is absent), then you need a bit more structure, i.e., either:
for r in results:
if 'key_name' in r:
print r['key_name']
or
for r in results:
try: print r['key_name']
except KeyError: pass
the second one can be faster (if it's reasonably rare than a key is missing), but the first one appears to be more natural for many people.
There are two straightforward ways of reading from Python dict if key might not be present. for example:
dicty = {'A': 'hello', 'B': 'world'}
The pythonic way to access a key-value pair is:
value = dicty.get('C', 'default value')
The non-pythonic way:
value = dicty['C'] if dicty['C'] else 'default value'
even worse:
try:
value = dicty['C']
except KeyError as ke:
value = 'default value'
If possible, use the simplejson library for managing JSON data.
the initial question in this thread is why I wrote the Dictor library, it handles JSON fallback and None values gracefully without needing try/except or If blocks.
Also gives you additional options like ignore upper/lower case,
see,
https://github.com/perfecto25/dictor
use has_key() , and that will return true or false
[Updated to remove careless mistake]
You could also do something like this:
for r in (row for row in results if 'a' in row):
print r['a']
This uses a generator expression to pick "rows" out of "results" where "row" includes the key "a".
Here's a little test script:
results = [{'a':True}, {'b':True}, {'a':True}]
for r in (row for row in results if 'a' in row): print r['a']
You can use the built in function hasattr
key='key_name'
# or loop your keys
if hasattr(e, key):
print(e[key])
else:
print('No key for %s' % key)
Taken from https://stackoverflow.com/a/14923509/1265070
id = getattr(myobject, 'id', None)

Do I have to cause an ValueError in Python

I have this code:
chars = #some list
try:
indx = chars.index(chars)
except ValueError:
#doSomething
else:
#doSomethingElse
I want to be able to do this because I don't like knowfully causing Exceptions:
chars = #some list
indx = chars.index(chars)
if indx == -1:
#doSomething
else:
#doSomethingElse
Is there a way I can do this?
Note that the latter approach is going against the generally accepted "pythonic" philosophy of EAFP, or "It is Easier to Ask for Forgiveness than Permission.", while the former follows it.
if element in mylist:
index = mylist.index(element)
# ... do something
else:
# ... do something else
For the specific case where your list is a sequence of single-character strings you can get what you want by changing the list to be searched to a string in advance (eg. ''.join(chars)).
You can then use the .find() method, which does work as you want. However, there's no corresponding method for lists or tuples.
Another possible option is to use a dictionary instead. eg.
d = dict((x, loc) for (loc,x) in enumerate(chars))
...
index = d.get(chars_to_find, -1) # Second argument is default if not found.
This may also perform better if you're doing a lot of searches on the list. If it's just a single search on a throwaway list though, its not worth doing.

Categories