[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] Enum, Flag, __contains__, and False vs TypeError

On 4/4/2018 11:32 AM, Ethan Furman wrote:
> API question.
> Background:
> ----------
> When doing checks such as
> --> 3 in [4, 5, 6]
> --> 'test' in {'test':True, 'live':False}
> the result is True or False.
> When doing checks such as
> --> 3 in 'hello world'
> --> [4, 5, 6] in {'test':True, 'live':False}
> the result is a TypeError.
> The general rule seems to be that if it is impossible for the 
> in-question object to be in the container object then a TypeError is 
> raised, otherwise True or False is returned.
> Question 1:
> ----------
> (A) A standard Enum class is a container of Enum members.? It cannot 
> hold anything else.? However, it has been returning False both in 
> cases where the in-question object was an Enum of a different class (a 
> Fruit in a Color, for example) and when the in-question object was not 
> even an Enum ('apple' in Fruit, for example).
> (B) The waters get even more muddied when Fruit has a str mixin, so 
> `Fruit.APPLE == 'apple' is True` -- in that case, should `'orange' in 
> Fruit` return True, False, or raise TypeError?
> Question 2:
> ----------
> (A) The new Flag type allows `in` tests on the members themselves; so, 
> for example:
> --> SomeFlag.ONE in SomeFlag.One|SomeFlag.TWO
> True
> The question, of course, is what to do when a non-Flag member is 
> tested for:
> --> 'apple' in SomeFlag.ONE
> # False or TypeError?
> --> 2 in SomeFlag.TWO
> # True or TypeError?
> (B) And, of course, the same muddier question arises with IntFlag, 
> where SomeFlag.TWO == 2 is True.
> My thoughts:
> -----------
> For question 1A (pure Enum):? I'm thinking a TypeError should be 
> raised when the in-question object is not an Enum member of any kind 
> -- it simply is not possible for that object to ever be in an Enum, 
> and is surely a bug.
> For question 1B (mixed Enum):? if 1A is TypeError, then 1B should also 
> be TypeError at least for non-mixin in-question types (so checking for 
> 1 in StrEnum would be a TypeError), but I'm torn between TypeError and 
> True/False for cases where the in-question type matches the mixin type 
> ('apple' in StrEnum)....? On the one hand, even though an Enum member 
> might be equal to some other type, that other type will not have the 
> Enum attributes, etc, and a True answer would lead one to believe you 
> could access `.name` and `.value`, etc., while a False answer would 
> lead one to believe there was no match even though equality tests 
> pass; on the other hand, how strong is the "container" aspect of a 
> mixed Enum?? How often is the test `'apple' in Fruit` meant to 
> discover if you have a Fruit member vs whether you have something that 
> could be a Fruit member?? Also, how important is it to be consistent 
> with IntFlag, which I definitely think should return True/False for 
> int checks?
> For question 2A (pure Flag):? I'm comfortable sticking with a 
> TypeError (assuming we switch to TypeError for 1A).
> For question 2B (int Flag):? I think a TypeError if the in-question 
> object is not an int is appropriate (assuming TypeError for 1A and 
> 2A), but if it is an int, True/False seems the better path.? My 
> reasoning being that Flag and IntFlag are more similar to sets than 
> lists, and IntFlag is specifically meant to work with ints, and a test 
> of `2 in some_int_flags` is more concerned with a flag being set than 
> with .name or .value attributes that may or may not exist on the 
> in-question object.
> Any and all thoughts appreciated.

I think the "in" test should raise TypeError if tested against 
_anything_ that is not an Enum member.

Why? I see a parallel between Enum and mappings.

x = {'test':True, 'live':False}

So it is True that? 'test' in x?? and? 'live' in x? and False that True 
in x and False in x.

It is False that? 'foo' in x? and? 3 in x

It is TypeError that [4,5,6] in x, or {'foo': 'bar'} in x.

Note that it is False that (4,5,6) in x? which is a little surprising 
given the above two, but not when you realize the differences between 
this and the above two.

So with mappings, you can have any hashable type as a key: with Enum, 
you can only have Enum members as keys.

So I have no idea why you would want to return False, rather than 
TypeError, other than (1) the distinction probably doesn't matter to 
most people (2) backward compatibility.

I would find?? 2 in some_int_flags? being True surprising.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20180404/a2fff4e8/attachment.html>