473,503 Members | 11,237 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Bizarre method keyword-arg bug.

I'm stumped. I'm calling a method that has keyword args, but not
setting them, and yet one of them starts off with data?!

The class definition begins like so:

class BattleIntentionAction( BattleAction ):
def __init__( self, factionName, location, tactic='hold',
targetFacName='', terrainArgs=[], garrisonIds=[] ):
self.terrainArgs = terrainArgs
print terrainArgs

The constructor is called somewhere else, like so:
act = BattleIntentionAction( facName, self.location )
During this object's construction, terrainArgs is set to a list with
values corresponding to a previously created BattleIntentionAction!
Even more bizarre, the terrainArgs param is a testing formality, and
doesn't actually get used anywhere in my code -- the corresponding
attribute is always modified after object creation. Furthermore, this
doesn't happen with the other keyword args...

Obviously, I'm glossing over a ton of code here, but I'm having a
tough time isolating this problem, as it seems to be very dependent on
events leading up to it. It feels like the sort of memory stomping
bug I remember seeing from days of yore when I hacked C++. :-(
I frankly don't understand how "terrainArgs" can have a value if
nothing is passed for it on the calling invocation, short of some
obscure compiler bug (this is Python 2.4.3). Am I being naive? Is
there some way I could be bringing this about myself?

I can easily work around this weirdness by having the caller set
terrainArgs explicitly, but I can't shake the sensation that this
"fix" just masks some deeper flaw in my code.
Arg!
-Jasper
Aug 18 '08 #1
20 1254
2008/8/18 Jasper <ja****@peak.org>:
I'm stumped. I'm calling a method that has keyword args, but not
setting them, and yet one of them starts off with data?!
<http://www.python.org/doc/faq/general/#why-are-default-values-shared-between-objects>

--
Cheers,
Simon B.
si***@brunningonline.net
http://www.brunningonline.net/simon/blog/
GTalk: simon.brunning | MSN: small_values | Yahoo: smallvalues | Twitter: brunns
Aug 18 '08 #2
Jasper schrieb:
I'm stumped. I'm calling a method that has keyword args, but not
setting them, and yet one of them starts off with data?!

The class definition begins like so:

class BattleIntentionAction( BattleAction ):
def __init__( self, factionName, location, tactic='hold',
targetFacName='', terrainArgs=[], garrisonIds=[] ):
self.terrainArgs = terrainArgs
print terrainArgs

The constructor is called somewhere else, like so:
act = BattleIntentionAction( facName, self.location )
During this object's construction, terrainArgs is set to a list with
values corresponding to a previously created BattleIntentionAction!
Even more bizarre, the terrainArgs param is a testing formality, and
doesn't actually get used anywhere in my code -- the corresponding
attribute is always modified after object creation. Furthermore, this
doesn't happen with the other keyword args...

Obviously, I'm glossing over a ton of code here, but I'm having a
tough time isolating this problem, as it seems to be very dependent on
events leading up to it. It feels like the sort of memory stomping
bug I remember seeing from days of yore when I hacked C++. :-(
I frankly don't understand how "terrainArgs" can have a value if
nothing is passed for it on the calling invocation, short of some
obscure compiler bug (this is Python 2.4.3). Am I being naive? Is
there some way I could be bringing this about myself?

I can easily work around this weirdness by having the caller set
terrainArgs explicitly, but I can't shake the sensation that this
"fix" just masks some deeper flaw in my code.
This is a FAQ:

http://effbot.org/pyfaq/why-are-defa...en-objects.htm

Diez
Aug 18 '08 #3
Jasper wrote:
I'm stumped. I'm calling a method that has keyword args, but not
setting them, and yet one of them starts off with data?!

The class definition begins like so:

class BattleIntentionAction( BattleAction ):
def __init__( self, factionName, location, tactic='hold',
targetFacName='', terrainArgs=[], garrisonIds=[] ):
self.terrainArgs = terrainArgs
print terrainArgs

The constructor is called somewhere else, like so:
act = BattleIntentionAction( facName, self.location )

During this object's construction, terrainArgs is set to a list with
values corresponding to a previously created BattleIntentionAction!
default argument values are evaluated when the function object is
created (by the "def" statement, that is), not when the resulting
function is called. if you mutate the default values, the mutations
will stick.

this is explained in the FAQ, the tutorial, and the reference manual,
and hopefully in your favourite python book as well; see e.g.

http://docs.python.org/tut/node6.htm...00000000000000
http://docs.python.org/ref/function.html

</F>

Aug 18 '08 #4
Fredrik Lundh wrote:
default argument values are evaluated when the function object is
created (by the "def" statement, that is), not when the resulting
function is called. if you mutate the default values, the mutations
will stick.
and yes, workarounds and further details are provided here:

http://effbot.org/zone/default-values.htm

</F>

Aug 18 '08 #5
On Aug 18, 1:49 am, "Simon Brunning" <si...@brunningonline.netwrote:
2008/8/18 Jasper <jas...@peak.org>:
I'm stumped. I'm calling a method that has keyword args, but not
setting them, and yet one of them starts off with data?!

<http://www.python.org/doc/faq/general/#why-are-default-values-shared-...>

--
Cheers,
Simon B.

Uggg! /That's/ an intuitive side-effect/wart. :-/

Thanks for sorting me out!

-Jasper
Aug 18 '08 #6
Jasper wrote:
Uggg! /That's/ an intuitive side-effect/wart. :-/
it's done that way on purpose, of course, because evaluating a full
closure for each default argument at every call would greatly hurt
performance (and lead to another set of surprises, of course).

please don't label things that you don't understand and haven't spent
any time reflecting over as bugs or warts; that's disrespectful to the
designers and probably not good for your blood pressure.

</F>

Aug 18 '08 #7
On Aug 18, 2:40 am, Fredrik Lundh <fred...@pythonware.comwrote:
Jasper wrote:
Uggg! /That's/ an intuitive side-effect/wart. :-/

it's done that way on purpose, of course, because evaluating a full
closure for each default argument at every call would greatly hurt
performance (and lead to another set of surprises, of course).

please don't label things that you don't understand and haven't spent
any time reflecting over as bugs or warts; that's disrespectful to the
designers and probably not good for your blood pressure.

</F>
I understand it's done that way on purpose, and that there are
tradeoffs
involved, but frankly your /guess/ that I don't understand is wrong.
Having
used Python for some 15 years, I'm hardly a neophyte -- it's pure
serendipity
that this hasn't bitten me before.

I can see the elegance from a language design perspective, the speed
advantage,
etc. Nonetheless, it's an unintuitive wart, hurting Python's clarity
-- as evidence
I'll point out all the warnings that need to be sprinkled through the
various docs.
And no, the alternative /does not/ have an equivalent set of surprises
-- it's not
like Python is unique in having default arguments.

Frankly, if I wanted speed, I wouldn't be using python, and if I
wanted clever tricks,
I'd use Perl. Surprise caching as a side-effect is /very/ Perl-like.

-Jasper
Aug 18 '08 #8
On 18 Aug, 11:40, Fredrik Lundh <fred...@pythonware.comwrote:
Jasper wrote:
Uggg! */That's/ an intuitive side-effect/wart. *:-/

it's done that way on purpose, of course, because evaluating a full
closure for each default argument at every call would greatly hurt
performance (and lead to another set of surprises, of course).
Having had the opportunity to reflect on this recently, I'd agree that
the current behaviour is probably the better outcome in many cases,
although one usually only sees people having problems with this when
using literals (lists mostly, and often empty lists), so there's
always the question of how people perceive those literals, whether
they consider them sufficiently "low cost" to be evaluated for each
call, and so on. Indeed, issues of binding don't apply to such
literals, and I imagine that this conceals the possibility of
surprising behaviour (in the general case with names which could refer
to different things at different times) and the rationale for
implementing a mechanism which is consequently less complicated (both
for the developers and in terms of predicting program behaviour).
please don't label things that you don't understand and haven't spent
any time reflecting over as bugs or warts; that's disrespectful to the
designers and probably not good for your blood pressure.
Well, in the page of "Python warts" that I compiled when it was
claimed that Python 3000 addresses such issues in Python 2.x, the
"Mutable default arguments" entry lists at least one experienced
Python author who agrees with the inquirer's assertion:

http://wiki.python.org/moin/PythonWarts

Paul
Aug 18 '08 #9
On Aug 18, 3:04 am, Paul Boddie <p...@boddie.org.ukwrote:
>
Well, in the page of "Python warts" that I compiled when it was
claimed that Python 3000 addresses such issues in Python 2.x, the
"Mutable default arguments" entry lists at least one experienced
Python author who agrees with the inquirer's assertion:

http://wiki.python.org/moin/PythonWarts

Paul
Not surprising, as it's fairly non-standard. I'd even argue that
calling them "default arguments" is a misnomer -- they're more akin to
static variables.

It doesn't help that the solution to get the expected behavior
involves adding boiler-plate code all over.

-Jasper
Aug 18 '08 #10
On 18 Aug, 12:20, Jasper <jas...@peak.orgwrote:
>
Not surprising, as it's fairly non-standard. *I'd even argue that
calling them "default arguments" is a misnomer -- they're more akin to
static variables.
Indeed, default parameter values are occasionally suggested for that
purpose, although it has often been said that one shouldn't really use
them for that, either because there are often superior alternatives to
static variables, or because (as at least claimed in years gone by)
the behaviour may change one day. I think the latter explanation has
little substance now, at least for implementations compatible with
CPython.
It doesn't help that the solution to get the expected behavior
involves adding boiler-plate code all over.
Yes, it's a case of avoiding the full extent of the feature because it
doesn't do what one might expect. Personally, I only really use None,
numbers and strings as defaults, with the boilerplate you mention in
the function to get the initialisation that would have been provided
by the defaults. There is, however, a useful pattern which arises from
such a conservative approach: adopting None as a default (or perhaps a
special value) means that one has a way of explicitly indicating that
the default is desired, rather than omitting a parameter - something
which may not always be convenient. The boilerplate then loads the
appropriate default which may be stored in a more convenient location:
as a module global or in a class or instance attribute.

Ultimately, I suppose one could enforce some kind of least surprising
"best practice" by limiting default parameter values to being literals
of immutable objects or names, as opposed to expressions, thus
eliminating some potential confusion. Perhaps the various static code
checking tools provide guidance on this matter.

Paul
Aug 18 '08 #11
On Mon, 18 Aug 2008 04:07:14 -0700, Paul Boddie wrote:
Ultimately, I suppose one could enforce some kind of least surprising
"best practice" by limiting default parameter values to being literals
of immutable objects or names, as opposed to expressions, thus
eliminating some potential confusion.
-1

Firstly, I *like* the ability to use mutable objects as default
arguments. I don't do it often, but when I do, I do it deliberately. I
find it useful.

Secondly, I think forbidding expressions as default arguments would be
far worse than the so-called "problem" you wish to fix. It would make
such simple default arguments as these unnecessarily complicated:

def foo(x=2**64, sentinel=object())

Please don't try to "fix" this feature.
--
Steven
Aug 18 '08 #12
On Mon, 18 Aug 2008 03:20:11 -0700, Jasper wrote:
It doesn't help that the solution to get the expected behavior involves
adding boiler-plate code all over.
Expected by who?

Please don't assume that everyone has had their intuition shaped by
exposure to the same languages yours has been shaped by. What surprises
you is obvious to me.

In a previous post, you asserted that the alternative behaviour (having
default arguments re-evaluated each time the function is called) can't
possibly be surprising. You wrote:

"And no, the alternative /does not/ have an equivalent set of surprises
-- it's not like Python is unique in having default arguments."

That's simply not true. I would find this behaviour very surprising, and
I bet you would too:

>>x = "parrot"
def foo(obj=x):
.... print obj
....
>>foo() # this is the current behaviour
parrot
>>x = "shrubbery"
foo() # but this is not
shrubbery
>>del x
foo() # nor is this
Traceback (most recent call last):
...
NameError: name 'x' is not defined

--
Steven
Aug 18 '08 #13

Steven D'Aprano <st***@REMOVE-THIS-cybersource.com.auwrites:
On Mon, 18 Aug 2008 03:20:11 -0700, Jasper wrote:
"And no, the alternative /does not/ have an equivalent set of surprises
-- it's not like Python is unique in having default arguments."

That's simply not true. I would find this behaviour very surprising, and
I bet you would too:
>>>x = "parrot"
def foo(obj=x):
... print obj
...
>>>foo() # this is the current behaviour
parrot
>>>x = "shrubbery"
foo() # but this is not
shrubbery
>>>del x
foo() # nor is this
Traceback (most recent call last):
NameError: name 'x' is not defined
You may find the above surprising, but Common Lisp users expect the default
argument expression to be evaluated anew when need by a function call:

* (defvar *x* "parrot")
*X*

* (defun foo (&optional (obj *x*)) ;; optional arg, default is *x*
obj)
FOO

* (foo)
"parrot"

* (setf *x* "shrubbery")
"shrubbery"

* (foo)
"shrubbery"

* (makunbound '*x*)
*X*

* (foo)
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread"
RUNNING {10023EDE81}>:
The variable *X* is unbound.

I find the Lisp approach more reasonable. Also, an argument based on
performance for Python's current behavior seems dubious, given the
language's other performance robbing design choices.

bob
Aug 18 '08 #14
Jasper wrote:
Having used Python for some 15 years, I'm hardly a neophyte -- it's pure
serendipity that this hasn't bitten me before.
Using languages and spending time reflecting over how they're put
together are two very different things.

</F>

Aug 18 '08 #15
Steven D'Aprano wrote:
>It doesn't help that the solution to get the expected behavior involves
adding boiler-plate code all over.

Expected by who?
Blub programmers.

</F>

Aug 18 '08 #16
Robert Brown wrote:
You may find the above surprising, but Common Lisp users expect the default
argument expression to be evaluated anew when need by a function call:
I find the Lisp approach more reasonable. Also, an argument based on
performance for Python's current behavior seems dubious, given the
language's other performance robbing design choices.
well, I'd say an argument based on "Common Lisp users" is a lot more
dubious ;-)

(and some of us are actually capable of writing pretty fast code in
Python. slowing the language down (or crippling it) because some blub
programmers screams "bug!" instead of looking things up in the handbook
when they stumble upon something they haven't seen before doesn't strike
me as a good use of anyone's time.)

</F>

Aug 18 '08 #17
Fredrik Lundh <fr*****@pythonware.comwrites:
Robert Brown wrote:
>You may find the above surprising, but Common Lisp users expect the
default argument expression to be evaluated anew when needed by a
function call:

well, I'd say an argument based on "Common Lisp users" is a lot more
dubious ;-)
Actually, it's really not dubious. Because Lisp is extensible, Lisp *users*
have evolved the language considerably over the years. It's an excellent
place to look for alternative design ideas. For instance, Lisp users
experimented with several ways (LOOPS, Flavors, etc.) of supporting the
object oriented style of programming before CLOS became part of the Common
Lisp standard. If you're designing a language feature, it's often the case
that Lisp users have tried several alternatives over the last few decades
and have decided what works best, for them of course.

In any case, chances are high that Lisp's way of handling default arguments
would have been changed had it been shown to cause performance problems.
We're talking about a language used to implement operating systems --
performance is always a consideration.
Aug 20 '08 #18
On Wed, 20 Aug 2008 13:09:21 -0400, Robert Brown wrote:
In any case, chances are high that Lisp's way of handling default
arguments would have been changed had it been shown to cause performance
problems.
But nobody is suggesting that it would cause performance problems in
*Lisp*. It might, or it might not. Who cares? We're not talking about
Lisp, we're talking about *Python*, and evaluating default arguments
every time the function is called would certainly cause a performance hit
in Python.
--
Steven
Aug 20 '08 #19
Steven D'Aprano <st***@REMOVE-THIS-cybersource.com.auwrites:
On Wed, 20 Aug 2008 13:09:21 -0400, Robert Brown wrote:
>In any case, chances are high that Lisp's way of handling default
arguments would have been changed had it been shown to cause performance
problems.

But nobody is suggesting that it would cause performance problems in
*Lisp*. It might, or it might not. Who cares? We're not talking about
Lisp, we're talking about *Python*, and evaluating default arguments
every time the function is called would certainly cause a performance hit
in Python.
Please explain why it's a performance problem for Python but not for other
languages.
Aug 20 '08 #20
On Wed, 20 Aug 2008 15:58:44 -0400, Robert Brown wrote:
Steven D'Aprano <st***@REMOVE-THIS-cybersource.com.auwrites:
>On Wed, 20 Aug 2008 13:09:21 -0400, Robert Brown wrote:
>>In any case, chances are high that Lisp's way of handling default
arguments would have been changed had it been shown to cause
performance problems.

But nobody is suggesting that it would cause performance problems in
*Lisp*. It might, or it might not. Who cares? We're not talking about
Lisp, we're talking about *Python*, and evaluating default arguments
every time the function is called would certainly cause a performance
hit in Python.

Please explain why it's a performance problem for Python but not for
other languages.
Because other languages are implemented differently from Python, just as
other languages are implemented differently from Lisp, C, Basic, Forth,
Smalltalk, Whitespace, Ruby, PHP, Java, and quite a few others.

If you mean, why is it a problem for Python but not Lisp, I'm afraid I'll
have to defer to others who are more knowledgeable about the
implementation than I am. All I know is that Fredrik Lundh, who has
forgotten more about Python than I know, has stated:

"it's done that way on purpose, of course, because evaluating a full
closure for each default argument at every call would greatly hurt
performance"

But if you want a simple example, consider this function definition:

def parrot(x=expr)

Evaluating expr will take arbitrary time. If you don't believe me,
consider the pathological case of x=time.sleep(10**6). The current
semantics pays that cost once, when the def statement is executed, and
from that point on retrieving the default value of x is almost free.
Evaluating expr every time the function is called will pay that cost
every time.

Frankly, I can't see how any language, including Lisp, could possibly
avoid that runtime cost.
--
Steven
Aug 21 '08 #21

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
3746
by: Marcin | last post by:
Hello! Is there any method to detect parameters values passed to called method? For example: public Guid ApplicationLogin(string userName, string password, int dbId)
5
14406
by: kuvpatel | last post by:
Hi I want to refer a class called LogEvent, and use one of its methods called WriteMessage without actually having to create an instance of Logevent. I have tried using the word sealed with...
8
7173
by: Jeroen Smits | last post by:
To Microsoft or other people who are interested, I have a feature request: I would really like to have an option to 'hide' a property or method from an inherited class. For example when I create...
3
1131
by: Stuart McGraw | last post by:
The following was cut and pasted exactly (except for the # lines which I added after the fact) from an interactive python session in a Window 2000 cmd.exe window. Can somebody please explain to...
22
1570
by: WXS | last post by:
Sometimes a method in a class requires the use of class instance variables/fields that will not be used outside of the method itself. Currently this means you must create a instance field in the...
26
2294
by: the.tarquin | last post by:
Okay, this one has me totally baffled. I have a function, getParsedKey(char* key, char* returnString). I pass in the key I want, it retrieves it from a data structure and puts the value in...
4
4777
by: Alan T | last post by:
I got a method in my ancestor form declared as Protected, this method has empty body. In my descendant form I declared as Protected also, then compile has no problem but the name of the method has...
4
6557
by: David Zha0 | last post by:
Hi, "when we call a virtual method, the runtime will check the instance who called the method and then choose the suitable override method, this may causes the performance drop down", is this...
35
2162
by: bukzor | last post by:
I've found some bizzare behavior when using mutable values (lists, dicts, etc) as the default argument of a function. I want to get the community's feedback on this. It's easiest to explain with...
9
2052
by: raylopez99 | last post by:
I'm posting this fragment from another thread to frame the issue clearer. How to pass an object to a function/method call in C# that will guarantee not to change the object?* In C++, as seen...
0
7194
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7267
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
1
6976
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
1
4993
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
4666
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
3160
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3148
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
1495
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
1
729
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.