On 9/12/19 3:03 PM, Neil Schemenauer wrote:
On 2019-09-10, Stefan Behnel wrote:
I find it a bit sad that state lookups in slot methods are disadvantaged compared to normal methods, given that their entire point is to provide a performance _benefit_. However, for the time being, we do not know how relevant and visible this will become in practice, so it is good that the PEP does not propose a new/real/complex solution to it for now.
While PEP 573 accomplishes the goal of providing access to the module state, I don't find it too elegant. I would like a design that reduces the differences between native Python code functions compared to ones implemented in C code.
For native Python code (i.e. implemented by bytecode), functions keep a reference to their module namespace. I.e. the __globals__ attribute of a PyFunctionObject. I think we should have a PyFunctionObject analog for functions implemented by C code. They would bind a reference to their namespace (i.e. the module/module state) when they are defined.
That would remove the need for PyType_DefiningTypeFromSlotFunc(). Once you have the function object, you have the reference to the correct module. No need to crawl around in the MRO to find it.
With PEP 573, you don't need PyType_DefiningTypeFromSlotFunc()
for
normal methods. It's there for slots. For example, addition (nb_add)
calls the C function:
typedef PyObject * (*binaryfunc)(PyObject *left, PyObject *right);
There's no space to pass a PyFunction object, or any other object, to this directly. PEP 573 *gives up* on solving the problem, but documents the least bad solution that's available now, and gives you the PyType_DefiningTypeFromSlotFunc helper since it's not trivial to write.
For normal methods, your idea is almost what the PEP proposes -- with one extra indirection through the defining class (which you can't reliably get from the module state).
The context that a method code could want is:
- (sub)Interpreter state
- Module (module object or its state)
- Defining class
- The (unbound) method object
These form a tree (an interpreter has many modules; a module has many types), and it's easy to get an item's parent (there just needs to be a pointer available) but it's not easy to get a child (Python code uses name-based lookups to get stuff from __globals__, but that's unsafe for C code, since anyone replace a __globals__ entry with an unexpected object). So, with PEP 590 (vectorcall) we pass the leaf of that hierarchy to the implementation code, and PEP 573 proposes adding the missing "parent" pointers.