RFC: Proposal: Deterministic Object Destruction

ooomzay at gmail.com ooomzay at gmail.com
Sat Mar 3 19:49:42 EST 2018


On Friday, March 2, 2018 at 3:37:25 PM UTC, Paul  Moore wrote:
> [...]
> RAII works in C++ (where it was initially invented) because it's used
> with stack-allocated variables that have clearly-defined and limited
> scope. 

RAII also works with smart pointers, which are a closer analogue 
to python references.

> In my experience writing C++, nobody uses RAII with
> heap-allocated variables - those require explicit allocation and
> deallocation and so are equivalent to having an explicit "close()"
> method in Python (or using __del__ in CPython as it currently exists).

In my experience no-one has been been explicitly deallocating
heap-variables for many years: they have been using smart pointers 
and RAII.

> Python doesn't have stack allocation, nor does it have a deterministic
> order of deletion of objects when their last reference goes out of
> scope (which can happen simultaneously for many objects):
>
> class Tracker:
>     def __init__(self, n):
>         self.n = n
>     def __del__(self):
>         print("Deleting instance", self.n)
> 
> def f():
>     a = Tracker(1)
>     b = Tracker(2)
> 
> f()
> 
> The language doesn't guarantee that a is removed before b. Are you
> proposing to make that change to the language as well?

It would improve things more, but I have not included such a proposal
in the current PEP as this is not essential for the main benefit. 
 
> Also Python only has function scope, so variables local to a
> smaller-than-the-function block of code aren't possible. That's
> something that is used in C++ a lot to limit the lifetime of resources
> under RAII. How do you propose to address that (without needing
> explicit del statements)? 

Create a sub function. 

> That's why the with statement exists, to
> clearly define lifetimes smaller than "the enclosing function". 

It does that in a half-hearted and smelly way compared to a function: 
The object references are left dangling in a broken state (not invariant). 
In fact you would do well to wrap your "with"s in a function to contain 
the smell.

> Your proposal doesn't offer any equivalent (other than an extra function).

An equivalent is not needed. Using a function is the ideomatic and does not
leak - unlike the "with" hack. (IMO block-scoping would enhance the langauge
but is not essential so not in this PEP).

> Consider C++:
> 
>     void fn() {
>         for (i = 0; i < 10000; ++i) {
>             char name[100];
>             sprintf(name, "file%d.txt, i);
>             File f(name); // I don't think std::ofstream doesn't support RAII
>             f << "Some text";
>         }
>     }
> 
> Or (real Python):
> 
>     def fn():
>         for i in range(10000):
>             with open(f"file{i}.txt", "w") as f:
>                 f.write("Some text")
> 
> How would you write this in your RAII style - without leaving 10,000
> file descriptors open until the end of the function?

     def write_some_text_to_file(fname):
          f = RAIIFileAccess(fname, 'w')
          f.write("Some text")

     def fn():
         for i in range(10000):
             write_some_text_to_file(f"file{i}.txt")

> That's both less efficient (function calls have a cost) 

Oh come now. No one is choosing to use python for its efficiency and 
functions that deal with real resources, such as files, are likely 
dominated by the os fopen/close etc.

> and less maintainable than the with-statement version.

I disagree. Even for such a trivial example, as soon as you 
start to change the detail of what you do then the usual maintainability
benefits of small well factored functions comes into play. That's why we
have functions in the language isn't it? Or do you just write one 
long script?



More information about the Python-list mailing list