4 ms·
> My personal preference is actually something like Python's "with" or C#'s "using", since they rely on "destructors" rather than explicit calls to close. But t
by shakna 4y ago
> My personal preference is actually something like Python's "with" or C#'s "using", since they rely on "destructors" rather than explicit calls to close. But they have the exact same issue as simple defer. If you ever need to store or return the thing, you need to handle it manually instead.
Python's "with" doesn't. That is to say, when the with-block closes, it doesn't call any kind of destructor against the variable. It calls the __exit__ method, but it still leaves the variable in-scope. And if the __exit__ method tries to call an actual destructor against the variable, you'll hit a double-free bug when the GC runs later.
You can safely return to the variable after storing it, and do anything it supports after-closing. Including returning the variable.
def retIOWrapper(x):
with open(x) as f:
pass
assert(f.closed == True)
return f
Python's with isn't introducing a scope lifetime.
- MereInterest 4y agoThis can sometimes be useful, though I haven't yet decided if I think it's a dirty hack or not. At one point, I made a timer whose __enter__ method started the timer, and whose __exit__ method stopped the timer. After the "with", the elapsed time could be queried.
- masklinn 4y agoThat is used by several context managers, mostly those related to intercepting exceptions and warnings.
- andreareina 4y agoI feel like that would be better as t = timer() with t: ... (I've done this exact thing for this exact use-case)
- sirwhinesalot 4y agoC#'s using is also not calling the "destructor", it's calling .Dispose() C# and Python have actual destructors "nobody" uses because they are GCed languages which make this discussion confusing (well, in Python they do sometimes, because it is reference counted and more predictable, making the discussion even more confusing). I'm treating __exit__() and .Dispose() as "destructors" here, in the sense that they are implicitly called methods that free the resource (like drop in Rust), vs defering a call to close, which is explicit and "configurable" (you have to call the right method). You get to keep the variable around, but the resource itself is gone after the with/using block, you have to reacquire it. With some changes to the way the APIs were written, it would make a bit more sense (IMO): files = [File("a.txt"), File("b.txt"), File("c.txt")] for f in files: with f.open('r'): print(f.read()) return files I personally extremely dislike the fact that "as f" is not scoped.