except clause syntax question

C

Charles Yeomans

To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

The latter makes more sense semantically to me -- "catch all exception types in a list" as opposed to "catch this single thing composed of three exception types".


Charles Yeomans
 
M

Mel Wilson

Charles said:
To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

The latter makes more sense semantically to me -- "catch all exception
types in a list" as opposed to "catch this single thing composed of three
exception types".

I've always been perfectly fine with sometimes treating tuples as immutable
sequences, so I'm +0 on saying "more sense semantically", but given that the
exception list can be a variable, I'm not sure what the gain is by keeping
it immutable.

#-----------------------------------------------------------
#!/usr/bin/env python
# -*- coding: ASCII -*-
'''Demonstrate catching variable exceptions.
'''
def excepter (a, exceptions):
try:
1.0/a
'Number ' + a
except exceptions as e:
print '!!! *** EXCEPTER CAUGHT ONE *** !!!'
print repr (e)

#~ excepter (0, [ZeroDivisionError])
excepter (0, (ZeroDivisionError,))
excepter (1, (ZeroDivisionError,TypeError))
excepter (1, (ZeroDivisionError,))

#-----------------------------------------------------------

excepter called with the list catches nothing, of course.


Mel.
 
S

Steven D'Aprano

To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

Simplicity.

If you also allow lists, then why not allow arbitrary sequences? What
about iterators, do you allow them? That could be awkward, because
iterators can only be run through once. Dictionaries are also iterable,
so once you allow arbitrary iterables, you get dicts. The whole thing
becomes a mess. Better to keep it simple and only allow a single
canonical collection type, and in Python, that type is tuple, not list.

Tuples are that canonical collection type because they have a number of
desirable properties:

- Tuples are small and memory efficient, using the smallest amount of
memory needed to hold their items. Lists typically carry a block of
spare memory, to make insertions fast.

- Consequently the Python virtual machine can create them rapidly and
efficiently.

- Tuples are immutable, so you don't have to worry about passing one to a
function and having the function modify it behind your back.

- Tuples are ordered, for the times where that matters.

- Since the typical use-case is to iterate over the items in fixed order,
there's no need to pay the extra expense for a dict or set.

- Tuples are simple to write: in general you only need commas between
items. Sometimes, to avoid ambiguity or change the precedence of
calculation, you also need round brackets (parentheses for Americans).
Except clauses are one of those times.

- Frozensets and sets are ruled out for historical reasons: they didn't
exist until Python 2.3. Besides, which would you rather write?

