Empty list as default parameter

Bengt Richter bokr at oz.net
Sat Nov 22 21:43:23 EST 2003


On Fri, 21 Nov 2003 13:26:13 +0000, Alex Panayotopoulos <A.Panayotopoulos at sms.ed.ac.uk> wrote:

>On Fri, 21 Nov 2003, anton muhin wrote:
>
>> Really common mistake: lists are _mutable_ objects and self.myList 
>> references the same object as the default parameter. Therefore 
>> a.myList.append modifies default value as well.
>
>It does?!
>Ah, I've found it: listHolder.__init__.func_defaults
>
>Hmm... this behaviour is *very* counter-intuitive. I expect that if I were 
>to define a default to an explicit object...
>
>	def process_tree1( start=rootNode )
>
>...then I should indeed be able to process rootNode through manipulating 
>start. However, if I define a default as a new instance of an object...
>
>	def process_tree2( myTree=tree() )
>
>...then the function should, IMHO, create a new object every time it is 
>entered. (By having func_defaults point to tree.__init__, or summat.)
>
>Was there any reason that this sort of behaviour was not implemented?
Yes. The bindings of default values for call args are evaluated at define-time,
in the context of the definition, not at execution time, when the function is called.

If you want to specify what to call at execution time, you have to call it at execution time,
so you either have to pass a reference to the thing to call ('tree' in this case) or have it
otherwise visible from inside the function, e.g., the interface could be something like

    def process_tree2(treemaker=tree):
        myTree = treemaker()
        ...

Now if you wanted to pass a pre-existing tree to this kind of process_tree2, you'd have to
pass a callable that would execute to produce the tree, e.g., call it like

    temp_treemaker = lambda: rootNode
    process_tree2(temp_treemaker) # or just put the lambda expression right in the call

More likely, you'd want to be able to accept a tree, or make one if nothing was passed. E.g.,

    def process_tree3(myTree=None):
        if myTree is None: myTree = tree() # here tree must be visible in an enclosing scope
        ...                                # -- usually global, but not necessarily

a variant would be to have a default value of a single particular tree, as in your first example,
and then test whether something executable, like tree (not tree()) was being passed, e.g.,

    def process_tree1( start=rootNode ):
        if callable(start): start = start()  # e.g., if called like process_tree1(tree)

Of course, if a node object is also callable, you have to make a different check ;-)
   
>
>> Mutable defaults are better avoided (except for some variants of memo
>> pattern). Standard trick is:
>> 
>>     def __init__(self, myList = None):
>>         if myList is None:
>>             self.myList = []
>>         else:
>>             self.myList = myList
>
>Thank you. I shall use this in my code. (Although I would have preferred a 
>trick that uses less lines!)
>
How about two less? I usually do it (with no trick ;-) like:

       def __init__(self, myList = None):
           if myList is None: myList = []        
           self.myList = myList

Regards,
Bengt Richter




More information about the Python-list mailing list