User login

Spotting and fixing problems caused by referencing the same object in memory

Here's a slightly simplified version of the code causing the problem— very slightly simplified; it really was about this short:

    def fields(self, key, items):
        for (name, cls) in items.items():
            for field in dir(cls):
                value = getattr(cls, field)
                if isinstance(value, Field):
                    new_name = '__'.join([key, name, field])
                    value.widget.attrs = {
                      'data-target-id': name,
                    }
                    yield (new_name, value)

It only took me hours to see the problem once isolated to this bit of code. See it?

When modifying the attribute pointed to by the variable value, we were modifying the attribute on the class itself. So if two items had the same class, when we added the data-target-id the second time (different name, same class), we were overwriting our previous data-target-id. This fixed it:

    def fields(self, key, items):
        for (name, cls) in items.items():
            for field in dir(cls):
                raw_value = getattr(cls, field)
                if isinstance(raw_value, Field):
                    value = deepcopy(raw_value)
                    new_name = '__'.join([key, name, field])
                    value.widget.attrs = {
                      'data-target-id': name,
                    }
                    yield (new_name, value)

Remember, it's not that the variable name ('value' in this example) was reused. That's being completely replaced each loop. If this hadn't been in a loop, say, and two variable names were used, we would still have the same problem: the new variable name would still be referencing the exact same object in memory.

In this case widget attributes on the copied attribute was itself an object, so copy wasn't enough, and we had to use deepcopy.

Many languages treat objects the same way (a variable is a reference to the object in memory), but in python everything is an object— albeit some are mutable and some, like strings, are immutable. For this reason in Python what is happening goes by a slightly different name, call by object.

When dealing with a list, instead of copy() you can just use list().

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.