[Tutor] set locals
spir
denis.spir at gmail.com
Wed Dec 18 12:40:30 CET 2013
On 12/18/2013 11:51 AM, eryksun wrote:
> On Tue, Dec 17, 2013 at 10:52 AM, spir <denis.spir at 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
More information about the Tutor
mailing list