Unexpected behavior using contextmanager on a class method

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Aug 7 13:45:26 EDT 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



More information about the Python-list mailing list