"no variable or argument declarations are necessary."

Ron Adam rrr at ronadam.com
Thu Oct 6 15:15:48 EDT 2005


Bengt Richter wrote:
> On Wed, 05 Oct 2005 11:10:58 GMT, Ron Adam <rrr at ronadam.com> wrote:

>>Looking at it from a different direction, how about adding a keyword to 
>>say,  "from this point on, in this local name space, disallow new 
>>names".  Then you can do...
>>
>>def few(x,y):
>>   a = 'a'
>>   b = 'b'
>>   i = j = k = l = None
>>   no_new_names
>>   # raise an error after here if a new name is used.
>>   ...
>>   for I in range(10):   <--  error
>>      ...
>>
>>This is more suitable to Pythons style than declaring types or variables 
>>I think.  Add to this explicit name-object locking to implement 
>>constants and I think you would have most of the features you want.
>>
> 
> You can do that now with a decorator, if you are willing to assign something
> to no_new_names (so it won't give you a name error if it doesn't exist). E.g.,

Works for me.

    __lock_names__ = True

It's not too different than __name__ == '__main__'...


>  >>> def nnn(f):
>  ...     names = f.func_code.co_names
>  ...     assert 'no_new_names' not in names or names[-1]=='no_new_names', 'Bad name:%r'%names[-1]
>  ...     return f
>  ...
>  >>> @nnn
>  ... def few(x,y):
>  ...     a = 'a'
>  ...     b = 'b'
>  ...     i = j = k = l = None
>  ...     no_new_names=None
>  ...     for i in range(10): print i,
>  ...
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in ?
>    File "<stdin>", line 3, in nnn
>  AssertionError: Bad name:'range'

Hmm... To make it work this way, the globals and arguments need to have 
local references.

@nnn
def few(x,y):
     global range
     range = range
     x,y = x,y
     a = 'a'
     b = 'b'
     i = j = k = l = None
     L = 1
     __no_new_names__ = True
     L += 1
     for i in range(x,y):
         print I

>  >>> @nnn
>  ... def few(x,y):
>  ...     a = 'a'
>  ...     b = 'b'
>  ...     i = j = k = l = None
>  ...     no_new_names=None
>  ...     return a,b,i,j,k,l
>  ...
>  >>> few(123,456)
>  ('a', 'b', None, None, None, None)
> 
> No guarantees, since this depends on the unguaranteed order of f.func_code.co_names ;-)

I had the thought that collecting the names from the 'STORE FAST' lines 
of dis.dis(f) would work nicely, but...  dis.dis() doesn't return a 
string like I expected, but prints the output as it goes.  This seems 
like it would be easy to fix and it would make the dis module more 
useful.  I'd like to be able to do...

    D = dis.dis(f)

An alternate option to output the disassembly to a list of of tuples. 
That would make analyzing the output really easy.  ;-)

Something like...

    good_names = []
    nnnames = False
    for line in dis.dislist(f):
        if line[2] = 'SAVE_FAST':
            if not nnnames:
                if line[-1] = '(__no_new_names__)':
                    nnnames=True
                    continue
                good_names.append(line[-1])
            else:
                 assert line[-1]in good_names, 'Bad name:%r'% line[-1] 



So, I wonder what kind of errors can be found by analyzing the disassembly?


>>so...
>>
>>    no_new_names     # limit any new names
>>    lock_name name   # lock a name to it's current object
> 
> That last one you could probably do with a decorator that imports dis and
> checks the disassembly (or does the equivalent check of the byte code) of f
> for STORE_FASTs directed to particular names after the lock_name name declaration,
> which you would have to spell as a legal dummy statement like
>     lock_name = 'name'
> 
> or perhaps better, indicating a locked assignment e.g. to x by
> 
>     x = lock_name = expr  # lock_name is dummy target to notice in disassembly, to lock x from there on

Using dis.dis it becomes two sequential 'STORE_FAST' operations.  So add 
(x) to the don't change list, and catch it on the next 'STORE_FAST' for 
(x).  ;-)

  28          12 LOAD_GLOBAL              2 (True)
              15 DUP_TOP
              16 STORE_FAST               0 (x)
              19 STORE_FAST               8 (__lock_name__)


>>Since names are stored in dictionaries,  a dictionary attribute to 
>>disallow/allow new keys, and a way to set individual elements in a 
>>dictionary to read only would be needed.  Once you can do that and it 
>>proves useful, then maybe you can propose it as a language feature.
> 
> I would want to explore how to compose functionality with existing elements
> before introducing either new elements or new syntax. E.g., the dictionaries
> used for instance attribute names and values already exist, and you can already
> build all kinds of restrictions on the use of attribute names via properties
> and descriptors of other kinds and via __getattribute__ etc.

That was more or less what I had in mind, but I think keeping things as 
passive as possible is what is needed.  One thought is to use this type 
of thing along with __debug__.

    if __debug__: __nnn__ = True


Wouldn't a debug block or suite be better than an if __debug__:?  Just a 
thought.  Even if the -0 option is given the if __debug__: check is 
still there.  Which means you still need to comment it out if it's in an 
inner loop.

debug: __nnn__ = True    # Is not included if __debug__ is false.

or...

    MAX = 256
    MIN = 0
    debug:
        __lock__ = MIN, MAX      # helps checker app
        __no_new_names__ = True  # find bugs.

    for MAX in range(1000):    # If __debug__, checker app catches
this.
        if m<MIN or m>MAX:
            print m


>>These might also be checked for in the compile stage and would probably 
>>be better as it wouldn't cause any slow down in the code or need a new 
>>dictionary type.
> 
> Although note that the nnn decorator above does its checking at run time,
> when the decorator is executed just after the _def_ is anonymously _executed_
> to create the function nnn gets handed to check or modify before what it
> returns is bound to the def function name. ;-)

Yes. ;-)

Is there a way to conditionally decorate?  For example if __debug__ is 
True, but not if it's False?  I think I've asked this question before. (?)

Cheers,
Ron



More information about the Python-list mailing list