What is the right way to check if element does not exist in Python ?
The element is expected to be present most of the time, and if it is empty it is not an "error" and need to be processed normally:
def checkElement(self, x, y):
if not (self.map[x][y]):
self.map[x][y] = 'element {}:{}'.format(x, y)
return self.map[x][y]
tldr
Your own code together with triplee's answer cover the common cases. I want to point out ambiguity in your question. How you check for "empty" very much depends on what your definition of empty is.
This is a tricky question because the semantics of "empty" are not exactly clear. Assuming that the data structure is a nested dict as could be inferred from your example, then it could be the case that empty means the inner/outer key is not contained in the dictionary. In that case you'd want to go with what triplee suggests. Similarly if the container is a nested list, but instead of KeyError you'd catch IndexError.
Alternatively, it could also be the case that "empty" means both the inner and outer keys are in the dictionary (or list) but the value at that position is some signifier for "empty". In this case the most natural "empty" in Python would be None, so you'd want to check if the value under those keys is None. None evaluates to False in boolean expressions so your code would work just fine.
However, depending on how your application defines empty these are not the only alternatives. If you're loading json data and the producer of said json has been prudent, empty values are null in json and map to None when loaded into Python. More often than not the producer of the json has not been prudent and empty values are actually just empty strings {firstName:''}, this happens more often than one would like. It turns out that if not self.map[x][y] works in this case as well because an empty string also evaluates to False, same applies to an empty list, an empty set and an empty dict.
We can generalise the meaning of empty further and say that "empty" is any value that is not recognised as actionable or valid content by the application and should therefore be considered "empty" - but you can already see how this is completely dependent on what the application is. Would {firstName: ' '} a string that only contains white space be empty, is a partially filled in email address empty?
The Best way to check if any object (Lists, Dicts, etc) exist or not is to wrap it within a try...except Block. Your checkElement Function could be re-written thus:
def checkElement
try:
self.map[x][y]
except:
# HANDLE THE CASE WHERE self.map[x][y] ISN'T SET...
self.map[x][y] = 'element {}:{}'.format(x, y)
The answer to what you seem to be asking is simply
try:
result = self.map[x][y]
except KeyError:
result = 'element {}:{}'.format(x, y)
self.map[x][y] = result
return result
Of course, if self.map[x] might also not exist, you have to apply something similar to that; or perhaps redefine it to be a defaultdict() instead, or perhaps something else entirely, depending on what sort of structure this is.
KeyError makes sense for a dict; if self[x] is a list, probably trap IndexError instead.
Related
Here's my code
if "value" not in dictionary():
do something
else:
do something else
I get the error 'TypeError: 'dict' object is not callable.
I've tried changing the first line to
if dictionary["value"]:
But get a different error. Where am I going wrong here?
Assuming dictionary is in fact a dict() composed of key-values then it would be
if 'value' not in dictionary:
...etc
The error is essentially telling you that you are erroneously attempting to call a non-callable object as if it were a method/function.
If you are not particularly interested in whether or not a value exists you may use a method I am personally a fan of:
some_value = dictionary.get('value', 'valueIfNotPresent')
do_something(some_value)
The above allows you to provide a sentinel value (which is provided when the key does not exist). This can help with branch elimination (e.g. not having to if/else it, just allowing your code to act upon the sentinel) or at least reduce logic in checking for the existence of a key (unless the absence of a key is important).
Both are quite readable however and your mileage may vary.
EDIT:
#user1857805 is correct, if you are attempting to find out if a value is in a dictionary then the above (while still good and valid to know) is not enough. You will need to get the .values() as a list from the dictionary; however, as a dictionary may contain dictionaries you will need to recurse the dictionary to find all of the possibly stored values.
try using the following:
if 'value' not in dictionary.values():
do something
else:
do something else.
I'm having an issue when trying to pass a sqlite query to another function.
The issue is that the sqlite query MAY contains a list and therefore I cannot use *args as it unpacks the tuple but then ignores the list, example query I'm attempting to pass to the function:
'SELECT postname FROM history WHERE postname = ? COLLATE NOCASE', [u'Test']
So in this case I could use args as opposed to *args in the destination function, however I may have a sqlite query that doesn't contain a list and therefore I can't always do this e.g.
'SELECT * FROM history'
so I guess my question in a nutshell is how can I successfully pass a sqlite query to another function whether it contains a list or not, using args?
Can you just try,except it?
try:
func(*args)
except TypeError:
func(args)
Of course, this will catch TypeErrors inside your function as well. As such, you may want to create another function which actually deals with the unpacking and makes sure to give you an unpackable object in return. This also doesn't work for strings since they'll unpack too (see comments).
Here's a function which will make sure an object can be unpacked.
def unpackable(obj):
if hasattr(obj,'__iter__'):
return obj
else:
return (obj,)
func(*unpackable(args))
I would argue the best answer here is to try and ensure you are always putting in an iterable, rather than trying to handle the odd case of having a single item.
Where you have ('SELECT postname FROM history WHERE postname = ? COLLATE NOCASE', [u'Test']) in one place, it makes more sense to pass in a tuple of length one - ('SELECT * FROM history', ) as opposed to the string.
You haven't said where the strings are coming from, so it's possible you simply can't change the way the data is, but if you can, the tuple is the much better option to remove the edge case from your code.
If you truly can't do that, then what you want is to unpack any non-string iterable, checking for that can be done as shown in this question.
Okay I concede that I didn't ask the question very well. I will update my question to be more precise.
I am writing a function that takes a list as an argument. I want to check the length of the list so I can loop through the list.
The problem that I have is when the list has only one entry, len(myList) returns the length of that entry (the length of the string) and not the length of the list which should be == 1.
I can fix this if I force the argument to be parsed as a single value list ['val']. But I would prefer my API to allow the user to parse either a value or a list of values.
example:
def myMethod(self,dataHandle, data,**kwargs):
comment = kwargs.get('comment','')
_dataHandle= list()
_data = list()
_dataHandle.append(dataHandle)
_data.append(data)
for i in range(_dataHandle):
# do stuff.
I would like to be able to call my method either by
myMethod('ed', ed.spectra,comment='down welling irradiance')
or by
myMethod(['ed','lu'] , [ed.spectra,lu.spectra] , comments = ['downwelling', upwelling radiance'])
Any help would be greatly appreciated. Might not seem like a big deal to parse ['ed'], but it breaks the consistency of my API so far.
The proper python syntax for a list consisting of a single item is [ 'ed' ].
What you're doing with list('ed') is asking python to convert 'ed' to a list. This is a consistent metaphor in python: when you want to convert something to a string, you say str(some_thing). Any hack you'd use to make list('ed') return a list with just the string 'ed' would break python's internal metaphors.
When python sees list(x), it will try to convert x to a list. If x is iterable, it does something more or less equivalent to this:
def make_list(x):
ret_val = []
for item in x:
ret_val.append(item)
return ret_val
Because your string 'ed' is iterable, python will convert it to a list of length two: [ 'e', 'd' ].
The cleanest idiomatic python in this case might be to have your function accept a variable number of arguments, so instead of this
def my_func(itemList):
...
you'd do this
def my_func(*items):
...
And instead of calling it like this
my_func(['ed','lu','lsky'])
You'd call it like this:
my_func('ed', 'lu', 'lsky')
In this way you can accept any number of arguments, and your API will be nice and clean.
You can ask if your variable is a list:
def my_method(my_var):
if isinstance(my_var, list):
for my_elem in my_var:
# do stuff with my_elem
else: # my_var is not iterable
# do stuff with my_var
EDIT: Another option is to try iterating over it, and if it fails (raises and exception) you assume is a single element:
def my_method(my_var):
try:
for my_elem in my_var:
# do stuff with my_elem
except TypeError: # my_var is not iterable
# do_stuff with my_var
The good thing about this second options is that it will work not only for lists, as the first one, but with anything that is iterable (strings, sets, dicts, etc.)
You do actually need to put your string in a list if you want it to be treated like a list
EDIT
I see that at some point there was a list in front of the string. list, contrary to what you may think, doesn't create a list of one item. It calls __iter__ on the string object and iterates over each item. Thus it makes a list of characters.
Hopefully this makes it clearer:
>>> print(list('abc'))
['a', 'b', 'c']
>>> print(list(('abc',)))
['abc']
list('ed') does not create a list containing a single element, 'ed'. list(x) in general does not create a list containing a single element, x. In fact, if you had been using numbers rather than strings (or anything else non-iterable), this would have been blindingly obvious to you:
>>> list('ed')
['e', 'd']
>>> list(3)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
list(3)
TypeError: 'int' object is not iterable
>>
So you are in fact passing a list with multiple elements to your method, which is why len is returning greater than 1. It's not returning the length of the first element of the list.
For your method to allow passing either a single item or a list, you'd have to do some checking to see if it's a single item first, and if it is create a list containing it with myVar = [myVar], then run your loop.
However this sort of API is tricky to implement and use, and I would not recommend it. The most natural way to check if you've been given a collection or an item is see if myVar is iterable. However this fails for strings, which are iterable. Strings unfortunately straddle the boundry between a collection and an individual data item; we very very often use them as data items containing a "chunk of text", but they are also collections of characters, and Python allows them to be used as such.
Such an API also is likely to cause you to one day accidentally pass a list that you're thinking of as a single thing and expecting the method to treat it as a single thing. But it's a list, so suddenly the code will behave differently.
It also raises questions about what you do with other data types. A dictionary is not a list, but it can be iterated. If you pass a dictionary as myVar, will it be treated as a list containing a single dictionary, or will it iterate over the keys of the dictionary? How about a tuple? What about a custom class implementing __iter__? What if the custom class implementing __iter__ is trying to be "string-like" rather than "list-like"?
All these questions lead to surprises if the caller guesses/remembers wrongly. Surprises when programming lead to bugs. IMHO, it's better to just live with the extra two characters of typing ([ and ]), and have your API be clean and simple.
I run into this same problem frequently. Building a list from an empty list, as you are doing with the "_dataHandle= list()" line, is common in Python because we don't reserve memory in advance. Therefore, it is often the case that the state of the list will transition from empty, to one element, to multiple elements. As you found, Python treats the indexing different for one element vs. multiple elements. If you can use list comprehension, then the solution can be simple. Instead of:
for i in range(_dataHandle):
use:
for myvar in _dataHandle:
In this case, if there is only one element, the loop only iterates once as you would expect.
def process_filter_description(filter, images, ial):
'''Return a new list containing only items from list images that pass
the description filter (a str). ial is the related image association list.
Matching is done in a case insensitive manner.
'''
images = []
for items in ial:
Those are the only two lines of code I have so far. What is troubling me is the filter in the function. I really don't know what the filter is supposed to do or how to use it.
In no way am I asking for the full code. I just want help with what the filter is supposed to do and how I can use it.
Like I said in my comment, this is really vague. But I'll try to explain a little about the concept of a filter in python, specifically the filter() function.
The prototype of filter is: iterable <- filter(function, iterable).
iterable is something that can be iterated over. You can look up this term in the docs for a more exact explanation, but for your question, just know that a list is iterable.
function is a function that accepts a single element of the iterable you specify (in this case, an element of the list) and returns a boolean specifying whether the element should exist in the iterable that is returned. If the function returns True, the element will appear in the returned list, if False, it will not.
Here's a short example, showing how you can use the filter() function to filter out all even numbers (which I should point out, is the same as "filtering in" all odd numbers)
def is_odd(i): return i%2
l = [1,2,3,4,5] # This is a list
fl = filter(is_odd, l)
print fl # This will display [1,3,5]
You should convince yourself that is_odd works first. It will return 1 (=True) for odd numbers and 0 (=False) for even numbers.
In practice, you usually use a lambda function instead of defining a single-use top-level function, but you shouldn't worry about that, as this is just fine.
But anyway, you should be able to do something similar to accomplish your goal.
Well it says in the description line:
Return a new list containing only items from list images that pass the description filter (a str)
...
Matching is done in a case insensitive manner
So.. im guessing the filter is just a string, do you have any kind of text associated with the images ? some kind of description or name that could be matched against the filter string ?
I have a class containing a list of strings. Say:
ClassName:
- list_of_strings
I need to enforce that this list of strings contains unique elements. Unfortunately, I can't change this list_of_strings to another type, like a set.
In the addToList(str_to_add) function, I want to guarantee string uniqueness. How can I best do this? Would it be practical to add the string being added to the list, convert to a set, then back to a list, and then reassign that to the object?
Here's the method I need to update:
def addToList(self, str_to_add):
self.list_of_strings.append(str_to_add)
Thanks!
def addToList(self, str_to_add):
if str_to_add not in self.list_of_strings:
self.list_of_strings.append(str_to_add)
Either check for the presence of the string in the list with in, or use a set in parallel that you can check and add to.
You indeed could do the list-to-set-to-list operation you described, but you could also use the in operator to check if the element is already in the list before appending it.
One possible way to do this would be to create a hash set and iterate through the list, adding the elements to the set; a second iteration could be used to remove any duplicates.
Perhaps we can do like this:
def addToList(self, str_to_add):
try:
self.list_of_strings.index(str_to_add)
except:
self.list_of_strings.append(str_to_add)
Well, I don't know whether it's the same mechanism with if/else yet.