Unifying Attributes and Names (was: Re: Death to tuples!)

spam.csubich+block at block.subich.spam-dot-com.no-spam.invalid spam.csubich+block at block.subich.spam-dot-com.no-spam.invalid
Tue Nov 29 16:28:53 EST 2005


Bengt Richter wrote:

> If we had a way to effect an override of a specific instance's
attribute accesses
> to make certain attribute names act as if they were defined in
type(instance), and
> if we could do this with function instances, and if function local
accesses would
> check if names were one of the ones specified for the function
instance being called,
> then we could define locally named constants etc like properties.
> 
> The general mechanism would be that instance.__classvars__ if
present would make
> 
Nah... you're not nearly going far enough with this.  I'd suggest a
full 
unification of "names" and "attributes."  This would also enhance 
lexical scoping and allow an "outer" keyword to set values in an outer

namespace without doing royally weird stuff.

In general, all lexical blocks which currently have a local namespace 
(right now, modules and functions) would have a __namespace__
variable, 
containing the current namespace object.  Operations to get/set/delete

names would be exactly translated to getattr/setattr/delattrs. 
Getattrs 
on a namespace that does not contain the relevant name recurse up the 
chain of nested namespaces, to the global (module) namespace, which
will 
raise an AttributeError if not found.

This allows exact replication of current behaviour, with a couple 
interesting twists:
1) i = i+1 with "i" in only an outer scope acutally works now; it uses

the outer scope "i" and creates a local "i" binding.
2) global variables are easily defined by a descriptor:
    def global_var(name):
       return property(
        lambda self: getattr(self.global,name),
        lambda (self, v): setattr(self.global,name,v),
        lambda self: delattr(self.global,name),
        "Global variable %s" % name)
3) "outer variables" under write access (outer x, x = 1) are also 
well-defined by descriptor (exercise left for reader).  No more weird 
machinations involving a list in order to build an accumulator
function, 
for example.  Indeed, this is probably the primary benefit.
4) Generally, descriptor-based names become possible, allowing some 
rather interesting features[*]:
    i) "True" constants, which cannot be rebound (mutable objects
aside)
   ii) Aliases, such that 'a' and 'b' actually reference the same bit,

so a = 1 -> b == 1
  iii) "Deep references", such that 'a' could be a reference to
my_list[4].
   iv) Dynamic variables, such as a "now_time" that implicitly expands

to some function.
5) With redefinition of the __namespace__ object, interesting run-time

manipulations become possible, such as redefining a variable used by a

function to be local/global/outer.  Very dangerous, of course, but 
potentially very flexible.  One case that comes to mind is a
"profiling" 
namespace, which tracks how often variables are accessed -- 
over-frequented variables might lead to better-optimized code, and 
unaccessed variables might indicate dead code.

[*] -- I'm not saying that any of these examples are particularly good

ideas; indeed, abuse of them would be incredibly ugly.  It's just that

these are the first things that come to mind, because they're also so 
related to the obvious use-cases of properties.

The first reaction to this is going to be a definite "ewwwww," and I'd

agree; this would make Python names be non-absolute [then again, the 
__classvars__ suggestion goes nearly as far anyway].  But this 
unification does bring all the power of "instance.attribute" down to
the 
level of "local_name".

The single biggest practical benefit is an easy definiton of an
"outer" 
keyword: lexical closures in Python would then become truly on-par
with 
use of global variables.  The accumulator example would become:
def make_accum(init):
    i = init
    def adder(j):
       outer i #[1]
       i += j
       return i
    return adder

[1] -- note, this 'outer' check will have to require that 'i' be
defined 
in an outer namespace -at the time the definition is compiled-. 
Otherwise, the variable might have to be created at runtime (as can be

done now with 'global'), but there's no obvious choice on which 
namespace to create it in: global, or the immediately-outer one?  This

implies the following peculiar behaviour (but I think it's for the
best):

> # no i exists
> def f(): # will error on definition
> outer i
       print i
> def g(): # won't error
> print i
> i = 1
> f()
> g()
> 
Definitely a Py3K proposal, though.




More information about the Python-list mailing list