function parameter scope python 2.5.2

Discussion in 'Python' started by J Kenneth King, Nov 20, 2008.

  1. I recently encountered some interesting behaviour that looks like a bug
    to me, but I can't find the appropriate reference to any specifications
    to clarify whether it is a bug.

    Here's the example code to demonstrate the issue:

    class SomeObject(object):

    def __init__(self):
    self.words = ['one', 'two', 'three', 'four', 'five']

    def main(self):
    recursive_func(self.words)
    print self.words

    def recursive_func(words):
    if len(words) > 0:
    word = words.pop()
    print "Popped: %s" % word
    recursive_func(words)
    else:
    print "Done"

    if __name__ == '__main__':
    weird_obj = SomeObject()
    weird_obj.main()


    The output is:

    Popped: five
    Popped: four
    Popped: three
    Popped: two
    Popped: one
    Done
    []

    Of course I expected that recursive_func() would receive a copy of
    weird_obj.words but it appears to happily modify the object.

    Of course a work around is to explicitly create a copy of the object
    property befor passing it to recursive_func, but if it's used more than
    once inside various parts of the class that could get messy.

    Any thoughts? Am I crazy and this is supposed to be the way python works?
     
    J Kenneth King, Nov 20, 2008
    #1
    1. Advertising

  2. J Kenneth King <> writes:

    > I recently encountered some interesting behaviour that looks like a bug
    > to me, but I can't find the appropriate reference to any specifications
    > to clarify whether it is a bug.
    >
    > Here's the example code to demonstrate the issue:
    >
    > class SomeObject(object):
    >
    > def __init__(self):
    > self.words = ['one', 'two', 'three', 'four', 'five']
    >
    > def main(self):
    > recursive_func(self.words)
    > print self.words
    >
    > def recursive_func(words):
    > if len(words) > 0:
    > word = words.pop()
    > print "Popped: %s" % word
    > recursive_func(words)
    > else:
    > print "Done"
    >
    > if __name__ == '__main__':
    > weird_obj = SomeObject()
    > weird_obj.main()
    >
    >
    > The output is:
    >
    > Popped: five
    > Popped: four
    > Popped: three
    > Popped: two
    > Popped: one
    > Done
    > []
    >
    > Of course I expected that recursive_func() would receive a copy of
    > weird_obj.words but it appears to happily modify the object.
    >
    > Of course a work around is to explicitly create a copy of the object
    > property befor passing it to recursive_func, but if it's used more than
    > once inside various parts of the class that could get messy.
    >
    > Any thoughts? Am I crazy and this is supposed to be the way python works?


    Of course, providing a shallow (or deep as necessary) copy makes it
    work, I'm curious as to why the value passed as a parameter to a
    function outside the class is passed a reference rather than a copy.
     
    J Kenneth King, Nov 20, 2008
    #2
    1. Advertising

  3. J Kenneth King

    alex23 Guest

    On Nov 21, 9:40 am, J Kenneth King <> wrote:
    > Of course, providing a shallow (or deep as necessary) copy makes it
    > work, I'm curious as to why the value passed as a parameter to a
    > function outside the class is passed a reference rather than a copy.


    You're passing neither a reference nor a copy, you're passing the
    object (in this case a list) directly:

    http://effbot.org/zone/call-by-object.htm
     
    alex23, Nov 20, 2008
    #3
  4. On Nov 20, 6:40 pm, J Kenneth King <> wrote:
    > J Kenneth King <> writes:
    >
    >
    >
    > > I recently encountered some interesting behaviour that looks like a bug
    > > to me, but I can't find the appropriate reference to any specifications
    > > to clarify whether it is a bug.

    >
    > > Here's the example code to demonstrate the issue:

    >
    > > class SomeObject(object):

    >
    > >     def __init__(self):
    > >         self.words = ['one', 'two', 'three', 'four', 'five']

    >
    > >     def main(self):
    > >         recursive_func(self.words)
    > >         print self.words

    >
    > > def recursive_func(words):
    > >     if len(words) > 0:
    > >         word = words.pop()
    > >         print "Popped: %s" % word
    > >         recursive_func(words)
    > >     else:
    > >         print "Done"

    >
    > > if __name__ == '__main__':
    > >     weird_obj = SomeObject()
    > >     weird_obj.main()

    >
    > > The output is:

    >
    > > Popped: five
    > > Popped: four
    > > Popped: three
    > > Popped: two
    > > Popped: one
    > > Done
    > > []

    >
    > > Of course I expected that recursive_func() would receive a copy of
    > > weird_obj.words but it appears to happily modify the object.

    >
    > > Of course a work around is to explicitly create a copy of the object
    > > property befor passing it to recursive_func, but if it's used more than
    > > once inside various parts of the class that could get messy.

    >
    > > Any thoughts? Am I crazy and this is supposed to be the way python works?

    >
    > Of course, providing a shallow (or deep as necessary) copy makes it
    > work, I'm curious as to why the value passed as a parameter to a
    > function outside the class is passed a reference rather than a copy.


    Why should it be a copy by default ? In Python all copies have to be
    explicit.

    George
     
    George Sakkis, Nov 21, 2008
    #4
  5. J Kenneth King

    Rafe Guest

    On Nov 21, 6:31 am, J Kenneth King <> wrote:
    > I recently encountered some interesting behaviour that looks like a bug
    > to me, but I can't find the appropriate reference to any specifications
    > to clarify whether it is a bug.
    >
    > Here's the example code to demonstrate the issue:
    >
    > class SomeObject(object):
    >
    >     def __init__(self):
    >         self.words = ['one', 'two', 'three', 'four', 'five']
    >
    >     def main(self):
    >         recursive_func(self.words)
    >         print self.words
    >
    > def recursive_func(words):
    >     if len(words) > 0:
    >         word = words.pop()
    >         print "Popped: %s" % word
    >         recursive_func(words)
    >     else:
    >         print "Done"
    >
    > if __name__ == '__main__':
    >     weird_obj = SomeObject()
    >     weird_obj.main()
    >
    > The output is:
    >
    > Popped: five
    > Popped: four
    > Popped: three
    > Popped: two
    > Popped: one
    > Done
    > []
    >
    > Of course I expected that recursive_func() would receive a copy of
    > weird_obj.words but it appears to happily modify the object.
    >
    > Of course a work around is to explicitly create a copy of the object
    > property befor passing it to recursive_func, but if it's used more than
    > once inside various parts of the class that could get messy.
    >
    > Any thoughts? Am I crazy and this is supposed to be the way python works?


    You are passing a mutable object. So it can be changed. If you want a
    copy, use slice:
    >>> L = [1, 2, 3, 4, 5]
    >>> copy = L[:]
    >>> L.pop()

    5
    >>> L

    [1, 2, 3, 4]
    >>> copy

    [1, 2, 3, 4, 5]

    ....in your code...

    def main(self):
    recursive_func(self.words[:])
    print self.words

    ....or...

    >>> def recursive_func(words):
    >>> words = words[:]
    >>> if len(words) > 0:
    >>> word = words.pop()
    >>> print "Popped: %s" % word
    >>> recursive_func(words)
    >>> else:
    >>> print "Done"
    >>>
    >>> words = ["one", "two", "three"]
    >>> recursive_func(words)

    Popped: three
    Popped: two
    Popped: one
    Done
    >>> words

    ['one', 'two', 'three']

    Though I haven't been doing this long enough to know if that last
    example has any drawbacks.

    If we knew more about what you are trying to do, perhaps an
    alternative would be even better.

    - Rafe
     
    Rafe, Nov 21, 2008
    #5
  6. On Thu, 20 Nov 2008 18:31:12 -0500, J Kenneth King wrote:

    > Of course I expected that recursive_func() would receive a copy of
    > weird_obj.words but it appears to happily modify the object.


    I am curious why you thought that. What made you think Python should/did
    make a copy of weird_obj.words when you pass it to a function?

    This is a serious question, I'm not trying to trap you into something :)


    --
    Steven
     
    Steven D'Aprano, Nov 21, 2008
    #6
  7. J Kenneth King <> writes:

    > I recently encountered some interesting behaviour that looks like a bug
    > to me, but I can't find the appropriate reference to any specifications
    > to clarify whether it is a bug.
    >
    > Here's the example code to demonstrate the issue:
    >
    > class SomeObject(object):
    >
    > def __init__(self):
    > self.words = ['one', 'two', 'three', 'four', 'five']
    >
    > def main(self):
    > recursive_func(self.words)
    > print self.words
    >
    > def recursive_func(words):
    > if len(words) > 0:
    > word = words.pop()
    > print "Popped: %s" % word
    > recursive_func(words)
    > else:
    > print "Done"
    >
    > if __name__ == '__main__':
    > weird_obj = SomeObject()
    > weird_obj.main()
    >
    >
    > The output is:
    >
    > Popped: five
    > Popped: four
    > Popped: three
    > Popped: two
    > Popped: one
    > Done
    > []
    >
    > Of course I expected that recursive_func() would receive a copy of
    > weird_obj.words but it appears to happily modify the object.
    >
    > Of course a work around is to explicitly create a copy of the object
    > property befor passing it to recursive_func, but if it's used more than
    > once inside various parts of the class that could get messy.
    >
    > Any thoughts? Am I crazy and this is supposed to be the way python works?


    That's because Python isn't call-by-value. Or it is according to some,
    it's just that the values it passes are references. Which, according to
    others, is unnecessarily convoluted: it's call-by-object, or shall we
    call it call-by-sharing? At least everybody agrees it's not
    call-by-reference or call-by-name.

    There. I hope this helps!

    --
    Arnaud
     
    Arnaud Delobelle, Nov 21, 2008
    #7
  8. alex23 <> writes:

    > On Nov 21, 9:40 am, J Kenneth King <> wrote:
    >> Of course, providing a shallow (or deep as necessary) copy makes it
    >> work, I'm curious as to why the value passed as a parameter to a
    >> function outside the class is passed a reference rather than a copy.

    >
    > You're passing neither a reference nor a copy, you're passing the
    > object (in this case a list) directly:
    >
    > http://effbot.org/zone/call-by-object.htm


    Ah, thanks -- that's precisely what I was looking for.

    I knew it couldn't be a mistake; I just couldn't find the documentation
    on the behaviour since I didn't know what it was called in the python
    world.

    Cheers.
     
    J Kenneth King, Nov 21, 2008
    #8
  9. Steven D'Aprano <> writes:

    > On Thu, 20 Nov 2008 18:31:12 -0500, J Kenneth King wrote:
    >
    >> Of course I expected that recursive_func() would receive a copy of
    >> weird_obj.words but it appears to happily modify the object.

    >
    > I am curious why you thought that. What made you think Python should/did
    > make a copy of weird_obj.words when you pass it to a function?
    >
    > This is a serious question, I'm not trying to trap you into something :)


    Don't worry, I don't feel "trapped" in usenet. ;)

    It was more of an intuitive expectation than a suggestion that Python
    got something wrong.

    I was working on a program of some complexity recently and quickly
    caught the issue in my tests. I knew what was going on and fixed it
    expediently, but the behaviour confused me and I couldn't find any
    technical documentation on it so I figured I just didn't know what it
    was referred to in Python. Hence the post. :)

    I suppose I have some functional sensibilities and assumed that an
    object wouldn't let a non-member modify its properties even if they were
    mutable.

    Of course if there is any further reading on the subject, I'd appreciate
    some links.

    Cheers.
     
    J Kenneth King, Nov 21, 2008
    #9
  10. On Fri, 21 Nov 2008 10:12:08 -0500, J Kenneth King wrote:
    > Steven D'Aprano <> writes:
    >>
    >> I am curious why you thought that. What made you think Python should/did
    >> make a copy of weird_obj.words when you pass it to a function?

    [snip]
    > Of course if there is any further reading on the subject, I'd appreciate
    > some links.


    As one relatively new Python fan to another, I recommend following
    this newsgroup. Many important aspects of Python that several books
    failed to drive through my skull are very clearly (and repeatedly)
    explained here. Hang around for a week, paying attention to posts
    with subjects like "Error in Python subscripts" (made-up example),
    and curse me if you don't find it greatly rewarding.

    --
    To email me, substitute nowhere->spamcop, invalid->net.
     
    Peter Pearson, Nov 21, 2008
    #10
  11. Peter Pearson <> writes:

    > On Fri, 21 Nov 2008 10:12:08 -0500, J Kenneth King wrote:
    >> Steven D'Aprano <> writes:
    >>>
    >>> I am curious why you thought that. What made you think Python should/did
    >>> make a copy of weird_obj.words when you pass it to a function?

    > [snip]
    >> Of course if there is any further reading on the subject, I'd appreciate
    >> some links.

    >
    > As one relatively new Python fan to another, I recommend following
    > this newsgroup. Many important aspects of Python that several books
    > failed to drive through my skull are very clearly (and repeatedly)
    > explained here. Hang around for a week, paying attention to posts
    > with subjects like "Error in Python subscripts" (made-up example),
    > and curse me if you don't find it greatly rewarding.


    I do lurk more often than I post and sometimes I help out people new to
    Python or new to programming in general. I know how helpful usenet can
    be and usually this group in particular is quite special. It's good
    advice to read before you post; quite often the question has been
    proposed and answered long before it came to your little head (not you
    in particular; just general "you").

    In this case, I was simply lacking the terminology to find what I was
    looking for on the subject. In such cases turning to the community seems
    like a fairly reasonable way to find clarification. I've only been
    programming in Python specifically for two years or so now, so I hope I
    can be forgiven.

    Cheers.
     
    J Kenneth King, Nov 21, 2008
    #11
  12. J Kenneth King

    Terry Reedy Guest

    J Kenneth King wrote:

    > I was working on a program of some complexity recently and quickly
    > caught the issue in my tests. I knew what was going on and fixed it
    > expediently, but the behaviour confused me and I couldn't find any
    > technical documentation on it so I figured I just didn't know what it
    > was referred to in Python. Hence the post. :)


    Language Reference / Expressions / Primaries / Calls +
    Language Reference / Compound statements / Function definitions

    Hmm. Read by themselves, these are not as clear as they could be that
    what parameters get bound to are the argument objects. One really needs
    to have read the section on assignment statements first.
     
    Terry Reedy, Nov 21, 2008
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Paul Opal
    Replies:
    12
    Views:
    961
    Paul Opal
    Oct 11, 2004
  2. Navkirat Singh

    Function parameter scope

    Navkirat Singh, Jul 29, 2010, in forum: Python
    Replies:
    1
    Views:
    255
    Steven D'Aprano
    Jul 29, 2010
  3. Mister B
    Replies:
    8
    Views:
    554
    Nick Keighley
    Aug 26, 2010
  4. AzamSharp
    Replies:
    2
    Views:
    181
  5. Andrew Falanga
    Replies:
    2
    Views:
    202
    Andrew Falanga
    Nov 22, 2008
Loading...

Share This Page