bug or feature?

2005-10-05 Thread beza1e1
Coming back from a bug hunt, i am not sure what to think of this python
behaviour. Here is a demo program:

class A:
   def __init__(self, lst=[]):
  self.lst = lst

a = A()
b = A()
b.lst.append("hallo")
print a.lst # output: ["hallo"]

The point seems to be, that lst=[] creates a class attribute (correct
name?), which is shared by all instances of A. So a.lst ist the same
object as b.lst, despite the fact, that object a is different to object
b.

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread skip

beza1e1> class A:
beza1e1>def __init__(self, lst=[]):
beza1e1>   self.lst = lst

Lists are mutable and default args are only evaluated once, at function
definition.  If you want independent default args use:

class A:
def __init__(self, lst=None):
if lst is None:
lst = []
self.lst = lst

The same scheme would work for other mutable types (dicts, sets, etc).

This same question gets asked once a month or so.  I'm sure this is in the
Python FAQ (check the website), but it was faster to reply than to look it
up...

Skip
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Tomasz Lisowski
beza1e1 wrote:
> Coming back from a bug hunt, i am not sure what to think of this python
> behaviour. Here is a demo program:
> 
> class A:
>def __init__(self, lst=[]):
>   self.lst = lst
> 
> a = A()
> b = A()
> b.lst.append("hallo")
> print a.lst # output: ["hallo"]
> 
> The point seems to be, that lst=[] creates a class attribute (correct
> name?), which is shared by all instances of A. So a.lst ist the same
> object as b.lst, despite the fact, that object a is different to object
> b.
> 

It is an *instance attribute* by nature, since it does not reside in the 
class object, but only in its instances. The truth is, that a.lst and 
b.lst point to the same memory object, so it seems to behave much like 
the class attribute :)

It is no more different from the simple fact, that two variables 
(attributes) may point to the same memory object, like you see below:

a = b = []
a.append("hallo")
print b #output: ["hallo"]

In fact, it is usually a bad practice to assign instance attributes a 
reference to the compound variable, existing in an external scope. Example:

aList = []

class A:
   def __init__(self, lst): #no default attribute!
 self.lst = lst

a = A(aList)
aList.append("hallo")
print a.lst #output: ["hallo"]

and your default value (, lst=[]) IS such an variable! The bad thing is, 
that the value of the instance attribute 'lst' (example above) depends 
on the external variable, which may be independently modified, thus 
modifying unexpectedly the instance attribute. The safer approach, of 
course is to write:

class A:
   def __init__(self, lst): #no default attribute!
 self.lst = lst[:] #take a copy

Summing up, is it an error, or a feature? I would say - a feature. 
Everyone should be aware, that the argument default values are evaluated 
once, and the same value (memory object) is reused at each instance 
creation.

Best regards,
Tomasz Lisowski
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Simon Percivall
Python.org General FAQ 1.4.21: Why are default values shared between
objects?
(http://www.python.org/doc/faq/general.html#why-are-default-values-shared-between-objects)

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Steve Holden
beza1e1 wrote:
> Coming back from a bug hunt, i am not sure what to think of this python
> behaviour. Here is a demo program:
> 
> class A:
>def __init__(self, lst=[]):
>   self.lst = lst
> 
> a = A()
> b = A()
> b.lst.append("hallo")
> print a.lst # output: ["hallo"]
> 
> The point seems to be, that lst=[] creates a class attribute (correct
> name?), which is shared by all instances of A. So a.lst ist the same
> object as b.lst, despite the fact, that object a is different to object
> b.
> 
Interestingly I couldn't find this in the FAQ, though it *is* a 
frequently-asked question [note: my not finding it doesn't guarantee 
it's not there]. The nearest I could get was in

 
http://www.python.org/doc/faq/programming.html#my-program-is-too-slow-how-do-i-speed-it-up

which says:

"""Default arguments can be used to determine values once, at compile 
time instead of at run time."""

The point is that the value of the keyword argument is determined when 
the def statement is executed (which is to say when the function body is 
being bound to its name).

If the default argument is (a reference to) a mutable object (such as a 
list instance) then if one call to the function modifies that mutable 
object, subsequent calls see the mutated instance as the default value.

regards
  Steve
-- 
Steve Holden   +44 150 684 7255  +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006  www.python.org/pycon/

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Fredrik Lundh
Steve Holden wrote:

> Interestingly I couldn't find this in the FAQ, though it *is* a
> frequently-asked question [note: my not finding it doesn't guarantee
> it's not there].

it's there:


http://www.python.org/doc/faq/general.html#why-are-default-values-shared-between-objects

(maybe "default values" should be changed to "default argument values")

it's also mentioned in chapter 4 of the tutorial:

http://docs.python.org/tut/node6.html#SECTION00671

 "*Important warning*: The default value is evaluated only once. This
makes a difference when the default is a mutable object such as a list,
dictionary, or instances of most classes. "

(the text then illustrates this with examples, and shows how to do things
 instead)

and in the description of "def" in the language reference:

http://docs.python.org/ref/function.html

   "*Default parameter values are evaluated when the function definition
is executed*. This means that the expression is evaluated once, when the
function is defined, and that that same "pre-computed" value is used for
each call. This is especially important to understand when a default para-
meter is a mutable object, such as a list or a dictionary: if the function
modifies the object (e.g. by appending an item to a list), the default
value is in effect modified."

(the text then shows how to do things instead)





-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread beza1e1
Thanks for you answer! This copy trick is the most elegant solution i
think.

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Steven D'Aprano
On Wed, 05 Oct 2005 03:39:30 -0700, beza1e1 wrote:

> Coming back from a bug hunt, i am not sure what to think of this python
> behaviour.

[snip code]

> The point seems to be, that lst=[] creates a class attribute (correct
> name?), which is shared by all instances of A. So a.lst ist the same
> object as b.lst, despite the fact, that object a is different to object
> b.

Not a bug, but not really a feature as such, it is a side-effect of
the way Python works. I guess that makes it a gotcha.

Argument defaults are set at compile time. You set the argument default to
a mutable object, an empty list. Every time you call the function,
you are appending to the same list.

This is not a problem if your argument default is a string, or a number,
or None, since these are all immutable objects that can't be changed.

I suppose someone might be able to come up with code that deliberately
uses this feature for good use, but in general it is something you want to
avoid. Instead of:

def spam(obj, L=[]):
L.append(obj)

do this:

def spam(obj, L=None):
if L is None: L = []
L.append(obj)


-- 
Steven.

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Ben Sizer
Fredrik Lundh wrote:

> it's also mentioned in chapter 4 of the tutorial:
>
> http://docs.python.org/tut/node6.html#SECTION00671
>
>  "*Important warning*: The default value is evaluated only once. This
> makes a difference when the default is a mutable object such as a list,
> dictionary, or instances of most classes. "

Perhaps it would be a good idea if Python actually raised a warning
(SyntaxWarning?) if you use an unnamed list or dict as a default
argument. This would doubtless help quite a few beginners. And for
people who really do want that behaviour, working around the warning
should involve minimal extra code, with extra clarity thrown in for
free.

-- 
Ben Sizer

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Steve Holden
beza1e1:

> Coming back from a bug hunt, i am not sure what to think of this python
> behaviour. Here is a demo program:
> 
> class A:
>def __init__(self, lst=[]):
>   self.lst = lst
> 
> a = A()
> b = A()
> b.lst.append("hallo")
> print a.lst # output: ["hallo"]
> 
> The point seems to be, that lst=[] creates a class attribute (correct
> name?), which is shared by all instances of A. So a.lst ist the same
> object as b.lst, despite the fact, that object a is different to object
> b.
> 
Fredrik Lundh wrote:
> Steve Holden wrote:
> 
> 
>>Interestingly I couldn't find this in the FAQ, though it *is* a
>>frequently-asked question [note: my not finding it doesn't guarantee
>>it's not there].
> 
> 
> it's there:
> 
> 
> http://www.python.org/doc/faq/general.html#why-are-default-values-shared-between-objects
> 
> (maybe "default values" should be changed to "default argument values")
> 
I couldn't believe it wasn't, but you're right: it should be easier to 
find, and a change of wording may do that.

regards
  Steve
-- 
Steve Holden   +44 150 684 7255  +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006  www.python.org/pycon/

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Steve Holden
Ben Sizer wrote:
> Fredrik Lundh wrote:
> 
> 
>>it's also mentioned in chapter 4 of the tutorial:
>>
>>http://docs.python.org/tut/node6.html#SECTION00671
>>
>> "*Important warning*: The default value is evaluated only once. This
>>makes a difference when the default is a mutable object such as a list,
>>dictionary, or instances of most classes. "
> 
> 
> Perhaps it would be a good idea if Python actually raised a warning
> (SyntaxWarning?) if you use an unnamed list or dict as a default
> argument. This would doubtless help quite a few beginners. And for
> people who really do want that behaviour, working around the warning
> should involve minimal extra code, with extra clarity thrown in for
> free.
> 
This would have to be extended to any mutable object. How does the 
compiler know which objects are mutable?

This would not be a good change.

regards
  Steve
-- 
Steve Holden   +44 150 684 7255  +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006  www.python.org/pycon/

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: bug or feature?

2005-10-05 Thread Fredrik Lundh
Steven D'Aprano wrote:

> I suppose someone might be able to come up with code that deliberately
> uses this feature for good use

argument binding is commonly used for optimization, and to give simple
functions persistent storage (e.g. memoization caches).

more importantly, it's the standard pydiom for passing object *values* (of
any kind) into an inner scope:

x = something

def myfunc(arg, x=x):
# myfunc needs the current value, not whatever x
# happens to be when the function is called

here's a typical gotcha:

for i in range(10):
def cb():
print "slot", i, "fired"
register_callback(slot=i, callback=cb)

to make this work as expected, you have to do

for i in range(10):
def cb(i=i):
print "slot", i, "fired"
register_callback(slot=i, callback=cb)





-- 
http://mail.python.org/mailman/listinfo/python-list


textwrap.dedent() drops tabs - bug or feature?

2005-11-17 Thread Steven Bethard
So I've recently been making pretty frequent use of textwrap.dedent() to 
allow me to use triple-quoted strings at indented levels of code without 
getting the extra spaces prefixed to each line.  I discovered today that 
not only does textwrap.dedent() strip any leading spaces, but it also 
substitutes any internal tabs with spaces.  For example::

py> def test():
... x = ('abcd  efgh\n'
...  'ijkl  mnop\n')
... y = textwrap.dedent('''\
... abcdefgh
... ijklmnop
... ''')
... return x, y
...
py> test()
('abcd\tefgh\nijkl\tmnop\n', 'abcdefgh\nijklmnop\n')

Note that even though the tabs are internal, they are still removed by 
textwrap.dedent().  The documentation[1] says:

"""
dedent(text)
 Remove any whitespace that can be uniformly removed from the left 
of every line in text.

 This is typically used to make triple-quoted strings line up with 
the left edge of screen/whatever, while still presenting it in the 
source code in indented form.
"""

So it looks to me like even if this is a "feature" it is undocumented. 
I'm planning on filing a bug report, but I wanted to check here first in 
case I'm just smoking something.

STeVe

[1] http://docs.python.org/lib/module-textwrap.html
-- 
http://mail.python.org/mailman/listinfo/python-list


New os.path.exists() behavior - bug or feature?

2006-12-17 Thread klappnase
Hi all,
yesterday I installed Python-2.5 and python-2.4.4 on my windows2k box.
When trying to use os.path.exists() to detect which drives are present
on the system I noticed that
these behave differently:

Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)]
on win32
Type "copyright", "credits" or "license()" for more information.

>>> import os
>>> for char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
if os.path.exists('%s:\\' % char):
print '%s:\\' % char


A:\
C:\
D:\
E:\

>>>

###

Python 2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit
(Intel)] on win32
Type "copyright", "credits" or "license()" for more information.

>>> import os
>>> for char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
if os.path.exists('%s:\\' % char):
print '%s:\\' % char


C:\
>>>

When I insert a floppy into A: os.path.exists('A:\\') will return True
on Python-2.5, too.
Does anyone know, is this inconsistent behavior a bug or a new feature?

I also noticed that the Tix binaries are no longer present in 2.5, does
anyone know if it is
planned to remove them pemanently?

Thanks in advance

Michael

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: textwrap.dedent() drops tabs - bug or feature?

2005-11-18 Thread Peter Hansen
Steven Bethard wrote:
> Note that even though the tabs are internal, they are still removed by 
> textwrap.dedent().  The documentation[1] says:
...
> So it looks to me like even if this is a "feature" it is undocumented. 
> I'm planning on filing a bug report, but I wanted to check here first in 
> case I'm just smoking something.

While I wouldn't say it's obvious, I believe it is (indirectly?) 
documented and deliberate.

Search for this in the docs:
"""
expand_tabs
 (default: True) If true, then all tab characters in text will be 
expanded to spaces using the expandtabs() method of text.
"""

(This is not to say a specific rewording of the docs to lessen any 
confusion in this area wouldn't be welcomed.)

-Peter
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: textwrap.dedent() drops tabs - bug or feature?

2005-11-19 Thread Steven Bethard
Peter Hansen wrote:
> Steven Bethard wrote:
> 
>> Note that even though the tabs are internal, they are still removed by 
>> textwrap.dedent().  The documentation[1] says:
> 
> ...
> 
>> So it looks to me like even if this is a "feature" it is undocumented. 
>> I'm planning on filing a bug report, but I wanted to check here first 
>> in case I'm just smoking something.
> 
> While I wouldn't say it's obvious, I believe it is (indirectly?) 
> documented and deliberate.
> 
> Search for this in the docs:
> """
> expand_tabs
> (default: True) If true, then all tab characters in text will be 
> expanded to spaces using the expandtabs() method of text.
> """

Thanks for double-checking this for me.  I looked at expand_tabs, and 
it's part of the definition of the TextWrapper class, which is not 
actually used by textwrap.dedent().  So I think the textwrap.dedent() 
expanding-of-tabs behavior is still basically undocumented.

I looked at the source code, and the culprit is the first line of the 
function definition:

 lines = text.expandtabs().split('\n')

I filed a bug_ report, but left the Category unassigned so that someone 
else can decide whether it's a doc bug or a code bug.

STeVe

.. _bug: http://python.org/sf/1361643
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: textwrap.dedent() drops tabs - bug or feature?

2005-11-19 Thread Peter Hansen
Steven Bethard wrote:
> Thanks for double-checking this for me.  I looked at expand_tabs, and 
> it's part of the definition of the TextWrapper class, which is not 
> actually used by textwrap.dedent().  So I think the textwrap.dedent() 
> expanding-of-tabs behavior is still basically undocumented.

Ah, good point.  I saw dedent() in there with the wrap() and fill() 
convenience functions which use a TextWrapper internally, but you're 
quite right that dedent() is different, and in fact merely uses the 
expandtabs() functionality of the standard string class, so this has 
nothing to do with the expand_tabs attribute I pointed out.

-Peter
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: New os.path.exists() behavior - bug or feature?

2006-12-18 Thread Martin v. Löwis
klappnase schrieb:
> When I insert a floppy into A: os.path.exists('A:\\') will return True
> on Python-2.5, too.
> Does anyone know, is this inconsistent behavior a bug or a new feature?

Neither, nor. In both cases, the operating system is asked, and gives
this answer. However, in the Windows API, there is no "exists" function
(nor is there on Unix); instead, exists is implemented by calling
several underlying functions. The precise set of functions used did
change between 2.4 and 2.5.

It is quite difficult to investigate the precise nature of the change
that leads to this change in observable behavior. If you think this is
a bug, it would be best if you could also investigate a patch.

> I also noticed that the Tix binaries are no longer present in 2.5, does
> anyone know if it is
> planned to remove them pemanently?

That was a mistake, I'll add it back in 2.5.1.

Regards,
Martin
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: New os.path.exists() behavior - bug or feature?

2006-12-19 Thread klappnase

Martin v. Löwis schrieb:
>
> Neither, nor. In both cases, the operating system is asked, and gives
> this answer. However, in the Windows API, there is no "exists" function
> (nor is there on Unix); instead, exists is implemented by calling
> several underlying functions. The precise set of functions used did
> change between 2.4 and 2.5.
>
> It is quite difficult to investigate the precise nature of the change
> that leads to this change in observable behavior. If you think this is
> a bug, it would be best if you could also investigate a patch.
>

I don't know if it is a bug; at least it is backwards incompatible,
which I think is never a good thing.
Unfortunately, I am afraid writing a patch is beyond my expertise :(

Regards

Michael

-- 
http://mail.python.org/mailman/listinfo/python-list

Re: New os.path.exists() behavior - bug or feature?

2007-01-24 Thread Martin Miller
FWIW, your code works correctly for me in all respects with Python 2.5
on Windows XP Pro.
I no longer have Python 2.4.x installed, so can't easily do a
comparison.

Perhaps the problem has something to do with Python 2.5 with Windows
2K.

-Martin


On Dec 17 2006, 4:29 pm, "klappnase" <[EMAIL PROTECTED]> wrote:
> Hi all,
> yesterday I installed Python-2.5 and python-2.4.4 on my windows2k box.
> When trying to use os.path.exists() to detect which drives are present
> on the system I noticed that
> these behave differently:
>
> Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)]
> on win32
> Type "copyright", "credits" or "license()" for more information.
>
> >>> import os
> >>> for char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':if 
> >>> os.path.exists('%s:\\' % char):
> print '%s:\\' % char
>
> A:\
> C:\
> D:\
> E:\
>
> ###
>
> Python 2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit
> (Intel)] on win32
> Type "copyright", "credits" or "license()" for more information.
>
> >>> import os
> >>> for char in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':if 
> >>> os.path.exists('%s:\\' % char):
> print '%s:\\' % char
>
> C:\
>
> When I insert a floppy into A: os.path.exists('A:\\') will return True
> on Python-2.5, too.
> Does anyone know, is this inconsistent behavior a bug or a new feature?
>
> I also noticed that theTixbinaries are no longer present in 2.5, does
> anyone know if it is
> planned to remove them pemanently?
> 
> Thanks in advance
> 
> Michael

-- 
http://mail.python.org/mailman/listinfo/python-list


is it bug or feature in xml.dom.minidom?

2007-03-02 Thread Maksim Kasimov

Hi, i'm faced with such a problem when i use xml.dom.minidom:

to append all child nodes from "doc" in "_requ" to "doc" in "_resp", i do the 
following:

_requ = 
minidom.parseString("OneTwo")
_resp = minidom.parseString("")


iSourseTag = _requ.getElementsByTagName('doc')[0]
iTargetTag = _resp.getElementsByTagName('doc')[0]


# it prints me that there are two child nodes
for iChild in iSourseTag.childNodes:
print iChild.toxml()


# when i walk elements, only first iteration was made
# and iSourseTag.childNodes now have only one element instead of two
for iChild in iSourseTag.childNodes:
iTargetTag.appendChild(iChild)


# it prints me that there is only one child node
for iChild in iSourseTag.childNodes:
print iChild.toxml()

i'm not sure, whether i append child nodes in properly way, but IMHO it looks 
like a bug.

My question is how to avoid the "iSourseTag" changes while iterate its nodes?

And, of course, how to append all child nodes to "iTargetTag"?

Thank for any help.

-- 
Maksim Kasimov
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: is it bug or feature in xml.dom.minidom?

2007-03-02 Thread Paul Boddie
Maksim Kasimov wrote:
> Hi, i'm faced with such a problem when i use xml.dom.minidom:
>
> to append all child nodes from "doc" in "_requ" to "doc" in "_resp", i do the 
> following:
>
> _requ = 
> minidom.parseString("OneTwo")
> _resp = minidom.parseString("")

