This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: tkinter.Variable equality inconsistency
Type: behavior Stage: resolved
Components: Tkinter Versions: Python 3.10
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: RhinosF1, epaine, serhiy.storchaka, shippo_
Priority: normal Keywords: patch

Created on 2020-12-26 15:06 by shippo_, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
equality.diff shippo_, 2020-12-26 15:06 patch
Messages (10)
msg383807 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-26 15:06
Greetings!

I just noticed:

>>> import tkinter as tk
>>> root = tk.Tk()

>>> str_0 = tk.StringVar()
>>> str_0.set("same value")

>>> str_1 = tk.StringVar()
>>> str_1.set("same value")

So:

>>> str_0.get() == str_1.get()
True

But:

>>> str_0 == str_1
False

So, maybe a Variable should be compared by value, and not by identity (._name) as currently? (please view attached) Does it make sense?
msg383810 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-12-26 16:55
It will break existing code which depends on the current behavior.

I don't see a problem with comparing variables by name.
msg383815 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-26 18:01
If it's gonna break existing code -- fine. I just wanted to point out that if two variables are of the same type and refer to the same value, they should be considered equal, even if they are not the same variable.

In the current implementation, two StrVars can hold the same strings, but are compared unequal, which doesn't seem right.

Considering that we are going through the Tcl interpreter, equality should compare by value, not by identity (regardless of the variable names).

Look at this, please.

>>> int_0 = tk.IntVar()
>>> int_0.set(51)

>>> int_1 = tk.IntVar()
>>> int_1.set(51)


How can:

>>> int_0.get() == int_1.get()
True

and

>>> type(int_0) == type(int_1)
True

..but:

>>> int_0 == int_1
False

This means that the equality operator is only showing the difference in their names, and the statement above loses meaning, as it can never return True.

I think "int_0 is int_1" should be False as it is now. "is" should take into account their names as defined in the Tcl interpreter.

But "int_0 == int_1" should be True. Same as a couple of identical Python lists with different names.

Thank you!
msg383818 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-12-26 18:31
a = tk.IntVar(name='a')
b = tk.IntVar(name='a')
assert a == b  # they refers to the same variable
assert a is not b  # but they are different objects
a.set(42); assert b.get() == a.get() == 42  # yes, it is the same variable
c = tk.IntVar(name='c', value=42)
assert c != a  # they are different variables
assert c.get() == a.get() == 42  # although having equal values
a.set(43); assert c.get() != a.get() == 43  # and setting one does not affect other

I do not see good reasons for making Tk variables comparable by value instead of name.
msg383823 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-26 19:29
There are 2 good reasons for making Tk variables comparable by value:

   1. We honor the equality operator! The equality operator (==) has to compare two objects by value. The current implementation does not fulfil this. Yes, if two Tk variables have the same names in the Tcl interpreter, they are guaranteed to have the same value.

>>> a = tk.IntVar(name='a')
>>> b = tk.IntVar(name='a')
>>> assert a == b  # this is not enough; equality means "by value"
                   # what about when they don't have the same name?

>>> assert a is not b  # this again does not make sense, since
                       # both Python "a" and "b" identifiers lead to
                       # the same "self._tk.globalgetvar(self._name)"
                       # Tcl registered variable: name="a"

>>> a.set(42); assert b.get() == a.get() == 42  # yes!
    # equality in names guarantees equality in value

Yes ... BUT, the negation is not true: if two Tk variables have different names, that does NOT mean that they have different values. This is where we fail the equality operator:

>>> c = tk.IntVar(name='c', value=42)
>>> assert a._name != c._name and a.get() != c.get(), \
...     "Difference in names does not guarantee difference in value"

   2. When we're interested in Tk variable comparison: .get() becomes redundant. Every time two Tk variables are of the same type AND they refer to the same value, we can safely compare them as equal, regardless of how they are named in the Tcl interpreter.
msg383824 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-26 20:16
2. (continued) .. This is including the case when both variables have the same internal name (the current implementation).
msg383849 - (view) Author: E. Paine (epaine) * Date: 2020-12-27 12:35
See also issue41851. I personally think that being able to compare whether two tkinter variables point to the same Tk variable is very useful so needs to stay in some form. However, I don't see any situation where comparing to see if two tkinter variables are the same Python object would be helpful.

Comparing to a list (for example), `assert a is b` would mean altering one would affect the other (while this is true of tkinter variables, to cover all cases you would instead check whether they are 'equal'). Therefore, while writing `a.get() == b.get()` isn't too bad, the point of this change would be consistency more than anything else (as-per the title).
msg383853 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-27 16:21
"I personally think that being able to compare whether two tkinter variables point to the same Tk variable is very useful so needs to stay in some form." -- I concur.

"However, I don't see any situation where comparing to see if two tkinter variables are the same Python object would be helpful." -- agreed.

"Therefore, while writing 'a.get() == b.get()' isn't too bad, .." -- it isn't. But "a == b" -> False IS, provided that "a.get() == b.get()" is True.

Serhiy, how about:

> class Variable:
>     ...
>     def is_(self, other):  # or maybe "same_as"
>         return self.__class__.__name__ == other.__class__.__name__ \
>             and self._name == other._name
>
>     def __eq__(self, other):
>         return self.__class__.__name__ == other.__class__.__name__ \
>             and self.get() == other.get()

Regards
msg383864 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-12-27 20:48
You confuse variable and its value. tkinter.Variable is not a data container like list. It is a reference to external resource, Tcl variable, which contains value. If different Variable instances refer to different Tcl variables, they are different, even if values of these variables at some moment were equal. If they refer to the same Tcl variable, they can be considered equal. Details are different for different Tkinter classes: Font instances with the same name are equal, Variable instances with the same name are equal only if they have the same class (so that StringVar and IntVar are always different), Image and Widget instances with the same name are different. But in any case references to different resources are different.

Use analogy of Path instead of list. The Path object refers to external resource (file). Path objects with the same path are equal. Path objects with different paths are different, even if files have the same content.

I didn't close the шіігу right away because I was trying to explain his mistake to Ivo. There is no inconsistency, there is just a misunderstanding. Anyway, I'm closing it now.
msg383890 - (view) Author: Ivo Shipkaliev (shippo_) * Date: 2020-12-28 11:50
Yes, I get it now. I've missed the idea. You can do:

> age = tk.IntVar(value=38, name="mine")
> age_str = tk.StringVar(name="mine")
> is_alive = tk.BooleanVar(name="mine")
> is_alive.get()
True

Maybe not the best example, but still, it was a misunderstanding.

Thank you very much, Serhiy, once again!
History
Date User Action Args
2022-04-11 14:59:39adminsetgithub: 86916
2020-12-28 11:50:41shippo_setmessages: + msg383890
2020-12-27 20:48:47serhiy.storchakasetstatus: open -> closed
resolution: not a bug
messages: + msg383864

stage: resolved
2020-12-27 16:21:35shippo_setmessages: + msg383853
2020-12-27 12:35:52epainesetmessages: + msg383849
2020-12-27 10:19:26shippo_settitle: tkinter.Variable equality consistency -> tkinter.Variable equality inconsistency
2020-12-26 20:16:56shippo_setmessages: + msg383824
2020-12-26 20:02:49RhinosF1setnosy: + RhinosF1
2020-12-26 19:29:16shippo_setmessages: + msg383823
2020-12-26 18:31:56serhiy.storchakasetmessages: + msg383818
2020-12-26 18:01:56shippo_setmessages: + msg383815
2020-12-26 16:55:13serhiy.storchakasetmessages: + msg383810
2020-12-26 15:06:16shippo_create