On Tue, 9 Nov 2021 at 06:31, Ethan Furman <et...@stoneleaf.us> wrote:

(IMO, Steven D'Aprano's comment above, which I don't think you have
responded to yet, is the clearest summary of the options here. I agree
with what he says, but just wanted to clarify one point below).

> I've had a couple people tell me that they think of flags as sets, and use 
> set theory / set behavior to understand how
> flags and groups of flags should interact.  My main concern is whether that 
> is the only correct way to look at flags,
> and whether the behavior that seems logical to me with regards to, for 
> example, RegexFlag(0), is wrong, unusual, or
> perfectly natural.

Looking at flags as sets, you need to make a very clear distinction¹
between a *group* of flag values (a set) and a *single* flag value (an
element). The operation "in" on sets tests if an *element* is a member
of a *set*. In the examples you are giving, you seem to be confusing
sets with elements - if you have flags 1 (0b001), 2 (0b010) and 4
(0b100), then asking if 3 is "in" 1|2|4 makes no sense, because 3 is a
*set* of flags, and sets don't go on the LHS of the "in" operator.
Asking if 1 is "in" 3 makes some level of sense (element in set) and
is true, but it looks much less convincing when expressed as numbers
rather than named values (where the names can be chosen to make 1
"look like" a value and 3 "look like" a set).

¹ Pedantry digression - in pure set theory, there are *only* sets, and
sets are members of other sets. But this is largely a technical detail
(and even then, some set theories have the idea of "levels" of sets,
which formalises the whole set vs element idea). Real-world intuitions
about sets tend to have sets and elements as different concepts.

> The way I see it, the following should hold
>
>      empty_flag = RegexFlag(0)
>      any_case = RegexFlag.IGNORECASE
>      any_case_on_any_line = RegexFlag.IGNORECASE | RegexFlag.MULTILINE
>
>      any_case in empty_flag is False
>      any_case_on_any_line in empty_flag is False
>
>      empty_flag in any_case is False
>      empty_flag in any_case_on_any_line is False

You seem to be confusing "in" with "is a subset of" here. Strong -1 on
using "in" to mean "is a subset", as that breaks the pattern used for
every other type in Python. None of the examples you give here make
any sense to me, either in terms of a "natural reading" nor in terms
of "flags are like sets and their elements". You also seem to be
confusing a flag value with the 1-element *set* of values containing
just that value².

The use of the name empty_flag for the value RegexFlag(0) makes things
even more confusing for me, as it's emphatically *not* a single flag,
but a collection (specifically the empty collection) of flags.

If I had to think of the flag type strictly as a set type, I'd think
of all the values as sets of flags (even RegexFlag.IGNORECASE would be
"a set of one flag") and the operation you're talking about here would
then be "subset" - which I'd either spell as "<=" or I wouldn't use an
operator for *at all*. I could just barely deal with thinking of
individual bits as having a dual "set or element" nature, which would
make "individual_flag in flag_set" make sense to me, but it *only*
makes sense if the LHS has exactly one bit set. And I think this is
where the confusion comes in - we typically don't think of operators
as only being meaningful for certain values of one operand (well,
there's 1/0, so I guess we could have "Flag(0) in Flag(1)|Flag(2)"
raise ValueError, by analogy with ZeroDivisionError, but that doesn't
seem helpful in any practical situation).

Overall, my view is that we shouldn't support "in" on enum.Flag types
at all, as the potential for confusion is too high. I'm -0 on having
an explicit "is subset" operator, but if we do, it should be named
"<=", not "in". I fear that any attempt to document an "is subset"
operator would get bogged down in confusion over whether a flag value
is a value or a set of values (or would completely confuse people with
a background in set theory, by making it look like "element is subset
of set" was somehow a thing).

² Pedantry digression 2 - set theories that allow values that are
equal to the set containing just that value are possible (such values
are called "Quine atoms" - see
https://math.stackexchange.com/questions/2389726/can-one-element-set-be-considered-equal-to-its-element)
but such theories are not mainstream, and are not as intuitive as
you'd hope - for example, "RegexFlag.IGNORECASE in
RegexFlag.IGNORECASE" just seems weird to me...

Paul
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/EHPB5775QZOWUIS3LJPISQ6FN4HBRJUG/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to