On 5/13/2013 7:36 PM, Ethan Furman wrote:
On 05/10/2013 10:15 PM, Glenn Linderman wrote:

So it is quite possible to marry the two, as Ethan helped me figure out using an earlier NamedInt class:

class NIE( IntET, Enum ):
     x = ('NIE.x', 1)
     y = ('NIE.y', 2)
     z = ('NIE.z', 4)

and then expressions involving members of NIE (and even associated integers) will be tracked... see demo1.py.

But the last few lines of demo1 demonstrate that NIE doesn't like, somehow, remember that its values, deep down under the covers, are really int. And doesn't even like them when they are wrapped into IntET objects. This may or may not
be a bug in the current Enum implementation.

[demo1.py excerpt]
    print( repr( NIE1( 1 ) + NIE1(2)))
    print( repr( NIE1( IntET('NIE1.x', 1 )) + NIE1(2)))


So the questions are:
1) Is there a bug in ref435 Enum that makes demo1 report errors instead of those lines working?

Nope.

Well, if it isn't a bug, it will be interesting to read the documentation that explains the behavior, when the documentation is written:

The "obvious" documentation would be that Enum names values of any type, particularly the first type in the multiple-inheritance list. The values assigned to the enumeration members are used as parameters to the constructor of that first type, but the value of the enumeration member itself is an item of the type, created by the constructor.

The __call__ syntax [ EnumDerivation( value ) ] looks up enumeration members by value.

The obvious documentation would stop there. But if demo1 doesn't demonstrate a bug, it would have to continue, saying something like:

However, if you have a complex type, you can't look up by value, but rather have to resupply the constructor parameters used to create the item. This means that for simple types

EnumDerivation( EnumerationMember.value ) is EnumerationMember

but that doesn't hold for complex types.  I think it should.


2) Is something like demo2 interesting to anyone but me? Of course, I think it would be great for reporting flag values
using names rather than a number representing combined bit fields.

No idea.  ;)

3) I don't see a way to subclass the ref435 EnumMeta except by replacing the whole __new__ method... does this mechanism warrant a slight refactoring of EnumMeta to make this mechanism easier to subclass with less code redundancy?

I've broken it down to make subclassing easier.

Thanks... I'll take a look, eventually, but I'll be offline until next week.

4) Or is it simple enough and useful enough to somehow make it a feature of EnumMeta, enabled by a keyword parameter?

Probably not.

5) All this is based on "IntET"... which likely suffices for API flags parameters... but when I got to __truediv__ and __rtruediv__, which don't return int, then I started wondering how to write a vanilla ET class that inherits from "number" instead of "int" or "float"? One could, of course, make cooperating classes FloatET and DecimalET .... is this a language limitation, or is there more documentation I haven't read? :) (I did read footnote [1] of <http://docs.python.org/3/reference/datamodel.html#emulating-numeric-types>, and trembled.)

Sounds like a fun project (for some value of fun ;)

Not sure I'll get there, for a few years... such might be useful in certain debugging scenarios, but not sure it is useful enough to implement, given the footnote, except, perhaps, to truly become an expert in the Python object model.

Okay, sorry for the long delay.

What it comes down to is if you want to marry two complex types together, you may have to be the counselor as well. ;)

:) I assume by "counselor" you mean the code for __new__ and __init__ below, which, when I get a chance to understand them, will probably explain some of your earlier remarks about it maybe being easier to implement in such a manner. Of course, I don't particularly want to marry the types, just have XxxEnum work for IntET as well as it does for int... I was bumping into name conflicts between Nick's implementation and yours, that weren't immediately obvious to me, because I haven't done multiple inheritance much — Enum is dragging me into that and metaclasses, though, which is a good thing for me, likely.

The one piece of "marriage" that is interesting is to avoid specifying the name twice, and it seems your code

Here's your code, revamped. I did make a slight change in the meta -- I moved the name assignment above the __init__ call so it's available in __init__.

That's handy, thanks.


--8<--------------------------------------------------------
from ref435 import Enum
from flags import IntET

class NIE1( IntET, Enum ):
    x =  1
    y =  2
    z =  4
    def __new__(cls, value):
        member = IntET.__new__(cls, 'temp', value)
        member._value = value
        return member
    def __init__(self, value):
        self._etname = self._name

print( repr( NIE1.x.value ))
print( repr( NIE1.x + NIE1.y ))
print( repr( NIE1.x + ~ NIE1.y))
print( repr( NIE1.x + ~ 2 ))
print( repr( NIE1.z * 3 ))


print( repr( NIE1( 1 ) + NIE1(2)))
print( repr( NIE1( IntET('NIE1.x', 1 )) + NIE1(2)))
--8<--------------------------------------------------------

and my results:

1
IntET('(x + y)', 3)
IntET('(x + ~y)', -2)
IntET('(x + -3)', -2)
IntET('(z * 3)', 12)
IntET('(x + y)', 3)
IntET('(x + y)', 3)

I'd expect NIE1.x.value to be IntET('x', 1) but I'll have to look more carefully at what you've done, when I have some time next week. You may have made some "simplifying assumptions", and things _should_ be as simple as possible, but no simpler... especially not if it leads to unexpected results.

Oh, and if you really wanted the 'NEI' in the _etname, change the name assignment:

    self._etname = 'NIE.' + self._name

Sure. I did, because one problem that might arise is the combination of NIE-style enums from different enumerations... not prohibited for IntEnum or NIE, because it gets converted to the base type (int or IntET, respectively). But if someone accidentally combines an enumeration member from NIE1 and an enumeration member from NIE2, and it has the same member name, the expression could "look right" without the class name included. So you see, including the class name was not just a whim, but the result of analyzing potential error cases.

Forget to mention the good part -- in the custom __new__ you are able to set the value to whatever you want (not a big deal in this case, but if you had several parameters going in you could still make _value be a single, simple int).

This will take more thought than I have time for tonight, also. Right now, I think I want the value for NIE.x to be IntET('NIE.x', 1 ). And your code isn't achieving that at present, but maybe I just need to tweak __new__ and then can... and maybe it cures the discrepancy in expectations mentioned earlier too...

On the other hand, when I think about it more, maybe I'll see what you are suggesting as a better path, for some reason. But I think it is the case at present, that what you think I want, is different than what I think I want :)
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to