| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
| true. | ||
|
|
||
| Two successive calls to :meth:`!__bool__` on the same object must | ||
| return same value. |
There was a problem hiding this comment.
Not if the object is mutated.
Sorry, something went wrong.
There was a problem hiding this comment.
But that means - some other method was called on the object in between. Thus, calls to __bool__ weren't actually successive.
Sorry, something went wrong.
There was a problem hiding this comment.
The object could have been mutated by another thread between the two calls...
Sorry, something went wrong.
There was a problem hiding this comment.
by another thread
... that calls some other object's method
between the two calls...
:)
Sorry, something went wrong.
There was a problem hiding this comment.
Hmm, how about this: "The __bool__ method can't mutate any objects."? (Ditto for __len__.)
This probably is a more strong requirement than actually need in current optimizations, but I doubt it blocks something useful.
Sorry, something went wrong.
|
As @AlexWaygood implied above, even builtin types can't provide the guarantee your current text for: if you call bool() or len() twice on a list, another thread might have run in between and mutated the list. I think instead, we should say that in cases where the interpreter implicitly calls bool(), it is unspecified how often bool() is called, and it is unspecified what happens if multiple calls happen and they return different results. Not sure where to put that, though. |
Sorry, something went wrong.
|
The language spec already says with regard to hashability:
So some interesting questions that come to mind for me here are:
If we think that well-behaved code should never do those things, and that doing those things could violate some assumptions made by Python in some places, then we could consider adding a similar note to the language spec for __bool__, i.e.
But I'm not sure if it's worth doing so for __len__ as well/I'm not sure what a good phrasing might be there. Additionally, this sort-of feels like an implicit requirement for an awful lot of dunders, really. I would find it pretty surprising if bytes(x) and bytes(y) produced different results for two objects x and y where x == y. Similarly for __int__, __index__, __neg__, etc... So that's an argument for not putting a special note in the entry for either __bool__ or __len__. |
Sorry, something went wrong.
Then this example will not satisfy to added remark: calls to __bool__ weren't actually successive.
Do you have examples? While your points look reasonable, I'm not sure that such assumptions are actually used somewhere.
Well, in current version I have to add symmetric note for __len__ just because that helper might be used instead of __bool__ in some implicit call to bool().
There is a difference. The __bool__ method in general can return different values for same object; but the reference implementation does assume it can't be in certain conditions. |
Sorry, something went wrong.
|
Maybe instead of saying something about __bool__(), we could instead say it about and/or. In a chain of logical ops, the interpreter is allowed to calculate the boolean value of any given term only once. Or more broadly, say in any given expression the boolean value of any given sub-expression may be reused if already calculated. |
Sorry, something went wrong.
|
I think the docs can just say that __bool__ and __len__ should be idempotent. |
Sorry, something went wrong.
That's a more short version of the current statement, isn't? But I worry it might require explanation of the term. E.g. we don't expect that reader knows about complex numbers. |
Sorry, something went wrong.
There was a problem hiding this comment.
Suggested wording added inline. Mutating objects shared between threads is another way of hitting this particular piece of implementation defined behaviour, so I think it's best just to state it that way.
I think "no mutation (of this object, or any other object) " is the right expected invariant to specify, since that's the assumption that gets violated in the multi-threading case.
Sorry, something went wrong.
| Two successive calls to :meth:`!__bool__` on the same object must | ||
| return same value. |
There was a problem hiding this comment.
| Two successive calls to :meth:`!__bool__` on the same object must | |
| return same value. | |
| Note: to help optimize logical expressions, implementations are permitted to assume | |
| that calls to :meth:`!__bool__` will not mutate that object, nor any other object. | |
| While this expected invariant is not explicitly enforced, failing to abide | |
| by it will result in implementation dependent runtime behaviour. This | |
| implementation dependent behaviour may also be encountered when mutable | |
| objects are shared across threads without appropriate synchronization. |
Sorry, something went wrong.
There was a problem hiding this comment.
I'm unsure if this needs to be explicitly documented as the behavior is dependent on the user's choice of implementation.
Sorry, something went wrong.
There was a problem hiding this comment.
is dependent on the user's choice of implementation.
Rather on bugs in the user code, if someone will implement __bool__(), doing crazy things (see issue). Added docs say that implementation may assume certain behaviour from the user code, just as for the __hash__() method (same hash value for equal objects - the invariant, which we also can't enforce, user code might break this).
PS: I think that part of discussion rather belongs to the issue thread, which has some other arguments on why we want document this. See e.g. this.
Sorry, something went wrong.
There was a problem hiding this comment.
Should the note warn against side effects in general (like I/O), not just mutation?
Sorry, something went wrong.
There was a problem hiding this comment.
Should the note warn against side effects in general (like I/O), not just mutation?
Side effects, including in fact object mutation - are fine, unless they break idempotence.
But I think we don't loose something practically relevant if just forbid mutation of any objects in __bool__() (a shortened version of @ncoghlan suggestion):
| Two successive calls to :meth:`!__bool__` on the same object must | |
| return same value. | |
| Calls to :meth:!__bool__` shouldn't mutate any objects. |
Sorry, something went wrong.
| Two successive calls to :meth:`!__len__` on the same object must | ||
| return same value. |
There was a problem hiding this comment.
| Two successive calls to :meth:`!__len__` on the same object must | |
| return same value. | |
| Note: to help optimize logical expressions, implementations are permitted to assume | |
| that calls to :meth:`!__len__` will not mutate that object, nor any other object. | |
| While this expected invariant is not explicitly enforced, failing to abide | |
| by it will result in implementation dependent runtime behaviour. This | |
| implementation dependent behaviour may also be encountered when mutable | |
| objects are shared across threads without appropriate synchronization. |
Sorry, something went wrong.
There was a problem hiding this comment.
Here is a typo (__len__ -> __bool__). But if we are going with this lengthly wording - I think it's better just point to the __bool__ docs.
Sorry, something went wrong.
|
I think that I can't make progress on this. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
📚 Documentation preview 📚: https://cpython-previews--124723.org.readthedocs.build/