Solidify loose ends.

This commit is contained in:
Guido van Rossum 2005-05-03 16:32:24 +00:00
parent 79c7f789bd
commit 45c25ac443
1 changed files with 58 additions and 41 deletions

View File

@ -22,10 +22,6 @@ Introduction
(Reliable Acquisition/Release Pairs), and PEP 325
(Resource-Release Support for Generators).
This proposal is just a strawman; we've had a heated debate about
this on python-dev recently [1], and I figured it would be time to
write up a precise spec in PEP form.
Motivation and Summary
(Thanks to Shane Hathaway -- Hi Shane!)
@ -70,7 +66,7 @@ Motivation and Summary
Use Cases
TBD. For now, see the Examples section near the end.
See the Examples section near the end.
Specification: the __next__() Method
@ -159,14 +155,15 @@ Specification: the Extended 'continue' Statement
This is also the case in the body of the block-statement proposed
below.
EXPR2 may contain commas; "continue 1, 2, 3" is equivalent to
"continue (1, 2, 3)".
Specification: the Anonymous Block Statement
A new statement is proposed with the syntax
block EXPR1 as VAR1:
BLOCK1
else:
BLOCK2
Here, 'block' and 'as' are new keywords; EXPR1 is an arbitrary
expression (but not an expression-list) and VAR1 is an arbitrary
@ -268,14 +265,14 @@ Specification: the Anonymous Block Statement
the limitations of all finalization semantics) that the block will
be resumed eventually.
I haven't decided yet whether the block-statement should also
allow an optional else-clause, like the for-loop, but I'm leaning
against it. I think it would be confusing, and emphasize the
Unlike the for-loop, the block-statement does not have an
else-clause. I think it would be confusing, and emphasize the
"loopiness" of the block-statement, while I want to emphasize its
*difference* from a for-loop. In addition, there are several
possible semantics for an else-clause.
possible semantics for an else-clause, and only a very weak use
case.
Specification: Generator Exception Handling
Specification: Generator Exit Handling
Generators will implement the new __next__() method API, as well
as the old argument-less next() method which becomes an alias for
@ -330,11 +327,10 @@ Specification: Generator Exception Handling
When __next__() is called with an argument that is not None, the
yield-expression that it resumes will return the value attribute
of the argument. If it resumes a yield-statement, the value is
ignored (or should this be considered an error?). When the
*initial* call to __next__() receives an argument that is not
None, the generator's execution is started normally; the
argument's value attribute is ignored (or should this be
considered an error?). When __next__() is called without an
ignored (this is similar to ignoring the value returned by a
function call). When the *initial* call to __next__() receives an
argument that is not None, TypeError is raised; this is likely
caused by some logic error. When __next__() is called without an
argument or with None as argument, and a yield-expression is
resumed, the yield-expression returns None.
@ -360,22 +356,20 @@ Specification: Generator Exception Handling
cases work differently; in Python, you cannot save the block for
later use, and you cannot test whether there is a block or not.
Loose Ends
Alternatives Considered
These are things that need to be resolved before accepting the
PEP.
- Many alternatives have been proposed for 'block', including '@'
and no keyword at all. I haven't seen a proposal for another
keyword that I like better than 'block' yet, and not using a
keyword at all makes many folks (including me) uncomfortable.
- Fill in the remaining TBD sections.
- Address Phillip Eby's proposal to have the block-statement use
- Phillip Eby has proposed to have the block-statement use
an entirely different API than the for-loop, to differentiate
between the two (a generator would have to be wrapped in a
decorator to make it support the block API).
- Decide on the keyword ('block', 'with', '@', nothing, or
something else?).
- Whether a block-statement should allow an else-clause.
between the two. A generator would have to be wrapped in a
decorator to make it support the block API. IMO this adds more
complexity with very little benefit; and we can't relly deny
that the block-statement is conceptually a loop -- it supports
break and continue, after all.
Comparison to Thunks
@ -418,7 +412,7 @@ Comparison to Thunks
and I'd be bummed if I couldn't write this as:
def findSomething(self, key, default=None):
block synchronized(self.lock):
block locking(self.lock):
for item in self.elements:
if item.matches(key):
return item
@ -427,7 +421,7 @@ Comparison to Thunks
This particular example can be rewritten using a break:
def findSomething(self, key, default=None):
block synchronized(self.lock):
block locking(self.lock):
for item in self.elements:
if item.matches(key):
break
@ -478,16 +472,17 @@ Comparison to Thunks
However, the use cases for multiple blocks seem elusive.
Alternatives Considered
TBD.
(Proposals have since been made to change the implementation of
thunks to remove most of these objections, but the resulting
semantics are fairly complex to explain and to implement, so IMO
that defeats the purpose of using thunks in the first place.)
Examples
1. A template for ensuring that a lock, acquired at the start of a
block, is released when the block is left:
def synchronized(lock):
def locking(lock):
lock.acquire()
try:
yield
@ -496,7 +491,7 @@ Examples
Used as follows:
block synchronized(myLock):
block locking(myLock):
# Code here executes with myLock held. The lock is
# guaranteed to be released when the block is left (even
# if by an uncaught exception).
@ -549,18 +544,40 @@ Examples
5. It is possible to nest blocks and combine templates:
def synchronized_opening(lock, filename, mode="r"):
block synchronized(lock):
def locking_opening(lock, filename, mode="r"):
block locking(lock):
block opening(filename) as f:
yield f
Used as follows:
block synchronized_opening("/etc/passwd", myLock) as f:
block locking_opening("/etc/passwd", myLock) as f:
for line in f:
print line.rstrip()
6. Coroutine example TBD.
6. It is possible to write a regular iterator with the
semantics of example 1:
class locking:
def __init__(self, lock):
self.lock = lock
self.state = 0
def __next__(self, arg=None):
# ignores arg
if self.state:
assert self.state == 1
self.lock.release()
self.state += 1
raise StopIteration
else:
self.lock.acquire()
self.state += 1
return None
def __exit__(self, type, value=None, traceback=None):
assert self.state in (0, 1, 2)
if self.state == 1:
self.lock.release()
raise type, value, traceback
Acknowledgements