("abc", "def")
frozenset([abc", "def"])

- Sets and lists are ruled out because they are mutable, both require
much more memory, and sets have a heavier computational burden.


The latter makes more sense semantically to me -- "catch all exception
types in a list" as opposed to "catch this single thing composed of
three exception types".

Then you are labouring under a misunderstanding. You're not catching a
tuple, because tuples are never thrown. You're catching any of the
exceptions that are contained in that tuple.

Both lists and tuples *are* single things in themselves. Both lists and
tuples are containers:

A list is a single thing that contains other things.

A tuple is a single thing that contains other things.
 
D

Devin Jeanpierre

To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

Simplicity.
-snip-

I agree with the snipped, but would also like to add that regardless
of why it might be so, tuples do appear to be the canonical type for
collections that need to be typechecked -- not just for except; it's a
consistent thing that if you're going to do something with "X, or a
bunch of X's", then it's either an X or a tuple of X's. For example,
string formatting with % works this way, as does isinstance(a, X).

-- Devin
 
M

Mel Wilson

Charles said:
To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

The latter makes more sense semantically to me -- "catch all exception
types in a list" as opposed to "catch this single thing composed of three
exception types".

On reflection, it seems to hint at a style that Python extensions were made
in. (IIRC) the first operand in an `except` statement was originally just
an arbitrary marker to identify the exception. Unique string values were
customary, although the Python library defined things with standard
exception names. Using a string means that general exceptions weren't to be
collected in general sequences; `except "Serious Error"` was never meant to
catch `raise "r"`. If only tuples were used for collections, it would
create havoc for fewer of any weirdos who had used strange markers of their
own devising.

It looks like tuples were chosen as the most "lightweight", or maybe least
intrusive, sequence type to require to denote a collection of exceptions.

You see a similar decision, with the opposite emphasis, with the string
modulo operator. The second operand is supposed to be a tuple, but if the
template string needs only one value, then the rules are relaxed and any
single non-tuple value is used as-is.


Mel.
 
C

Charles Yeomans

To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

Simplicity.

If you also allow lists, then why not allow arbitrary sequences? What
about iterators, do you allow them? That could be awkward, because
iterators can only be run through once. Dictionaries are also iterable,
so once you allow arbitrary iterables, you get dicts. The whole thing
becomes a mess. Better to keep it simple and only allow a single
canonical collection type, and in Python, that type is tuple, not list.

Tuples are that canonical collection type because they have a number of
desirable properties:

- Tuples are small and memory efficient, using the smallest amount of
memory needed to hold their items. Lists typically carry a block of
spare memory, to make insertions fast.

- Consequently the Python virtual machine can create them rapidly and
efficiently.

- Tuples are immutable, so you don't have to worry about passing one to a
function and having the function modify it behind your back.

- Tuples are ordered, for the times where that matters.

- Since the typical use-case is to iterate over the items in fixed order,
there's no need to pay the extra expense for a dict or set.

- Tuples are simple to write: in general you only need commas between
items. Sometimes, to avoid ambiguity or change the precedence of
calculation, you also need round brackets (parentheses for Americans).
Except clauses are one of those times.

- Frozensets and sets are ruled out for historical reasons: they didn't
exist until Python 2.3. Besides, which would you rather write?

("abc", "def")
frozenset([abc", "def"])

- Sets and lists are ruled out because they are mutable, both require
much more memory, and sets have a heavier computational burden.


The latter makes more sense semantically to me -- "catch all exception
types in a list" as opposed to "catch this single thing composed of
three exception types".

Then you are labouring under a misunderstanding. You're not catching a
tuple, because tuples are never thrown. You're catching any of the
exceptions that are contained in that tuple.

Both lists and tuples *are* single things in themselves. Both lists and
tuples are containers:

A list is a single thing that contains other things.

A tuple is a single thing that contains other things.

I don't think of a tuple as a container, and I don't think it a misunderstanding on my part to think this. But I am aware that it is common to use tuples as immutable lists.

I don't see that performance was really a consideration, given that one can use any expression in an except statement --

except IOError if today == 'Monday' else OSError as e

or

L = []
try:
#code

except tuple(L) as e:
pass

except Exception, e:
L.append(e.__class__)

In any case, though I appreciate your attempt at a post hoc justification, I was hoping for a positive explanation.

Charles Yeomans
 
S

Steven D'Aprano

I don't think of a tuple as a container, and I don't think it a
misunderstanding on my part to think this.

Well, it is a misunderstanding, because tuples ARE containers. You might
as well say "I don't think of boxes as containers". What exactly are they
if not containers?
 
C

Charles Yeomans

Well, it is a misunderstanding, because tuples ARE containers. You might
as well say "I don't think of boxes as containers". What exactly are they
if not containers?


Tuple is a heterogenous datatype that allows one to define objects ad hoc. That is to say, a tuple represents a single thing distinct from its components. For example, suppose you need to represent a location in text by line number and offset within a line. A tuple object makes it easy to do so without writing a class having no methods other than a constructor. Here, the components, a line number and an offset, define a new object distinct from the pieces.

One can certainly view a tuple as a list, just as one can view a string as a list of characters, and sometimes that's useful; the Python dictum "there should only be one way to do it" doesn't imply that there is only one way to think of it.

Nor am I the only person who sees such a distinction between tuple and list.


Charles Yeomans
 
D

Devin Jeanpierre

Tuple is a heterogenous datatype that allows one to define objects ad hoc.. That is to say, a tuple represents a single thing distinct from its components.  For example, suppose you need to represent a location in text by line number and offset within a line.  A tuple object makes it easyto do so without writing a class having no methods other than a constructor.  Here, the components, a line number and an offset, define a new object distinct from the pieces.

One can certainly view a tuple as a list, just as one can view a string as a list of characters, and sometimes that's useful; the Python dictum "there should only be one way to do it" doesn't imply that there is only one way to think of it.

Nor am I the only person who sees such a distinction between tuple and list.

Perhaps it'd be useful to look at how the Python language reference
defines containers?

Quote:

Some objects contain references to other objects; these are called
containers. Examples of containers are tuples, lists and dictionaries.
The references are part of a container’s value. In most cases, whenwe
talk about the value of a container, we imply the values, not the
identities of the contained objects; however, when we talk about the
mutability of a container, only the identities of the immediately
contained objects are implied. So, if an immutable container (like a
tuple) contains a reference to a mutable object, its value changes if
that mutable object is changed.

End quote.
(Offtopic: How do I do an external block quote appropriately in an email?)

Tuples are most certainly containers, precisely _because_ they're an
ad-hoc way to define objects, where the only purpose of the object is
to contain the values inside the tuple.

But these are just words and it's beside the point. We should talk of
things as if the word "container" didn't matter, because I don't think
that's what you meant, but neither do I want to put words in your
mouth.

My interpretation is that you see tuples as an object where you can
get meaningfully things by field, rather than just grab arbitrarily
from a bag of things. This isn't the only way they are used, see the
except statement (hee) and their use as keys in dictionaries. But it
is true that their immutable length makes them very well suited to the
task of representing product types.

-- Devin
 
E

Ethan Furman

Charles said:
Tuple is a heterogenous datatype that allows one to define objects
ad hoc.

And any object can be seen as a container for its component pieces --
some are just more general than others.

Compare:

location = (13, 4, 9) # line, word, char
time = (10, 15, 41) # hour, minute, second
result = ('this', 'that', 'huh') # result a, result b, result c

with:

record1 = Record('Ethan', 41, Male)
record2 = Record('Charles', 37, Male)
record3 = Record('Steven', 43, Male)
record4 = Record('Jennifer', 39, Female)

In this example, Records have a set layout and so it is more common to
think of a Record as a thing; location, time, and result, however, are
all random tuples created on the fly with no absolute restrictions on
what goes in which position.

lists, dicts, sets, and tuples are general purpose containers; strs (and
most user defined classes) are special purpose containers.
That is to say, a tuple represents a single thing distinct from its
components.

You could say that about a list as well. Doesn't change the fact that a
list is a container.
One can certainly view a tuple as a list, just as one can view a string
as a list of characters, and sometimes that's useful; the Python dictum
"there should only be one way to do it" doesn't imply that there is only
one way to think of it.

The 'dictum' is "there should only be one *obvious* way to do it"
(emphasis added).

~Ethan~
 
C

Charles Yeomans

Perhaps it'd be useful to look at how the Python language reference
defines containers?

Quote:

Some objects contain references to other objects; these are called
containers. Examples of containers are tuples, lists and dictionaries.
The references are part of a container’s value. In most cases, when we
talk about the value of a container, we imply the values, not the
identities of the contained objects; however, when we talk about the
mutability of a container, only the identities of the immediately
contained objects are implied. So, if an immutable container (like a
tuple) contains a reference to a mutable object, its value changes if
that mutable object is changed.

End quote.
(Offtopic: How do I do an external block quote appropriately in an email?)

Tuples are most certainly containers, precisely _because_ they're an
ad-hoc way to define objects, where the only purpose of the object is
to contain the values inside the tuple.

But these are just words and it's beside the point. We should talk of
things as if the word "container" didn't matter, because I don't think
that's what you meant, but neither do I want to put words in your
mouth.

My interpretation is that you see tuples as an object where you can
get meaningfully things by field, rather than just grab arbitrarily
from a bag of things. This isn't the only way they are used, see the
except statement (hee) and their use as keys in dictionaries. But it
is true that their immutable length makes them very well suited to the
task of representing product types.

I had read that bit of documentation, and don't entirely agree with it. Certainly many objects contain references to other objects, but are not considered containers.

And I claim that tuples are most certainly not containers, when they are used to define an object as a collection of other objects, like the text-location example I offered earlier. On the other hand, it is certainly true that tuples quack like containers, as do strings.


Charles Yeomans
 
C

Charles Yeomans

Charles said:
To catch more than one exception type in an except block, one writes

except (A, B, C) as e:

I'm wondering why it was decided to match tuples, but not lists:

except [A, B, C] as e:

The latter makes more sense semantically to me -- "catch all exception
types in a list" as opposed to "catch this single thing composed of three
exception types".

On reflection, it seems to hint at a style that Python extensions were made
in. (IIRC) the first operand in an `except` statement was originally just
an arbitrary marker to identify the exception. Unique string values were
customary, although the Python library defined things with standard
exception names. Using a string means that general exceptions weren't to be
collected in general sequences; `except "Serious Error"` was never meant to
catch `raise "r"`. If only tuples were used for collections, it would
create havoc for fewer of any weirdos who had used strange markers of their
own devising.

It looks like tuples were chosen as the most "lightweight", or maybe least
intrusive, sequence type to require to denote a collection of exceptions.

You see a similar decision, with the opposite emphasis, with the string
modulo operator. The second operand is supposed to be a tuple, but if the
template string needs only one value, then the rules are relaxed and any
single non-tuple value is used as-is.

Compatilbility; that makes sense. I came to python well after strings were used for exceptions. Thanks.


Charles Yeomans
 
T

Terry Reedy

In any case, though I appreciate your attempt at a post hoc justification,
I was hoping for a positive explanation.

I think the best you are going to get is that Python somewhat
consistently*, for both practical and historical reasons#, uses tuples
when the syntax allows an object or collection of objects.

* except, isinstance, isubclass, ''%x, perhaps other places.

In the last case, that creates a problem when one wants to interpolate a
tuple as an object rather than having it viewed as a container of
several objects to be interpolated. That was on

# Python once treated tuples as different from lists in ways that is not
true now. (Read the 1.5 docs if really interested.)
 
C

Charles Yeomans

I think the best you are going to get is that Python somewhat consistently*, for both practical and historical reasons#, uses tuples when the syntax allows an object or collection of objects.

* except, isinstance, isubclass, ''%x, perhaps other places.

In the last case, that creates a problem when one wants to interpolate a tuple as an object rather than having it viewed as a container of several objects to be interpolated. That was on

# Python once treated tuples as different from lists in ways that is not true now. (Read the 1.5 docs if really interested.)


I'll do that. Thanks.


Charles Yeomans
 
C

Chris Angelico

Abitrarily nested tuples of exceptions cannot contain loops so the code
simply needs to walk through the tuples until it finds a match.

Is this absolutely guaranteed? The C API for CPython provides:
(Py2) http://docs.python.org/c-api/tuple.html#PyTuple_SetItem
(Py3) http://docs.python.org/dev/c-api/tuple.html#PyTuple_SetItem

which doesn't have massive warnings on it saying "USE THIS ONLY TO
INITIALIZE A TUPLE" (compare, for instance, _PyTuple_Resize which does
carry a similar warning). Is the assumption then that we're all
adults, and that mutating a tuple is like passing a null pointer to an
API function (aka "loaded gun in proximity to foot")?

ChrisA
 
I

Ian Kelly

Is this absolutely guaranteed? The C API for CPython provides:
(Py2) http://docs.python.org/c-api/tuple.html#PyTuple_SetItem
(Py3) http://docs.python.org/dev/c-api/tuple.html#PyTuple_SetItem

which doesn't have massive warnings on it saying "USE THIS ONLY TO
INITIALIZE A TUPLE" (compare, for instance, _PyTuple_Resize which does
carry a similar warning). Is the assumption then that we're all
adults, and that mutating a tuple is like passing a null pointer to an
API function (aka "loaded gun in proximity to foot")?

I don't know why the docs are written the way that they are, but if
you check the code, you can see that PyTuple_SetItem will raise a
SystemError if the reference count is anything other than 1. So I
think that it is only meant to be used with similar caution and
restraint.

Cheers,
Ian
 
I

Ian Kelly

I don't know why the docs are written the way that they are, but if
you check the code, you can see that PyTuple_SetItem will raise a
SystemError if the reference count is anything other than 1.  So I
think that it is only meant to be used with similar caution and
restraint.

Incidentally, I *think* that any correctly written C code attempting
to nest a tuple inside itself would make the reference count of the
tuple be at least 2 at the time of the call, and so it would fail.
 
C

Chris Angelico

Incidentally, I *think* that any correctly written C code attempting
to nest a tuple inside itself would make the reference count of the
tuple be at least 2 at the time of the call, and so it would fail.

Good, nice that that's certain :)

Might be worth moving the "ecause tuples are supposed to be
immutable" warning up to the top of the page then, since the bulk of
it applies to all those functions and not just resize.

ChrisA
 
M

Mel Wilson

Chris said:
Is this absolutely guaranteed? The C API for CPython provides:
(Py2) http://docs.python.org/c-api/tuple.html#PyTuple_SetItem
(Py3) http://docs.python.org/dev/c-api/tuple.html#PyTuple_SetItem

which doesn't have massive warnings on it saying "USE THIS ONLY TO
INITIALIZE A TUPLE" (compare, for instance, _PyTuple_Resize which does
carry a similar warning). Is the assumption then that we're all
adults, and that mutating a tuple is like passing a null pointer to an
API function (aka "loaded gun in proximity to foot")?

Unfortunately, I can't remember the details now, but I once set out to
create a recursive tuple by using the C API, and it turned out then that the
C API went to some lengths to prevent anyone being able to do that. I did
finally do it in some peculiar way, but it wasn't simple. The c.l.python
archives might still have the post where I described it.

Mel.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,997
Latest member
mileyka

Latest Threads

Top