Paul Rubin wrote: > Jeff Schwab <[EMAIL PROTECTED]> 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() >> 'world\n' > > 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: some_class().method() 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 the statement. > 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: class Closer: def __init__(self, closable): self.closable = closable) def __del__(self): self.closable.close() 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. class Closer: actions = TypeActionMap() # ... def __del__(self): actions[type(self.closable)](self.closable) 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 > "with". 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. -- http://mail.python.org/mailman/listinfo/python-list