Ben North <ben@redfrontdoor.org> wrote:
Hi,
I'd like to describe an addition I made to Python syntax which allows easier access to attributes where the attribute name is only known at run-time. For example:
setattr(self, method_name, getattr(self.metadata, method_name))
from Lib/distutils/dist.py could be rewritten
self.(method_name) = self.metadata.(method_name)
As noted in the PEP-style description below, I mostly did this for fun, but I thought it might be worth bringing to the attention of python-ideas. A quick search through prior postings and Google for this idea didn't come up with anything.
My only concern with your propsed change is your draft implementation...
Draft Implementation
A draft implementation adds a new alternative to the "trailer" clause in Grammar/Grammar; a new AST type, "DynamicAttribute" in Python.asdl, with accompanying changes to symtable.c, ast.c, and compile.c, and three new opcodes (load/store/del) with accompanying changes to opcode.h and ceval.c. The patch consists of c.180 additional lines in the core code, and c.100 additional lines of tests.
Specifically, your changes to ceval.c and the compiler may have been easier to implement, but it may negatively affect general Python performance. Have you run a recent pystone before and after the changes? Since the *value* being used for the dynamic set, get, and load should be available, it may be possible to replace the 3 new opcodes with 1 new opcode that shifts the the value on the top of stack into the the code object co_names, which can then be accessed directly by the standard [LOAD|STORE|DEL]_ATTR opcodes. The major concern with such a change is that the co_name field would no longer be read-only, so wouldn't be sharable between threads (and would need to grow by at least 1 when such dynamic accesses were allowed, though growing by 2 could help in augmented assignment cases). We could probably get away with a single new attribute on the stack frame, adding an alternate GETITEM implementation... #ifndef Py_DEBUG #define GETITEM2(v, i, s) \ ((i) != -1) ? PyTuple_GET_ITEM((PyTupleObject *)(v), (i)) : (s)) #else #define GETITEM2(v, i, s) \ ((i) != -1) ? PyTuple_GetItem((v), (i)) : (s)) #endif With that change, dynamic attribute access would result in a single opcode DYNAMIC_ACCESS, which would copy/move the value on the top of the stack into some stack frame attribute, which is then automatically accessed in the [LOAD|STORE|DEL]_ATTR opcodes (the GETITEM2 macro not being used in any other opcodes). The problem with the alternate approach above is that the overhead of general [LOAD|STORE|DEL]_ATTR opcodes may become measurably larger (depending on the branch prediction efficiency of a processor). Something to think about. - Josiah