70

In PHP there a function called isset() to check if something (like an array index) exists and has a value. How about Python?

I need to use this on arrays because I get "IndexError: list index out of range" sometimes.

I guess I could use try/catching, but that's a last resort.

Charles
  • 50,943
  • 13
  • 104
  • 142
Jonny
  • 15,955
  • 18
  • 111
  • 232
  • 2
    [EAFP:](http://docs.python.org/glossary.html#term-eafp) It's easier to ask for forgiveness than it is to get permission. – NullUserException Dec 20 '11 at 03:58
  • 5
    Your question asks about PHP arrays (which are roughly dictionaries in Python) but your error message references a list operation. A code sample would probably clear up some of the confusion. – David K. Hess Dec 20 '11 at 04:08
  • 1
    Also: "last resort". "Last revert" is something rather different. – Amadan Dec 20 '11 at 04:12
  • The thing is I was writing a script to convert a Google Docs spreadsheet matrix exported from CSV into something else. Sometimes if the last (most right) column was empty Google would ignore the last comma indicating the final column. – Jonny Dec 21 '11 at 11:58
  • This question makes little sense as posed, and seems to be based in a fundamental misconception. Python lists **cannot** have "holes" in the middle; every element is assigned a value. (Also, we don't call them "arrays".) – Karl Knechtel Aug 09 '22 at 07:51

6 Answers6

118

Look before you leap (LBYL):

if idx < len(array):
    array[idx]
else:
    # handle this

Easier to ask forgiveness than permission (EAFP):

try:
    array[idx]
except IndexError:
    # handle this

In Python, EAFP seems to be the popular and preferred style. It is generally more reliable, and avoids an entire class of bugs (time of check vs. time of use). All other things being equal, the try/except version is recommended - don't see it as a "last resort".

This excerpt is from the official docs linked above, endorsing using try/except for flow control:

This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements.

wim
  • 338,267
  • 99
  • 616
  • 750
  • Sometimes you just want to figure out if an index exists or which index it is (python lacks a switch statement of course). Also I'm not sure this is a race condition but I'm not an expert. Isn't a race condition when the act of doing something generates potential for some sort of looping madness? if I just check if an index exists this shouldn't affect the race-iness of the threads. I guess my actions after the check could affect other threads. – ekeyser Jan 16 '14 at 22:48
  • 1
    On the other hand (OTOH), EAFP is problematic because it entails traversing the call stack when an error occurs. – Darth Egregious May 27 '15 at 15:30
50

EAFP vs. LBYL

I understand your dilemma, but Python is not PHP and coding style known as Easier to Ask for Forgiveness than for Permission (or EAFP in short) is a common coding style in Python.

See the source (from documentation):

EAFP - Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

So, basically, using try-catch statements here is not a last resort; it is a common practice.

"Arrays" in Python

PHP has associative and non-associative arrays, Python has lists, tuples and dictionaries. Lists are similar to non-associative PHP arrays, dictionaries are similar to associative PHP arrays.

If you want to check whether "key" exists in "array", you must first tell what type in Python it is, because they throw different errors when the "key" is not present:

>>> l = [1,2,3]
>>> l[4]

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    l[4]
IndexError: list index out of range
>>> d = {0: '1', 1: '2', 2: '3'}
>>> d[4]

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    d[4]
KeyError: 4

And if you use EAFP coding style, you should just catch these errors appropriately.

LBYL coding style - checking indexes' existence

If you insist on using LBYL approach, these are solutions for you:

  • for lists just check the length and if possible_index < len(your_list), then your_list[possible_index] exists, otherwise it doesn't:

    >>> your_list = [0, 1, 2, 3]
    >>> 1 < len(your_list) # index exist
    True
    >>> 4 < len(your_list) # index does not exist
    False
    
  • for dictionaries you can use in keyword and if possible_index in your_dict, then your_dict[possible_index] exists, otherwise it doesn't:

    >>> your_dict = {0: 0, 1: 1, 2: 2, 3: 3}
    >>> 1 in your_dict # index exists
    True
    >>> 4 in your_dict # index does not exist
    False
    

Did it help?

jacobq
  • 11,209
  • 4
  • 40
  • 71
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • 1
    Great answer. Is there any reason why undefined properties or indexes simply don't return as `undefined` or bool `false` in Python? Other language interpreters return on these events and I'm just curious why Python doesn't. – 65Fbef05 Feb 06 '12 at 15:10
  • @65Fbef05: I believe it is a design decision, but in fact you are able to do `my_dict.get('some_index', False)` for example, so you are not so limited. Also should Python really return `False` when you are trying to get some entry from the dictionary, when it does not exist? I believe no, because it would be hard to find the problem in the code, if storing `False` and not storing anything for some specific key gives the same result. I believe this quite a good idea to throw error when something you wanted is not here and you did not explicitly say to deal with such case (eg. by using `get()`). – Tadeck Feb 06 '12 at 16:37
  • 1
    One specific case where I think returning `False` would prove more helpful than throwing an error is when you are unsure if an index exists, like in the case of optional CLI arguments where the practical exception to your application logic only happens when there IS a value. – 65Fbef05 Feb 07 '12 at 04:18
  • @65Fbef05: There is no need for throwing exceptions when some specific CLI arguments exist or not, you can just use [`argparse` module](http://docs.python.org/library/argparse.html#module-argparse). And I do not remember any language that actually throws an error when there is a value it looks for. Or I misunderstood your comment ;) Anyway, as I said, you can support default argument by using something similar to `cli_args.get('option1', False)`. – Tadeck Feb 07 '12 at 07:22
  • I didn't necessarily mean "throw an error" when a value is found to exists. I meant more or less "do something different" than the standard routine. – 65Fbef05 Feb 07 '12 at 13:36
  • @65Fbef05: Then maybe something similar to switch/case/default is something you want, eg. `routines.get(some_option, standard_routine)(args)`? – Tadeck Feb 07 '12 at 13:40
15
`e` in ['a', 'b', 'c']  # evaluates as False
`b` in ['a', 'b', 'c']  # evaluates as True

EDIT: With the clarification, new answer:

Note that PHP arrays are vastly different from Python's, combining arrays and dicts into one confused structure. Python arrays always have indices from 0 to len(arr) - 1, so you can check whether your index is in that range. try/catch is a good way to do it pythonically, though.

If you're asking about the hash functionality of PHP "arrays" (Python's dict), then my previous answer still kind of stands:

`baz` in {'foo': 17, 'bar': 19}  # evaluates as False
`foo` in {'foo': 17, 'bar': 19}  # evaluates as True
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Hm? I was asking about if an indexed element exists in the array, not if a value exists in the array. – Jonny Dec 20 '11 at 04:02
  • @Jonny: This is one of the differences between PHP and Python. A PHP arrays is a [Dictionary](http://docs.python.org/library/stdtypes.html#dict) in Python. In python a [List](http://docs.python.org/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange) just has integer indexes. – unholysampler Dec 20 '11 at 04:10
11

has_key is fast and efficient.

Instead of array use an hash:

valueTo1={"a","b","c"}

if valueTo1.has_key("a"):
        print "Found key in dictionary"
Tom Leese
  • 19,309
  • 12
  • 45
  • 70
Arik Harel
  • 119
  • 1
  • 2
  • 4
    ```AttributeError: 'set' object has no attribute 'has_key' ``` – alamin Sep 26 '18 at 16:13
  • Python does not have anything called a "hash"; you seem to have had in mind using a *dictionary* (called `dict` in Python source code), but you show using a *set* (and then try to use a `dict` method with it). `has_key` [only exists in 2.x, and was deprecated very early on](https://stackoverflow.com/questions/1323410). Most importantly, these tools cannot replace a list in general. This answer is completely wrong. – Karl Knechtel Aug 09 '22 at 07:48
2

You may be able to use the built-in function dir() to produce similar behavior to PHP's isset(), something like:

if 'foo' in dir():  # returns False, foo is not defined yet.
    pass

foo = 'b'

if 'foo' in dir():  # returns True, foo is now defined and in scope.
   pass

dir() returns a list of the names in the current scope, more information can be found here: http://docs.python.org/library/functions.html#dir.

DRH
  • 7,868
  • 35
  • 42
0

In Python you might be in for some surprises if you ask for forgiveness in this case.

try-except is not the right paradigm here.

If you accidentally get negative indices your in for a surprise.

Better solution is to provide the test function yourself:

def index_in_array(M, index):
    return index[0] >= 0 and index[1] >= 0 and index[0]< M.shape[0] and index[1] < M.shape[1]