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