Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Unexpected behavior using contextmanager on a class method

Reply
Thread Tools

Unexpected behavior using contextmanager on a class method

 
 
Thomas Draper
Guest
Posts: n/a
 
      08-07-2012
I want to use with..as in a "reversible circuit generator". However, it seems that @contextmanager changes the expected nature of the class. I tried to distill the problem down to a simple example.

import contextlib

class SymList:
def __init__(self, L=[]):
self.L = L

@contextlib.contextmanager
def SymAdd(self, a):
self.L.append(a)
yield
self.L.append(a)

SL = SymList()
with SL.SymAdd(3):
SL.L.append(5)
print(SL.L) # Expect and see [3, 5, 3]
SL2 = SymList() # New object. Should have default values.
print(SL2.L) # Expect [] and see [3, 5, 3]

Why is the data member SL2.L referring to the data member SL.L? Has the @contextmanager somehow made all instantions of the class related?



 
Reply With Quote
 
 
 
 
Peter Otten
Guest
Posts: n/a
 
      08-07-2012
Thomas Draper wrote:

> I want to use with..as in a "reversible circuit generator". However, it
> seems that @contextmanager changes the expected nature of the class. I
> tried to distill the problem down to a simple example.
>
> import contextlib
>
> class SymList:


The problem you experience has nothing to do with context managers, you have
a mutable default argument in your __init__().

> def __init__(self, L=[]):


L is initialised with an empty list exactly once, when the method is
defined; any changes you make to the list will be seen by all instances that
use the default. The fix is

def __init__(self, L=None):
if L is None:
L = []

> self.L = L
>
> @contextlib.contextmanager
> def SymAdd(self, a):
> self.L.append(a)
> yield
> self.L.append(a)
>
> SL = SymList()
> with SL.SymAdd(3):
> SL.L.append(5)
> print(SL.L) # Expect and see [3, 5, 3]
> SL2 = SymList() # New object. Should have default values.
> print(SL2.L) # Expect [] and see [3, 5, 3]
>
> Why is the data member SL2.L referring to the data member SL.L? Has the
> @contextmanager somehow made all instantions of the class related?



 
Reply With Quote
 
 
 
 
Steven D'Aprano
Guest
Posts: n/a
 
      08-07-2012
On Tue, 07 Aug 2012 08:30:15 -0700, Thomas Draper wrote:

> I want to use with..as in a "reversible circuit generator". However, it
> seems that @contextmanager changes the expected nature of the class. I
> tried to distill the problem down to a simple example.


Nothing to do with contextmanager. That's a red-herring. Your error is
here:

class SymList:
def __init__(self, L=[]):
self.L = L

The default value for L is only set *once*, when the function is defined,
NOT every time the function is called. Later on, in the SymAdd method you
modify that list in place. So naturally later instances see the changes,
because you have changed the default list.

You can see this "early binding" of the default value in action with this
simple example:


import time
def test(x=time.ctime()): # Default values are set *once*, not each time.
print(x)

test()
=> always prints Wed Aug 8 03:40:32 2012

(or whatever time the function is defined).

In this example, the default value is a string, and cannot be changed;
but in your code it is a list, and can be modified in place. Either way,
the result is the same: you get the same object used as the default, each
and every time.


In your case, you can fix this problem and get the effect of "late
binding" like this:

class SymList:
def __init__(self, L=None):
if L is None: L = []
self.L = L


Now each time the method body runs, you get a different empty list.



--
Steven
 
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
A better contextlib.contextmanager Michele Simionato Python 0 05-23-2012 06:21 AM
contextlib.contextmanager and try/finally johannh@gmail.com Python 9 02-01-2012 02:15 AM
ContextDecorator via contextmanager: broken? Ian Kelly Python 0 06-10-2011 10:57 PM
Unexpected __metaclass__ method behavior anne.nospam01@wangnick.de Python 5 01-06-2008 06:44 PM
Unexpected behavior when initializing class alfred.fazio@gmail.com Python 7 11-28-2007 05:13 PM



Advertisments