On 12/18/2013 11:51 AM, eryksun wrote:
On Tue, Dec 17, 2013 at 10:52 AM, spir <denis.s...@gmail.com> wrote:
is it at all possible to set new vars (or any symbol) into an existing scope
(typically locals())?
scope[name] = value
raises by me an error like:
TypeError: 'mappingproxy' object does not support item assignment
I guess 'mappingproxy' is the implementation name of a scope (here, local),
and I thought scopes were just dicts; so what is the issue? Do you see an
alternative?
For a CPython function, built-in locals() creates/updates the frame's
f_locals mapping against the fast locals by calling the C function
PyFrame_FastToLocals. The reverse update can be done (but why?) with
the help of an extension module or ctypes to call
PyFrame_LocalsToFast. For example:
import sys
from ctypes import *
pythonapi.PyFrame_LocalsToFast.argtypes = [py_object, c_int]
def f(x, clear=0):
if not clear:
locals()['x'] += 1
else:
del locals()['x']
frame = sys._getframe()
pythonapi.PyFrame_LocalsToFast(frame, clear)
return x
>>> f(1)
2
If cleared this way (akin to `del x`), the fast local for x is set to
NULL (unbound), so trying to return it raises an UnboundLocalError:
>>> f(1, clear=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in f
UnboundLocalError: local variable 'x' referenced before assignment
There's no facilitated way to add new fast locals. The memory used for
the frame's stack, fast locals, and closure cells is allocated when
the frame is instantiated, based on attributes of the compiled code
object.
On the other hand, a class body is unoptimized (intentionally), so it
uses the frame's f_locals mapping (that's a dict, unless you're using
the PEP 3115 __prepare__ hook). The populated mapping gets passed to
the metaclass __new__ (e.g. type.__new__), which copies it to a new
dict for the class.
All right, I don't understand the details (not knowing anything about CPython's
implentation internals), but the general scheme is clear enough, thank you!
You can use locals() to your heart's content in a class body:
class Cls:
locals()['x'] = 'spam'
>>> Cls.x
'spam'
All right. So, when a user defines a grammar (parser) inside a class (as I like
to do myself) they could call my utility naming func directly from _inside_ the
class def.
class some_parser: # no capital, it's a parser, not an actual class
digit = Range("09")
# lots of other pattern defs
Pattern.name(locals())
I'll try it to see if setting inside a class's locals works fine... works! So,
apparently, I don't need to special-case classes anymore, do I?
I still ask for your advice because, since I don't get all details, despite my
quick trial I'm not 100% sure there aren't cases where it would not work. I just
need to register copies of patterns in the given scope/namespace in case they
already have a non-None .name attribute.
A class's __dict__ attribute is a descriptor defined by the metaclass.
In CPython, for example, this descriptor needs to know the offset into
the PyTypeObject where the dict is stored. The code it calls is simple
enough to include here:
static PyObject *
type_dict(PyTypeObject *type, void *context)
{
if (type->tp_dict == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyDictProxy_New(type->tp_dict);
}
If you aren't familiar with descriptors, read the following:
http://docs.python.org/3/howto/descriptor.html
And try the following:
class Cls(object, metaclass=type):
pass
type_dict_descr = vars(type)['__dict__']
type_dict_descr.__get__(Cls, type)
Calling the __get__ method eventually makes its way to the above C
function type_dict, which creates the mappingproxy by calling
PyDictProxy_New.
The proxy wraps the type's dict to keep you from hacking it directly.
The functions to do so aren't even defined (i.e. mp_ass_subscript and
sq_ass_item). So the abstract C API function PyObject_SetItem just
raises a TypeError. If you need to set attributes dynamically, use
built-in setattr().
I have been familiar with Python metaclasses some years ago (used them to
simulate toy languages in python itself as interpretor! ;-) including one
prototype-based à la Lua, Self, Io or JS) but this did not give me any precise
clue about the other side of the business, in particular descriptors. I remember
however having stepped on them once or twice, but at the time I knew too few
about language implementations in C to be able to get anything.
I'll have another look at them following your links, thank you very much,
"eryksun"!
denis
_______________________________________________
Tutor maillist - Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor