bug or feature?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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