[Python-ideas] Allow Enum members to refer to each other during execution of body

Antony Lee anntzer.lee at gmail.com
Thu Jul 11 00:47:51 CEST 2013


Forward references are now implemented (https://github.com/anntzer/enum).  
They require an explicit declaration, à la

class C(Enum, declaration=...):
    B = ...
    A = {1: B}
    B = {1: A}

I had implemented a version where the initial declaration wasn't needed, 
but as mentioned in previous enum-related threads this can create many 
problems.  For example, consider

class C(Enum):
    A = {1: B}; B = {1: A}
    @property
    def also_value(self): return self.value

how is Python supposed to know that when it tries to resolve "B" in the 
class dict, it must create a new member, but when it tries to resolve 
"property" in the class dict and doesn't find it, it must look in the 
enclosing scope?  You can decide that a name lookup creates a new member if 
the name isn't defined in the enclosing scope either (I implemented this 
using sys._getframe in a previous commit of my fork) but this leads to 
other (somewhat contrieved) problems:

x = 1
class C(Enum):
    y = x # <- what is this supposed to mean?
    x = 2

Note that even AST macros don't (fully) this issue because you can't really 
even know the list of all names that are defined in the class body:

x = 1
def inject(**kwargs):
    for k, v in kwargs.items(): sys._getframe(1).f_locals[k] = v # 
interestingly using dict.update does not trigger the use-defined __setitem__
class C(Enum):
    y=x # <- ???
    inject_value(x=2)

Antony

On Monday, July 8, 2013 6:03:27 PM UTC-7, Haoyi Li wrote:
>
> > then the other methods can either dereference the name with an 
> __getitem__ look-up, or the class can be post-processed with a decorator to 
> change the strings back to actual members... hmmm, maybe a post_process 
> hook in the metaclass would make sense?
>
> Having real strings be part of the enums data members is a pretty common 
> thing, and working through and trying to identify the linkage-strings from 
> normal-strings seems very magical to me. Is there some metaclass-magic way 
> to intercept the usage of A, to instead put the enum instance there?
>
> Also, for this to be useful for your described use case, (state machines 
> yay!) you'd probably want to be able to define back/circular references, 
> which i think isn't currently possible. The obvious thing to do would be to 
> somehow make the RHS of the assignments lazy, which would allow 
> out-of-order and circular assignments with a very nice, unambigious:
>
> class StateMachine(Enum):
>     "Useless ping-pong state machine"
>     A = {1: B}
>     B = {1: A}
>
> But short of using macros to do an AST transform, I don't know if such a 
> thing is possible at all.
>
> -Haoyi
>
>
> On Tue, Jul 9, 2013 at 8:12 AM, Ethan Furman <et... at stoneleaf.us<javascript:>
> > wrote:
>
>> On 07/08/2013 02:27 PM, Antony Lee wrote:
>>
>>> Currently, during the execution of the body of the Enum declaration, 
>>> member names are bound to the values, not to the
>>> Enum members themselves.  For example
>>>
>>> class StateMachine(Enum):
>>>      A = {}
>>>      B = {1: A} # e.g. a transition table
>>>
>>> StateMachine.B[1] == {}, when one could have expected StateMachine.B[1] 
>>> == StateMachine.A
>>>
>>> It seems to me that a behavior where member names are bound to the 
>>> members instead of being bound to the values is more
>>> useful, as one can easily retrieve the values from the members but not 
>>> the other way round (at least during the
>>> execution of class body).
>>>
>>> Initially, I thought that this could be changed by modifying _EnumDict, 
>>> so that its __setitem__ method sets the member
>>> in the dict, instead of the value, but in fact this doesn't work because 
>>> while the values are being set in the _EnumDict
>>> the class itself doesn't exist yet (and for good reason: the __init__ 
>>> and __new__ methods may be defined later but there
>>> is no way to know that).  However, a possible solution could to 
>>> momentarily create Enum members as instances of some
>>> dummy class, and then later, after execution of class body has 
>>> completed, change the members' class to the actual Enum
>>> and initialize them as needed (if an __init__ or a __new__ are actually 
>>> defined).  Well, there are limitations with this
>>> approach (e.g. the members are not fully initialized before class body 
>>> finishes to execute) but this seems better than
>>> the current behavior(?)
>>>
>>
>> Part of the problem here would be maintaining the linkage when the temp 
>> enum object from _EnumDict was translated into an actual Enum member.
>>
>> One possible work around is to store the name of the member instead:
>>
>>   class StateMachine(Enum):
>>       A = {}
>>       B = {1:'A'}
>>
>> then the other methods can either dereference the name with an 
>> __getitem__ look-up, or the class can be post-processed with a decorator to 
>> change the strings back to actual members... hmmm, maybe a post_process 
>> hook in the metaclass would make sense?
>>
>> --
>> ~Ethan~
>> ______________________________**_________________
>> Python-ideas mailing list
>> Python... at python.org <javascript:>
>> http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20130710/59d8ed71/attachment-0001.html>


More information about the Python-ideas mailing list