Names and bindings, round 42 (was Re: questions about scope/threading)

Bengt Richter bokr at oz.net
Sun Dec 8 21:47:57 EST 2002


On 8 Dec 2002 16:08:00 -0500, aahz at pythoncraft.com (Aahz) wrote:

>In article <asqb48$77b$1 at wheel2.two14.net>,  <maney at pobox.com> wrote:
>>Aahz <aahz at pythoncraft.com> wrote:
>>>
>>> Generally speaking, in Python a "binding" *is* a reference.  Of course
>>> "bound" gets confusing because it also refers to instance methods.  What
>>> subset of references were you thinking "bound" was?
>>
>>I guess I was thinking of binding to a name, as opposed to anonymous   
>>references.  And yeah, I have noticed the word gets used in a mess of  
>>different ways.  :-)
>
>Python code never uses reference/dereference semantics explicitly, so it
>only makes sense (at best) to refer to named bindings and anonymous
>bindings.  Even then, it's a tenuous distinction.  Consider:
>
>class C:
>    pass
>
>C.x = 'spam'
>print C.__dict__['x']
>
>Is "x" a name or an anonymous binding?  About the only time it makes
>sense to refer to anonymous bindings is when the binding truly is
>hidden:
>
>def f(x=None):
>    pass
>
>Where is the binding for the default value of "x" stored?  ;-)

Well, I would say the abstract binding is derivably there[1], but it has
a different representation than the usual name binding (e.g. globals, etc.).

By abstract binding, I think I mean an ordered pair in a particular set of
ordered pairs, where the first member of the pair is an abstract name, and
the second member is an abstract object. The particular set of ordered pairs
is the relevant namespace.

A name, in turn, is a composite asbstract entity, composed of an ordered sequence
of one or more name-character abstract entities selected from at least one and optionally
two sets: initial-name-character-entitities and following-name-character-entities.
A name then, is an abstract entity selected from the set of possible names generatable
by composition of aforementioned character entities. We are still talking abstractions
without specifying representation.

Similarly, we can talk about the other half of name-binding pairs in abstract terms, which
I won't belabor, but basically we have atomic objects and composite objects.

My sense of "anonymous binding" is a binding that doesn't associate a name with an object
(remember this is abstract, how and how represented is another matter). The bindings that
don't bind names are the bindings that bind a composite object's components into the composition.
In other words, they are object-object bindings, potentially one:many. <side note>ISTM Python doesn't
have a name object per se (unless you want to conceptualize one). A name can be represented
by a string object, but that is not a name object per se. So AFAIK there can't exist a name
that has a the status of unbound. Instead we can look for a name in a namespace and find that
the name is not bound therein. But that is different.</side note>

[1] Ok, back to the question. I'd say a binding exists (in the abstract) in *some* sense
because we can find representational state from which we can reliably deduce the abstract
name->object relationship. But we need a qualified name for this kind of binding.
I.e., we have

 >>> f.func_code.co_argcount
 1
 >>> f.func_code.co_varnames
 ('x',)
 >>> f.func_defaults
 (None,)
 >>> import inspect
 >>> inspect.getargspec(f)
 (['x'], None, None, (None,))
    ^-----------------^

(Of course the text above is a concrete textual representation,
of some information derived from another kind of representation.
Without looking at the code, it's not possible to tell what exactly
is being used).

The above is not a name binding in the usual sense. I'd call it a structurally
derivable binding. And this implied binding is given a more typical
representation if you call for it, so it is reconstructable even if
not represented in a particular way at a given time. I think there is
a useful distinction. You could call it an unevaluated binding. I.e.,
the result is predictable if the conditions don't change, but the result
does not yet explicitly exist. We can tell the system to evaluate the situation:

 >>> def f(x=None):
 ...     return vars()
 ...
 >>> f()
 {'x': None}

Also, the binding will exist when an execution frame is built. E.g., brute force
capturing the frame on entering f:

 >>> import sys
 >>> gtargs = [] # global trace args
 >>> def gt(*args): gtargs.append(args)
 ...
 >>> def f(x=None):
 ...     pass
 ...
 >>> sys.settrace(gt)
 >>> f()
 >>> sys.settrace(None)
 >>> gtargs
 [(<frame object at 0x007A3E80>, 'call', None), (<frame object at 0x007AB030>, 'call', None), (<f
 rame object at 0x007C0270>, 'call', None)]
 >>> [f[0].f_code.co_name for f in gtargs]
 ['?', 'f', '?']
 >>> ff = gtargs[1][0]
 >>> ff.f_locals
 {'x': None}

So, during execution, in some form or another, the binding exists in the
frame's f_locals namespace. Again, the text printout above is just a text
representation (where name-entities are represented as string literals, etc),
but here I think it reflects existence of explicit name binding as opposed to
unevaluated structurally derivable name binding.

I'm inventing terminology on the fly here to try to express my thoughts, hoping
to be clear. If you know of a good glossary, please post the URL ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list