Which is faster in Python: if or try
<p>Please consider the following code:</p>

<pre><code>def readPath(path):
   content = None
   if os.path.isfile(path):
      f = open(path,"rb")
      content = f.read()
      f.close()
   return content
</code></pre>

<p>versus this one:</p>

<pre><code>def readPath(path):
   content = None
   try:
      f = open(path,"rb")
      content = f.read()
      f.close()
   except:
     pass
   return content
</code></pre>

<p>Given that the def is called many (hundreds to thousands times) in succession, mostly with valid paths (that represent actual files on the file system) but sometimes with non existent paths, which version is more efficient? Is checking a condition prior to opening the file is slower than setting a try block?</p>

<p>Usually, the answer to "whichever to use, <code>if</code> or <code>try</code>", the answer is <strong>EAFP</strong> - it is easier to ask for forgiveness than permission, thus always prefer <code>try</code> over <code>if</code>. However, in this case, neither EAFP nor performance considerations shouldn't be reason to choose one over another. Instead you should choose the one that is correct.</p>

<p>Using <code>isfile</code> because it makes your code prone to race conditions. Suppose someone removes the file just after you called <code>isfile</code> and before you actually opened it - and you would get a spurious exception. However, there have been countless of security bugs because of code that first checks for existence, permissions, ownership or so on, of a file, and then open it - an attacker may change the file that the link is pointing to in between.</p>

<p>Furthermore, there are other reasons why <code>open</code> would fail than just the file not existing:</p>

<pre><code>&gt;&gt;&gt; os.path.isfile('/etc/shadow')
True
&gt;&gt;&gt; open('/etc/shadow')
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
PermissionError: [Errno 13] Permission denied: '/etc/shadow'
</code></pre>

<p><code>isfile</code> also amounts to an <em>extra</em> system call and that in itself is more expensive than catching an exception; just doing the system call alone makes the overhead of catching an exception minuscule in comparison. As you have stated that you expect most of the file names to actually exist, the time spent in <code>isfile</code> is just extra overhead.</p>

<p>I did some timings with Python 3 on Ubuntu 16.04, and <code>os.path.isfile</code> for a non-existent file took <code>/etc/foo</code> ~2 Âµs (it is actually faster for an existing file <code>/etc/passwd</code>, 1.8 Âµs); trying to open a non-existent file and failing and catching the <code>IOError</code> took ~3 Âµs - so checking for the existence of a file using <code>os.path.isfile</code> is faster than checking for its existence using <code>open</code>; but that's not what your code needs to do: it needs to read the contents of a file that can be read, and return its contents, and for that, if 66 % of the files are expected to exist, then <code>open</code> without checking is absolutely faster than using <code>isfile</code> to check first, so this should be a no-brainer.</p>

<hr>

<p>P.S: your code possibly leaks an open file on other Pythons than CPython, and is unnecessarily complicated. Using the <code>with</code> block, this becomes much cleaner:</p>

<pre><code>def read_path(path):
    try:
        with open(path, "rb") as f:
           return f.read()
    except IOError:
        return None
</code></pre>

python