Note that these are different documents - this is important later on.

> iSourseTag = _requ.getElementsByTagName('doc')[0]
> iTargetTag = _resp.getElementsByTagName('doc')[0]
>
>
> # it prints me that there are two child nodes
> for iChild in iSourseTag.childNodes:
>  print iChild.toxml()

Seems alright.

> # when i walk elements, only first iteration was made
> # and iSourseTag.childNodes now have only one element instead of two
> for iChild in iSourseTag.childNodes:
>  iTargetTag.appendChild(iChild)

But since you're taking a node from one document to add it to another,
you should instead use importNode to make that node importable into
the target document:

for iChild in iSourseTag.childNodes:
# 1 or True should cause a deep copy
iNewChild = _resp.importNode(iChild, 1)
iTargetTag.appendChild(iNewChild)

> # it prints me that there is only one child node
> for iChild in iSourseTag.childNodes:
>  print iChild.toxml()

That's probably because you've "stolen" the node from its document in
order to add it to the target document - something which is possibly
an artifact of the minidom implementation.

> i'm not sure, whether i append child nodes in properly way, but IMHO it looks 
> like a bug.

That minidom does not refuse to let you "move" nodes in this way could
be debated as being a bug or not, but the correct way of copying nodes
is to use importNode.

> My question is how to avoid the "iSourseTag" changes while iterate its nodes?
>
> And, of course, how to append all child nodes to "iTargetTag"?

These questions are hopefully answered above.

Paul

-- 
http://mail.python.org/mailman/listinfo/python-list