Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Keyword arguments - strange behaviour?

Reply
Thread Tools

Keyword arguments - strange behaviour?

 
 
Fredrik Lundh
Guest
Posts: n/a
 
      12-21-2004
"harold fellermann" wrote:

> I cannot see any strange behavior. this code works exacly as you and I suspect:


you seem to have missed some of the posts that led up to the one you
replied to. (most importantly, the first one).

</F>



 
Reply With Quote
 
 
 
 
Steven Bethard
Guest
Posts: n/a
 
      12-21-2004
http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> However, is there a good reason why default parameters aren't evaluated
> as the function is called? (apart from efficiency and backwards
> compatibility)?


So, one of my really common use cases that takes advantage of the fact
that default parameters are evaluated at function definition time:

def foo(bar, baz, matcher=re.compile(r'...')):
...
text = matcher.sub(r'...', text)
...

If default parameters were evaluated when the function was called, my
regular expression would get re-compiled every time foo() was called.
This would be inefficient, especially if foo() got called a lot. If
Python 3000 changed the evaluation time of default parameters, I could
rewrite this code as:

class foo(object):
matcher=re.compile(r'...')
def __new__(self, bar, baz, matcher=None):
if matcher is None:
matcher = self.matcher
...
text = matcher.sub(r'...', text)
...

But that seems like a lot of work to do something that used to be pretty
simple...

Steve
 
Reply With Quote
 
 
 
 
brian.bird@securetrading.com
Guest
Posts: n/a
 
      12-23-2004
> Channelling the effbot, I think he was asking what namespace context
you
> expected the expression "arg=otherfunction(x)" to be evaluated in

when
> it's used at the time of a function call to dynamically create a new
> default value for arg.


Thanks, I realise now that's what was meant. I think I was just
assuming otherfunction() would be globally available - but that's not
something I want to go into since I can see now there will be problems
trying to define what python should do if it evaluated defaults at the
function call

I think I can now explain to someone why there are good reasons that
the default params are evaluated at the definition. However, I still
can't give a nice looking solution on how to re-write a function to
have empty mutable values as default arguments: eg.

def method(a,b,opt1=[],opt2=None,opt3="",opt4={})

How could I re-write this (especially if there are perhaps 20 optional
parameters,any number of which may have mutable defaults) without
writing 20 "if opt1 is None: opt1=[]" statements?

Brian

 
Reply With Quote
 
Fuzzyman
Guest
Posts: n/a
 
      12-23-2004

(E-Mail Removed) wrote:
> > Channelling the effbot, I think he was asking what namespace

context
> you
> > expected the expression "arg=otherfunction(x)" to be evaluated in

> when
> > it's used at the time of a function call to dynamically create a

new
> > default value for arg.

>
> Thanks, I realise now that's what was meant. I think I was just
> assuming otherfunction() would be globally available - but that's not
> something I want to go into since I can see now there will be

problems
> trying to define what python should do if it evaluated defaults at

the
> function call
>
> I think I can now explain to someone why there are good reasons that
> the default params are evaluated at the definition. However, I still
> can't give a nice looking solution on how to re-write a function to
> have empty mutable values as default arguments: eg.
>
> def method(a,b,opt1=[],opt2=None,opt3="",opt4={})
>
> How could I re-write this (especially if there are perhaps 20

optional
> parameters,any number of which may have mutable defaults) without
> writing 20 "if opt1 is None: opt1=[]" statements?
>
> Brian


One way that is *slightly neater* is to simply collect all keyword
arguments as a dictionary. Then compare with a dictionary of defaults
for any missing keywords.

def afunction(**keywargs):
defaults = {'param1' : [], 'param2' : {}, 'param3' : []}
for entry in defaults:
if not keywargs.has_key(entry):
keywargs[entry] = defaults[entry]

This keeps all your defaults in a single dictionary and avoids a really
long function definition.
Regards,

Fuzzy
http://www.voidspace.org.uk/python/index.shtml

 
Reply With Quote
 
Fuzzyman
Guest
Posts: n/a
 
      12-23-2004

(E-Mail Removed) wrote:
> > Channelling the effbot, I think he was asking what namespace

context
> you
> > expected the expression "arg=otherfunction(x)" to be evaluated in

> when
> > it's used at the time of a function call to dynamically create a

new
> > default value for arg.

>
> Thanks, I realise now that's what was meant. I think I was just
> assuming otherfunction() would be globally available - but that's not
> something I want to go into since I can see now there will be

problems
> trying to define what python should do if it evaluated defaults at

