3

I found myself writing the following code:

def dlt(translation):
    del translation.strands[translation.active][translation.locus]

I would rather prefer something like:

def dlt(translation):
    *something*(translation):
        del strands[active][locus]

Is there a way to achieve this?

2 Answers 2

4

Namespaces are just Python objects, and you can assign objects (including the result of attribute lookups) to local variable names:

strands = translation.strands
active = translation.active
locus = translation.locus

Alternatively, you'd have to hack together a context manager that modifies locals(), as shown in this answer.

Something like this would do that:

import inspect

class Namespace(object):
    def __init__(self, namespaced):
        self.namespaced = namespaced

    def __enter__(self):
        """store the pre-contextmanager scope"""
        ns = globals()
        namespaced = self.namespaced.__dict__
        # keep track of what we add and what we replace
        self.scope_added = namespaced.keys()
        self.scope_before = {k: v for k, v in ns.items() if k in self.scope_added}
        globals().update(namespaced)
        return self

    def __exit__(self):
        ns = globals()
        # remove what we added, then reinstate what we replaced
        for name in self.scope_added:
            if name in ns:
                del ns[name]
        ns.update(self.scope_before)

then use it like this:

with Namespace(translation):
     del strands[active][locus]

where all items in the translation.__dict__ are made available globally while in the while block.

Note that this is not thread-safe, and has the potential to create lots of confusion for anyone trying to read code that uses this in future, including yourself. Personally, I wouldn't use this.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks. I'm actually after a solution which doesn't require me to hand-write all assignments, so I'll probably try the hack you suggested.
@Constantius: I've updated my answer with a version of that hack that should work for you. It's a hack though.
The context manager is a cute hack! I think in __exit__ you should iterate over ns, not the object namespace, because the latter may have changed in the meantime. Doing that safely takes some care, obviously.
@JameySharp: The globals() namespace could have changed as well; I've updated the cleanup to take into account new names could have been added to the namespaced object too.
You're right, both could have changed. Um... I'm more concerned about names deleted from the namespace object. I guess you need to save a copy of the namespace key-set on entry, then on exit, delete all keys in that set, then update globals from the saved globals. In case the same name is in both, that order will restore the original value.
|
2

You should probably use Martijn's answer. But if you really want to do what you asked for, I think this (untested) snippet will do it:

exec "del strands...", translation.__dict__

If you don't like that: Good, you have taste. :-)

Here's another option:

def within(obj, func):
    return func(**obj.__dict__)

Call it like this:

def dostuff(strands, active, locus, **ignored):
    del ...
within(translation, dostuff)

2 Comments

The thing I though about after asking the question. I like it, although: does it have any pitfalls? Touching __dict__ feels like it may cause some trouble.
The main pitfall is you'll get fields you didn't know your objects had. Besides the fields, you'll get passed the methods and various other stuff that's normally invisible. That's why the sample dostuff function has to accept and ignore other keyword arguments.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.