Default parameters

Bengt Richter bokr at oz.net
Thu Dec 18 03:39:31 EST 2003


On Wed, 17 Dec 2003 06:20:54 +0000 (UTC), stain at stud.ntnu.no (Stian =?iso-8859-1?Q?S=F8iland?=) wrote:

>* J.R. spake thusly:
>> >    def f(d=[]):
>> >       d.append(0)
>> >       print d
>> >    f()
>> >    f()
>> > Explain results.  When is d bound?
>
>When is this issue going to be resolved? Enough newbie-pythoners have
>made this mistake now.
It works as designed. The default parameter value bindings are made a def time.
If you want to do them at call time, the idiom is 

        def f(d=None):
           if d is None: d = []
           d.append(0)
           print d

>
>Why not evaluate the parameter lists at calltime instead of definition
>time? This should work the same way as lambdas.
Lambdas do work the same as defs, except for the automatic name binding
and the limitation to an expression as the body.
>
>>>> f = lambda: []
>>>> a=f()
>>>> b=f()
>>>> a.append(1)
>>>> print a, b
>[1] []
The comparable def code to your lambda would be
     def f(): return []
The above is misguiding attention away from your point, IMO.

>Maybe this could be defined in a similar way to remind of the
>"lazy-evaluation":
>
>def getvalue(cls):
>    return "OK"
>
>class SomeClass:
>    def blapp(something: getvalue(), other: []):
>        print something, other
>
This might be an interesting concise spelling to accomplish what the following does:
(BTW, you need a self for normal methods)

 >>> class Defer(object):
 ...     def __init__(self, fun): self.fun = fun
 ...
 >>> class SomeClass(object):
 ...     def blapp(self, something=Defer(lambda: getvalue()), other=Defer(lambda:[])):
 ...         if isinstance(something, Defer): something = something.fun()
 ...         if isinstance(other, Defer): other = other.fun()
 ...         print something, other
 ...
 >>> def getvalue(): return 'gotten_value'
 ...
 >>> sc = SomeClass()
 >>> sc.blapp()
 gotten_value []
 >>> sc.blapp(1)
 1 []
 >>> sc.blapp('one', 'two')
 one two

>This way, the lambda forms defined after 'something' and 'other' are
>evaluated each time the function is called without supplying those
>parameters.
>
>The lambda forms could be evaluated as if within the class-block, and
>therefore they might actually use other values defined within that
>namespace. However, this might again confuse users, as subclassed
>attributes and instance attributes would not be resolved that way.
Yes, messy. ISTM better to let them be defined in the same scope
as the def, as in my example above.

>
>(Note that default-parameters-lambdas by today does NOT resolve this way:
>
>>>> class Blapp:
>...   ting = 15
>...   def fix(self, per=lambda: ting):
>...     print per()
>...
>>>> a = Blapp()
>>>> a.fix()
>Traceback (most recent call last):
>  File "<stdin>", line 1, in ?
>  File "<stdin>", line 4, in fix
>  File "<stdin>", line 3, in <lambda>
>  NameError: global name 'ting' is not defined
Right, though you could get the functionality if you wanted to.
>
>
>
>Finally, things could also be done like this, to avoid confusion to all
>those new folks (our main goal):
If they're confused because their preconceptions are filtering out
anything discordant, pandering to them would not serve the language.

>
>def blapp(something=getvalue(), other=[]):
>
>getvalue() should be called if something-paramer is not specified (ie.
>expression something=getvalue() is evaluated), and likewise for other.
No. I like your version with the lambda-colons much better.
>    
>    
>Although this would break existing code and need to be delayed to at
>least 3.0 and implemented in the __future__. 
>
>I must say I can't see the reason to not delay evalution now that we
Are you sure you counted your negatives there? ;-)

>have nested scopes. This way, even this would work:
>
You're assuming default_height will refer to A.default_height
>
>class A:
>    default_height=100
>    default_width=200
>    def make_picture(self, height=default_height, 
>                           width=default_width):
>        self.gui.do_blabla(height, width)                       
>    set_default_width = classmethod(set_default_width)     
>
IMO better:

 class A:
     default_height=100
     default_width=200
     def make_picture(self, height: A.default_height, 
                            width: A.default_width):
         self.gui.do_blabla(height, width)                       
     set_default_width = classmethod(set_default_width)     

>a = A()
>a.make_picture()
>A.default_width = 150
>a.make_picture() # Now with new default width
>
>
>One might argue that this could could benefit from resolving
>self.default_width instead, that would still require setting
>height=None and testing inside make_picture.

Or maybe have the implict lambda have an implicit self arg bound like a method
if it is the default arg of a method, ie., so you could write

 class A:
     default_height=100
     default_width=200
     def make_picture(self, height: self.default_height, 
                            width: self.default_width):
         self.gui.do_blabla(height, width)                       

Then when the function was called, it would be like getting an implicit something like

 >>> class A(object):
 ...     default_height=100
 ...     default_width=200
 ...     def make_picture(self, height=Defer(lambda self: self.default_height),
 ...                            width=Defer(lambda self: self.default_width)):
 ...         if isinstance(height, Defer): height = height.fun.__get__(self)()
 ...         if isinstance(width, Defer): width = width.fun.__get__(self)()
 ...         self.gui.do_blabla(height, width)
 ...     class gui(object): # something to catch the above ;-/
 ...         def do_blabla(h,w): print 'h=%r, w=%r'%(h,w)
 ...         do_blabla = staticmethod(do_blabla)
 ...
 >>> a=A()
 >>> a.make_picture()
 h=100, w=200
 >>> a.make_picture('one')
 h='one', w=200
 >>> a.make_picture('one','two')
 h='one', w='two'

Obviously the nested class gui is just to make the self.gui.do_blabla call work as spelled ;-)

I doubt if this is going to make it, but I think it's feasible without backwards breakage.
Write a PEP if you want to pursue something like that, but don't get overexcited ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list