Article of interest: Python pros/cons for the enterprise
jeff at schwabcenter.com
Sat Feb 23 17:40:13 CET 2008
Paul Rubin wrote:
> Jeff Schwab <jeff at schwabcenter.com> writes:
>> The most traditional, easiest way to open a file in C++ is to use an
>> fstream object, so the file is guaranteed to be closed when the
>> fstream goes out of scope.
> Python has this too, except it's using a special type of scope
> created by the "with" statement.
Yes, this seems to be the Python way: For each popular feature of some
other language, create a less flexible Python feature that achieves the
same effect in the most common cases (e.g. lambda to imitate function
literals, or recursive assignment to allow x = y = z).
>> CPython offers a similar feature, since
>> you can create a temporary object whose reference count will become
>> zero at the end of the statement where it is defined:
>> $ echo world >hello
>> $ python
>> >>> file('hello').read()
> CPython does not guarantee that the reference count will become zero
> at the end of the statement. It only happens to work that way in your
> example, because the file.read operation doesn't make any new
> references to the file object anywhere.
It doesn't "happen" to work that way in the example; it works that way
by design. I see what you're saying, though, and it is a good point.
Given a statements of the form:
The method body could create an external reference to the instance of
some_class, such that the instance would not be reclaimed at the end of
> Other code might well do
> something different, especially in a complex multi-statement scope.
> Your scheme's
It's not "my" scheme. I got it from Martelli.
> determinism relies on the programmer accurately keeping
> track of reference counts in their head, which is precisely what
> automatic resource management is supposed to avoid.
This is a special case of the reference count being 1, then immediately
dropping to zero. It is simple and convenient. The approach is, as you
rightly point out, not extensible to more complicated situations in
Python, because the reference counting ceases to be trivial.
The point is that once you tie object lifetimes to scope, rather than
unpredictable garbage collection, you can predict with perfect ease and
comfort exactly where the objects are created and destroyed. If you can
then request that arbitrary actions be taken automatically when those
events happen, you can pair up resource acquisitions and releases very
easily. Each resource has an "owner" object whose constructor acquires,
and whose destructor releases. The resources are released in the
reverse order, which is almost always exactly what you want.
Suppose you are using objects that have to be closed when you have
finished with them. You would associate this concept with a type:
def __init__(self, closable):
self.closable = closable)
The C++-style paradigm would then let you do this:
def my_func(a, b, c):
a_closer = Closer(a)
b_closer = Closer(b)
c_closer = Closer(c)
# ... arbitrary code ...
If an exception gets thrown, the objects get closed. If you return
normally, the objects get closed. This is what "with" is supposed to
replace, except that it only seems to cover the trivial case of a
single, all-in-one cleanup func. That's only a direct replacement for a
single constructor/destructor pair, unless you're willing to have an
additional, nested with-statement for each resource.
Now suppose there is an object type whose instances need to be
"released" rather than "closed;" i.e., they have a release() method, but
no close() method. No problem: You have the Closer class get its
action indirectly from a mapping of types to close-actions. Whenever
you have a type whose instances require some kind of cleanup action, you
add an entry to the mapping.
actions = TypeActionMap()
The mapping is not quite as simple as a dict, because of inheritance.
This is what function overloads and C++ template specializations are
meant to achieve. Similar functionality could be implemented in Python
via pure Python mapping types, represented above by TypeActionMap.
> If you want
> reliable destruction it's better to set it up explicitly, using
That's true of the current language. I don't have enough experience
with "with" yet to know whether it's a realistic solution to the issue.
IMO, they are at least preferable to Java-style finally-clauses, but
probably not a replacement for C++-style RAII.
More information about the Python-list