[Python-Dev] PEP 463: Exception-catching expressions

Chris Angelico rosuav at gmail.com
Sat Feb 22 12:13:58 CET 2014


On Sat, Feb 22, 2014 at 9:17 PM, Antoine Pitrou <solipsis at pitrou.net> wrote:
> On Sat, 22 Feb 2014 21:09:07 +1100
> Chris Angelico <rosuav at gmail.com> wrote:
>> On Sat, Feb 22, 2014 at 8:58 PM, Antoine Pitrou <solipsis at pitrou.net> wrote:
>> > On Sat, 22 Feb 2014 20:29:27 +1100
>> > Chris Angelico <rosuav at gmail.com> wrote:
>> >>
>> >> Which means that, fundamentally, EAFP is the way to do it. So if PEP
>> >> 463 expressions had existed from the beginning, hasattr() probably
>> >> wouldn't have been written - people would just use an
>> >> except-expression instead.
>> >
>> > Really? hasattr() is much easier to write than the corresponding
>> > except-expression.
>>
>> But would it be sufficiently easier to justify the creation of a
>> built-in?
>
> Well, can you propose the corresponding except-expression?

It's hard to do hasattr itself without something messy - the best I
can come up with is this:

hasattr(x,"y") <-> (x.y or True except AttributeError: False)

but the bulk of uses of it are immediately before attempting to use
the attribute. Many require multiple statements, so they'd be better
done as a full try/except:

cpython/python-gdb.py:1392:
        if not hasattr(self._gdbframe, 'select'):
            print ('Unable to select frame: '
                   'this build of gdb does not expose a
gdb.Frame.select method')
            return False
        self._gdbframe.select()
        return True
becomes
        try:
            self._gdbframe.select()
            return True
        except AttributeError:
            print ('Unable to select frame: '
                   'this build of gdb does not expose a
gdb.Frame.select method')
            return False

but others are clearly expressions in disguise:

Lib/aifc.py:882:
        if hasattr(f, 'mode'):
            mode = f.mode
        else:
            mode = 'rb'
becomes
        mode = (f.mode except AttributeError: 'rb')

(In fact, I'm adding that one to the PEP's examples section.)

Lib/cgi.py:145:
    if hasattr(fp,'encoding'):
        encoding = fp.encoding
    else:
        encoding = 'latin-1'
becomes
    encoding = (fp.encoding except AttributeError: 'latin-1')

Some could be done either way. If hasattr didn't exist, then this:

Lib/argparse.py:597:
            if hasattr(params[name], '__name__'):
                params[name] = params[name].__name__

could be written instead as:
            params[name] = (params[name].__name__
                except AttributeError: params[name])

which is similar length and doesn't require a built-in.

Some are fairly clearly asking to be done as try/except, irrespective
of this PEP:

Lib/decimal.py:449:
    if hasattr(threading.current_thread(), '__decimal_context__'):
        del threading.current_thread().__decimal_context__
becomes
    try: del threading.current_thread().__decimal_context__
    except AttributeError: pass

(also ibid:476)

Some are a bit of a mixture.

Lib/dis.py:40:
    if hasattr(x, '__func__'):  # Method
        x = x.__func__
    if hasattr(x, '__code__'):  # Function
        x = x.__code__
    if hasattr(x, '__dict__'):  # Class or module
        ... lots more code ...

Could be done as try/except; first part could be done cleanly as an expression.

This one's not quite as clean, but if hasattr didn't exist, this could
be done either of two ways:

Lib/fileinput.py:342:
                        if hasattr(os, 'O_BINARY'):
                            mode |= os.O_BINARY
As an expression:
                        mode |= (os.O_BINARY except AttributeError: 0)
Or as a statement:
                        try: mode |= os.O_BINARY
                        except AttributeError: pass

This one definitely would want to be changed, and is also going in the PEP:

Lib/inspect.py:1350:
    return sys._getframe(1) if hasattr(sys, "_getframe") else None
becomes
    return (sys._getframe(1) except AttributeError: None)

Lib/ntpath.py:558:
# Win9x family and earlier have no Unicode filename support.
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
                              sys.getwindowsversion()[3] >= 2)
becomes
supports_unicode_filenames = (sys.getwindowsversion()[3] >= 2
                              except AttributeError: False)

Another ternary-if LBYL that could become an expression-except EAFP:
Lib/pdb.py:745:
        globs = self.curframe.f_globals if hasattr(self, 'curframe') else None
becomes
        globs = (self.curframe.f_globals except AttributeError: None)
although that will return None if self.curframe has no f_globals.

Another nice easy one:
Lib/pickletools.py:2227:
    if hasattr(data, "tell"):
        getpos = data.tell
    else:
        getpos = lambda: None
becomes
    getpos = (data.tell except AttributeError: lambda: None)

I could continue this theme, but behold, as Rose Maybud said, I have
said enough.

There are definitely cases where a local hasattr function could be
useful, but if the code were already written to use try/except or an
except-expression, there aren't many that would justify the creation
of a builtin.

ChrisA


More information about the Python-Dev mailing list