What I don't understand is the assignment of a tuple in the except clause.
The O'Reilly Nutshell book says "The optional target is an identifier that
names a variable that Python binds to the exception object just before the
exception handler executes". This doesn't make sense in this case:
except IOError, (errno, strerror):
The target is a tuple of 2 variable names so how can it bind "the exception
object" to them? The documentation for try/except says that "the
exception's parameter is assigned to the target". So what, exactly, is an
"exception's parameter"?
> What I don't understand is the assignment of a tuple in the except
> clause.
> The O'Reilly Nutshell book says "The optional target is an identifier
> that
> names a variable that Python binds to the exception object just before
> the
> exception handler executes". This doesn't make sense in this case:
>
> except IOError, (errno, strerror):
>
> The target is a tuple of 2 variable names so how can it bind "the
> exception
> object" to them? The documentation for try/except says that "the
> exception's parameter is assigned to the target". So what, exactly, is
> an
> "exception's parameter"?
This is just using tuple unpacking in assignment:
>>> tup = 1, 2, 3
>>> x, y, z = tup
>>> x
1
>>> y
2
>>> z
3
This syntax can be used anywhere an lvalue is allowed, which might
include the "variable" of a for statement:
>>> for x, y in [(1, 2), (3, 4), (5, 6)]:
... print x, y
...
1 2
3 4
5 6
and the "variable" in a try/except clause is just another example of
this. The "exception's parameter" is a 2-tuple, consisting of the errno
and the strerror.
--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \
\__/ Yes I'm / Learning from falling down / Heavily
-- Lamya
OK, that makes a lot of sense. However, the thing being unpacked here isn't
a tuple - it's an object of type IOError. How does an object know how to
unpack itself when used in an assignment like this? More specifically, how
does the IOError object know to return its errno and strerror values when
it gets assigned to a 2 value tuple?
> OK, that makes a lot of sense. However, the thing being unpacked here
> isn't
> a tuple - it's an object of type IOError. How does an object know how
> to
> unpack itself when used in an assignment like this? More specifically,
> how
> does the IOError object know to return its errno and strerror values
> when
> it gets assigned to a 2 value tuple?
Because the Exception class, which IOError is derived from, works that
way:
>>> e = Exception(1, 2, 3)
>>> tuple(e)
(1, 2, 3)
>>> x, y, z = e
>>> x
1
>>> y
2
>>> z
3
The "exception parameter" is an exception object, which knows how to
behave like a tuple.
OK, and what gives it that ability? I tried tuple(f), where f was a file
object. It gave me the contents of the file! I tried it again on an
instance of one of my own objects and got a "TypeError: iteration over
non-sequence" exception.
It must be possible to give a class the ability to present itself as a
tuple. How is that done?
> OK, and what gives it that ability? I tried tuple(f), where f was a
> file
> object. It gave me the contents of the file! I tried it again on an
> instance of one of my own objects and got a "TypeError: iteration over
> non-sequence" exception.
>
> It must be possible to give a class the ability to present itself as a
> tuple. How is that done?
The tuple function can work with instances which support an iterating
interface. (This is why you were seeing this behavior with a file
object; iterating over a file object gives you the lines in sequence.)
>>> class C:
... def __init__(self, x):
... self.x = x
... def __getitem__(self, i):
... if i < self.x:
... return i**2
... else:
... raise IndexError
...
>>> c = C(10)
>>> tuple(c)
(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
And that's the final piece of the puzzle! I can now claim to understand it.
Many thanks.
> And that's the final piece of the puzzle! I can now claim to
> understand it.
> Many thanks.
Sure thing. By the way, that little explanation is a hint at one of the
key insights to Python. A lot of features in Python work like that --
you don't have to provide dedicated support for a certain builtin
function to do its work (e.g., using the `tuple' function to make a
tuple out of an arbitrary object), you only need to make the object
conform to the relevant interface. In this case, it was the iteration
interface -- tuple will work with any object that can be iterated over.
So exactly the same property that allows you to write:
for x in myObject:
...
is precisely the same one that makes
tuple(myObject)
meaningful and do what you'd expect. For instance, to get a file-like
object, you don't actually need a literal file object (created with
`file'), you just need something that has the relevant methods (read,
readlines, write, writelines, flush, close, or a subset). Often these
sorts of interfaces are orthogonal, so that's why you saw that native
file objects actually can behave as iteratable objects as well -- all
they need to do is support read/write/flush/close and __getitem__ in the
expected way (start from 0, raise IndexError when you're done).
--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \
\__/ You're wasting time / Asking what if / You linger on too long
-- Chante Moore
>Derek Fountain wrote:
>
>> OK, and what gives it that ability? I tried tuple(f), where f was a
>> file
>> object. It gave me the contents of the file! I tried it again on an
>> instance of one of my own objects and got a "TypeError: iteration over
>> non-sequence" exception.
>>
>> It must be possible to give a class the ability to present itself as a
>> tuple. How is that done?
>
>The tuple function can work with instances which support an iterating
>interface. (This is why you were seeing this behavior with a file
>object; iterating over a file object gives you the lines in sequence.)
>
>>>> class C:
>... def __init__(self, x):
>... self.x = x
>... def __getitem__(self, i):
>... if i < self.x:
>... return i**2
>... else:
>... raise IndexError
>...
Hmm. More generally you have to implement __iter__ for a class to be
iterable, be in for loops => can be list-ified, tuple-ified,
etc.-ified.
>>> class C(object):
... def __init__(self, x):
... self.x = x
... def __iter__(self):
... return iter(range(self.x))
...
>>> c = C(9)
>>> for elem in c:
... print elem
...
0
1
2
3
4
5
6
7
8
>>> tuple(c)
(0, 1, 2, 3, 4, 5, 6, 7, 8)
>>> list(c)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> 2 in c
True
>>> "2" in c
False
>>>
With my best regards,
G. Rodrigues
> Hmm. More generally you have to implement __iter__ for a class to be
> iterable, be in for loops => can be list-ified, tuple-ified,
> etc.-ified.
Defining an explicit __iter__ method is another way, and is particularly
useful when you're dealing with a class whose iteration can be
encapsulated easily in a separate iterator object that you can return
(and you're running Python 2.2 or up). That isn't always the case.
--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \
\__/ Golf is a good walk spoiled.
-- Mark Twain
The use of tuple unpacking that surprised me most is in
function arguments:
Python 2.3 (#46, Jul 29 2003, 18:54:32) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def f ( (a,b) ):
... print a, b
...
>>> x = (1,2)
>>> f(x)
1 2
>>>
It almost looks like Prolog unification (but isn't
really.)
Regards. Mel.
That's a trick! Is it used? Does anybody use assignments like this:
(a,((b,(c,)),d,e)) = (1,((2,(3+4,)),5,6))
--
Georgy Pruss
E^mail: 'ZDAwMTEyMHQwMzMwQGhvdG1haWwuY29t\n'.decode('base64')
A quick scan of the 2.3 standard library shows that it is used, but
only nestred to one level:
* bdb.py(144): def user_exception(self, frame, (exc_type, exc_value,
exc_traceback)):
* binhex.py(181): def __init__(self, (name, finfo, dlen, rlen),
ofp):
* cgitb.py(168): def text((etype, evalue, etb), context=5):
* cgitb.py(82): def html((etype, evalue, etb), context=5):
* formatter.py(233): def push_font(self, (size, i, b, tt)):
* imputil.py(278): def _process_result(self, (ispkg, code, values),
fqname):
* inspect.py(507): def tokeneater(self, type, token, (srow, scol),
(erow, ecol), line):
* modulefinder.py(258): def load_module(self, fqname, fp, pathname,
(suffix, mode, type)):
* pdb.py(134): def user_exception(self, frame, (exc_type, exc_value,
exc_traceback)):
* pydoc.py(197): def __init__(self, filename, (exc, value, tb)):
* threading.py(126): def _acquire_restore(self, (count, owner)):
* tokenize.py(135): def printtoken(type, token, (srow, scol), (erow,
ecol), line): # for testing
--
Emile van Sebille
em...@fenx.com