the
> function call
>
> I think I can now explain to someone why there are good reasons that
> the default params are evaluated at the definition. However, I still
> can't give a nice looking solution on how to re-write a function to
> have empty mutable values as default arguments: eg.
>
> def method(a,b,opt1=[],opt2=None,opt3="",opt4={})
>
> How could I re-write this (especially if there are perhaps 20

optional
> parameters,any number of which may have mutable defaults) without
> writing 20 "if opt1 is None: opt1=[]" statements?
>
> Brian


One way that is *slightly neater* is to simply collect all keyword
arguments as a dictionary. Then compare with a dictionary of defaults
for any missing keywords.

def afunction(**keywargs):
defaults = {'param1' : [], 'param2' : {}, 'param3' : []}
for entry in defaults:
if not keywargs.has_key(entry):
keywargs[entry] = defaults[entry]

This keeps all your defaults in a single dictionary and avoids a really
long function definition.
Regards,

Fuzzy
http://www.voidspace.org.uk/python/index.shtml

 
Reply With Quote
 
Fuzzyman
Guest
Posts: n/a
 
      12-23-2004

Steven Bethard wrote:
> (E-Mail Removed) wrote:
> > However, is there a good reason why default parameters aren't

evaluated
> > as the function is called? (apart from efficiency and backwards
> > compatibility)?

>
> So, one of my really common use cases that takes advantage of the

fact
> that default parameters are evaluated at function definition time:
>
> def foo(bar, baz, matcher=re.compile(r'...')):
> ...
> text = matcher.sub(r'...', text)
> ...
>


Surely "re.compile(r'...')" is effectively a constant ? So your above
code is equivalent to :

aConst = re.compile(r'...')
def foo(bar, baz, matcher=aConst):
....
text = matcher.sub(r'...', text)
....

I agree that dynamic evaluation of default arguments seems much more
pythonic, as well as getting rid of a common python gotcha.

Regards,

Fuzzy
http://www.voidspace.org.uk/python/index.shmtl

> If default parameters were evaluated when the function was called, my


> regular expression would get re-compiled every time foo() was called.


> This would be inefficient, especially if foo() got called a lot. If
> Python 3000 changed the evaluation time of default parameters, I

could
> rewrite this code as:
>
> class foo(object):
> matcher=re.compile(r'...')
> def __new__(self, bar, baz, matcher=None):
> if matcher is None:
> matcher = self.matcher
> ...
> text = matcher.sub(r'...', text)
> ...
>
> But that seems like a lot of work to do something that used to be

pretty
> simple...
>
> Steve


 
Reply With Quote
 
Steven Bethard
Guest
Posts: n/a
 
      12-23-2004
Fuzzyman wrote:
> Steven Bethard wrote:
>
>>(E-Mail Removed) wrote:
>>
>>>However, is there a good reason why default parameters aren't
>>>evaluated as the function is called? (apart from efficiency
>>>and backwards compatibility)?

>>
>>So, one of my really common use cases that takes advantage of the
>>fact that default parameters are evaluated at function definition
>>time:
>>
>>def foo(bar, baz, matcher=re.compile(r'...')):
>> ...
>> text = matcher.sub(r'...', text)
>> ...

>
> Surely "re.compile(r'...')" is effectively a constant ? So your above
> code is equivalent to :
>
> aConst = re.compile(r'...')
> def foo(bar, baz, matcher=aConst):
> ...
> text = matcher.sub(r'...', text)
> ...


Basically, yes. Though you add an extra name to the module or class
namespace by defining 'aConst'. I consider this a minor pollution of
the namespace since 'matcher' is only used in the function, not the
module or class.

But if we're arguing for equivalencies, the oft-asked for

def foo(lst=[]):
...

where [] is evaluated for each function call, is equivalent for most
purposes to:

def foo(lst=None):
if lst is None:
lst = []

There's a minor pollution of the range of values 'lst' can take on -- if
you need lst to be able to take on the value None, you'll want to
rewrite this something like:

no_value = object()
def foo(lst=no_value):
if lst is no_value:
lst = []

My point here is that there's always going to be some equivalent
expression (turing completeness etc.) I was just pointing out a quite
reasonable use of the current default parameter evaluation system.

Steve
 
Reply With Quote
 
Fuzzyman
Guest
Posts: n/a
 
      12-23-2004

Steven Bethard wrote:
> Fuzzyman wrote:
> > Steven Bethard wrote:
> >
> >>(E-Mail Removed) wrote:
> >>
> >>>However, is there a good reason why default parameters aren't
> >>>evaluated as the function is called? (apart from efficiency
> >>>and backwards compatibility)?
> >>
> >>So, one of my really common use cases that takes advantage of the
> >>fact that default parameters are evaluated at function definition
> >>time:
> >>
> >>def foo(bar, baz, matcher=re.compile(r'...')):
> >> ...
> >> text = matcher.sub(r'...', text)
> >> ...

