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

Glenn Linderman v+python at g.nevcal.com
Wed Apr 4 16:24:15 EDT 2018


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>


More information about the Python-Dev mailing list