What's Going On?

M

MartinRinehart

(Accompanied by Marvin Gaye)
.... list[0]+=1
.... return list[0]
....
f() 1
f() 2
f() # 'list' is a name bound to a list (mutable) so this makes sense 3
f([5]) 6
f() # What's Going On?
4

Off topic: Motown chief Berry Gordy tells Gaye he won't release the
"uncommercial" song. Gaye tells Gordy
he'll never have another Gaye song if he doesn't release it. Gordy
backs down. 2.5 million singles plus title
track for blockbuster album.
 
D

Diez B. Roggisch

(Accompanied by Marvin Gaye)
... list[0]+=1
... return list[0]
...
f() 1
f() 2
f() # 'list' is a name bound to a list (mutable) so this makes sense 3
f([5]) 6
f() # What's Going On?
4

That the same default argument is mutated? What did you expect, that it got
replaced by you passing another list? That would kind of defy the meaning
of default-arguments, replacing them whenever you call a function with
actual parameters.


Diez
 
C

castironpi

(Accompanied by Marvin Gaye)
def f(list=[0]):
... list[0]+=1
... return list[0]
...
f() 1
2
f() # 'list' is a name bound to a list (mutable) so this makes sense 3
6
f() # What's Going On?
4

That the same default argument is mutated? What did you expect, that it got
replaced by you passing another list? That would kind of defy the meaning
of default-arguments, replacing them whenever you call a function with
actual parameters.

f( ite, itr= ite.__iter__ ).

Case 1: 'ite' is bound in outer scope.
Case 2: not.

function(code, globals[, name[, argdefs[, closure]]])
The optional argdefs tuple specifies the default argument values.

TypeError: __defaults__ must be set to a tuple object
.... print( 'k call' )
........ def __init__( self, arg ):
.... self._arg= arg
........ @functools.wraps( fun )
.... def post( *ar ):
.... lar= list( fun.__defaults__ )
.... for i, a in enumerate( lar ):
.... if isinstance( lar[ i ], GT ):
.... lar[ i ]= eval( a._arg)
.... return fun( *( list( ar )+ lar ) )
.... return post
........ def f( ite, itr= GT('k()') ):
.... return itr
....
Furthermore,

....
.... def f( ite, itr= GT('ar[0]+ 1') ):
.... return itr
....
Unfortunately,

....
.... def f( ite, itr= GT('ite+ 1') ):
.... return itr
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in post

The moral of the story is, wouldn't it be nice if wrappers could
access the post-invocation bindings of the wrappee's internal
variables? Certainly the 'TypeError: __defaults__ must be set to a
tuple object' is unnecessarily restrictive: let it be a mutable, or,
if it's not a tuple, call it. Just add a __call__ method to that
tuple that does nothing, if a type-test isn't faster-- perhaps even a
null __call__ which does -actually- nothing. (Why is __defaults__
None when empty, not an empty tuple?)

What are the use case proportions of function-static vs. call-time
evaluation? Function-static is nice in that it keeps objects floating
around with the function, both in information design, and in
encapsulation. They both have easy workarounds:
.... @functools.wraps( fun )
.... def post( *ar ):
.... return fun( post, *ar )
.... return post
........ def f( self ):
.... print( self.ite )
....
f.ite= []

f() []
f.ite.append( 2 )
f()
[2]

@auto adds the binding function to its called parameter list, which is
nice if the function will change names ever-- that is, become bound to
other variables-- because its statics still stay with it, and because
it's not a global either.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in post
File "<stdin>", line 3, in f
NameError: global name 'f' is not defined

But on the other hand, if the Python community on the whole wants to
keep the status quo,
.... if None is itr:
.... itr= ite+ 1
.... return itr
....3

isn't so bad. (If you think it is, lobby!)

@latebound is a good compromise. It keeps the information in the
right place, but takes a little redundancy if the evalled expression
refers to another variable, and but costs the additional GT class, and
adds an O( n ) argument-length boilerplate rearrangement. @auto frees
us to allow a different behavior to default parameters while keeping
both statics and call-times explicit, keeping the information in the
right place, but it's a change in the language. Lastly, we have:
.... def f( ite, itr= latearg, j= 0 ):
.... return itr, j
....(3, 0)

Proof of concept:

import functools

latearg= object()
def late( **kw ):
def pre( fun ):
_defs= fun.__defaults__
if None is _defs: _defs= ()
_names= fun.__code__.co_varnames
_deflen= len( _defs )
_namelen= fun.__code__.co_argcount
for k in kw:
if k not in _names:
raise TypeError( 'Non-parameter'
' keyword \'%s\' in \'late\''
' call.'% k )
print( _defs )
print( _names )
print( _deflen )
for a, b in zip( _names[ -_deflen: ], _defs ):
if b is latearg and a not in kw:
raise TypeError( 'Non-bound'
' latearg \'%s\' in \'late\''
' call.'% k )
@functools.wraps( fun )
def post( *ar ):
_arglen= len( ar )
_defleft= _namelen- _arglen
_defused= ()
if _defleft:
_defused= _defs[ -_defleft: ]
_lar= list( ar+ _defused )
_funargs= {}
for i, a in enumerate( ar ):
_funargs[ _names[ i ] ]= a
for k, v in kw.items():
if k not in _names:
raise TypeError( 'Not all latearg'
' arguments bound in call'
' of \'%s\''% fun.__name__ )
_place= _names.index( k )
if _place>= _arglen:
_lar[ _place ]= eval(
v, globals(), _funargs )
if latearg in _lar:
raise TypeError( 'Not all latearg'
' arguments bound in call'
' of \'%s\''% fun.__name__ )
return fun( *_lar )
return post
return pre

@late( itr= 'ite+ 1' )
def f( ite, itr= latearg, j= 0 ):
return itr, j

assert f( 2 )== ( 3, 0 )
assert f( 2, 0 )== ( 0, 0 )
assert f( 2, 0, 1 )== ( 0, 1 )
assert f( 2, 1 )== ( 1, 0 )

To complete 'post' (**kw not shown) is left as an exercise to the
reader.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,710
Latest member
bernietqt

Latest Threads

Top