> >
> > Surely "re.compile(r'...')" is effectively a constant ? So your

above
> > code is equivalent to :
> >
> > aConst = re.compile(r'...')
> > def foo(bar, baz, matcher=aConst):
> > ...
> > text = matcher.sub(r'...', text)
> > ...

>
> Basically, yes. Though you add an extra name to the module or class
> namespace by defining 'aConst'. I consider this a minor pollution of


> the namespace since 'matcher' is only used in the function, not the
> module or class.
>
> But if we're arguing for equivalencies, the oft-asked for
>
> def foo(lst=[]):
> ...
>
> where [] is evaluated for each function call, is equivalent for most
> purposes to:
>
> def foo(lst=None):
> if lst is None:
> lst = []
>
> There's a minor pollution of the range of values 'lst' can take on --

if
> you need lst to be able to take on the value None, you'll want to
> rewrite this something like:
>
> no_value = object()
> def foo(lst=no_value):
> if lst is no_value:
> lst = []
>
> My point here is that there's always going to be some equivalent
> expression (turing completeness etc.) I was just pointing out a

quite
> reasonable use of the current default parameter evaluation system.
>
> Steve


Sure.. but you also gave an example of an alternative that was complex,
and used it's complexity as an argument against having default
arguments dynamically evaluated. What I was saying is that there is an
alternative that is at least as readable (if not more) than your
example.

Having default arguments evaluated when the function is defined is
unintuitive and unpythonic.
Regards,


Fuzzy
http://www.voidspace.org.uk/python/index.shtml

 
Reply With Quote
 
Steve Holden
Guest
Posts: n/a
 
      12-23-2004
Fuzzyman wrote:

[...]
>
> Having default arguments evaluated when the function is defined is
> unintuitive and unpythonic.


With due respect that's a matter of opinion.

Having default arguments evaluated when the function is defined is the
result of several design decisions in Python, not the least of which is
the decision to have "def" be an executable statement. Ever wondered how
come you can def the same function name several times in the same
module? It's simply because on of def's side-effects is the binding of
its name in the current namespace.

Unfortunately there's absolutely no guarantee that the namespace context
when a function is called will bear any relation to the namespace
context when it was defined (particularly given that definition and call
can take place in entirely separate modules). So, how would one specify
a "deferred expression" to be evaluated when the function is called
rather than when it is defined? There presumably has to be some
technique that's different from the current ones, since presumably you
would also want to retain the option of having the default evaluated at
definition time.

I still haven't seen any reasonable suggestions as to how one might
exactly specify the execution context for such a deferred expression,
let alone how to spell the deferred expression itself. Would you have us
provide a code object that can be eval()'d?

regards
Steve
--
Steve Holden http://www.holdenweb.com/
Python Web Programming http://pydish.holdenweb.com/
Holden Web LLC +1 703 861 4237 +1 800 494 3119
 
Reply With Quote
 
Steven Bethard
Guest
Posts: n/a
 
      12-23-2004
Fuzzyman wrote:
>>>Steven Bethard wrote:
>>>>
>>>>So, one of my really common use cases that takes advantage of the
>>>>fact that default parameters are evaluated at function definition
>>>>time:
>>>>
>>>>def foo(bar, baz, matcher=re.compile(r'...')):
>>>> ...
>>>> text = matcher.sub(r'...', text)
>>>> ...

> Sure.. but you also gave an example of an alternative that was complex,


Interesting. I would have thought that my example was pretty simple.
Maybe it would be helpful to generalize it to:

def foo(bar, baz, spam=badger(x, y, z)):
...

All it does is use a default value that was produced by a function call.
I'm surprised you haven't run into this situation before...

Of course, what is complex or simple is a matter of personal opinion. I
use this pattern so often that it's quite simple to me, but I guess I
can understand that if you don't use such a pattern, it might seem
foreign to you.

Steve
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
RE: keyword checker - keyword.kwlist Hamilton, William Python 4 05-13-2007 06:31 AM
keyword checker - keyword.kwlist tom@finland.com Python 6 05-10-2007 04:53 PM
strange behaviour with keyword arguments and inheritance matthewperpick Python 8 04-17-2007 03:06 PM
Keyword arguments in a block, possible with zero arguments? Peter Motzfeldt Ruby 1 03-13-2007 01:33 PM
Difference between default arguments and keyword arguments Edward Diener Python 14 04-05-2004 11:26 PM



Advertisments