Jono
Jono

Reputation: 3633

Effective closures in python

With Python 2.7x I'm attempting to create a Map object which can reference itself with a 'This' or 'self'. In Javascript this would be roughly,

myObj = function(){ 
 obj = {}; 
 this = obj; 
 obj = { 'a':'b', 'b':this.a };
 return obj;
}()

But in Python you can't do multi-line lambda expressions. Scoping also doesn't behaviour the same as I expect. I can create a function on a separate line then call it, but this seems to lack pizzaz (especially since it isn't limited to being called only once). Is there an effective way to do this in Python?

EDIT: Some people have been asking OH MY GOD WHY???? Well first of all, as an exercise. Second, you are failing to understand what I'm trying to do - I'm attempting to emulate a CLASS with a MAP. In a class in python you would say, var otherfunc = self.predefinedFunction I want to be able to use self (this in some other languages), to reference the object. So in python I want to turn this:

my_obj = { 'sqr':lambda x: x*x, 'quad': my_obj['sqr']}

into this:

my_obj = { 'sqr':lambda x: x*x, 'quad': this['sqr']}

Upvotes: 0

Views: 112

Answers (3)

matsjoyce
matsjoyce

Reputation: 5844

With the following boilerplate code:

class StoringOperations:
    for specfunc in ["getattr", "getitem", "call", "len"]:  # Extend as needed
        name = "__" + specfunc + "__"
        f = lambda self, *args, __name=name, **kwargs: StoredOperation(self, __name, args, kwargs)
        f.__name__ = name
        locals()[name] = f


class StoredOperation(StoringOperations):
    def __init__(self, s, methname, args, kwargs):
        self.__args = args
        self.__kwargs = kwargs
        self.__methname = methname
        self.__s = s
        super().__init__()


def gethiddenname(thing, name):
    return getattr(thing, "_StoredOperation" + name)


def consolidate(thing, this):
    if isinstance(thing, StoredOperation):
        s = consolidate(gethiddenname(thing, "__s"), this)
        methname = gethiddenname(thing, "__methname")
        try:
            meth = getattr(s, methname)
        except AttributeError:
            if methname == "__getattr__":
                meth = lambda *args, **kwargs: getattr(s, *args, **kwargs)
            else:
                raise
        return meth(*gethiddenname(thing, "__args"), **gethiddenname(thing, "__kwargs"))
    elif isinstance(thing, StoringOperations):
        return this
    else:
        return thing


class SelfConsciousMap(dict):
    def __init__(self, **kwargs):
        super().__init__()
        self.kwargs = kwargs
        for key in kwargs:
            self.consolidation(key)

    def consolidation(self, key):
        self[key] = consolidate(self.kwargs[key], self)

    def __getitem__(self, key):
        if key in self.kwargs and key not in self:
            self.consolidation(key)
        return super().__getitem__(key)


this = StoringOperations()

You can do this:

d = SelfConsciousMap(a=[1,2,3], b=this["a"], c=this["b"].copy(), d=this["a"].__len__())
print(d)

And you will get:

{'a': [1, 2, 3], 'c': [1, 2, 3], 'b': [1, 2, 3], 'd': 3}

Might not be useful in real code, but it shows that with enough classes, anything is possible...

Upvotes: 0

Dunes
Dunes

Reputation: 40703

Given your example in your answer here, why don't you just write a class? The following is functionally equivalent to your code example.

class myobj(object):
    def __init__(self):
        self.count = 0
    @staticmethod
    def addOne(n):
        return n + 1
    def addOneTimesTwo(self, n):
        return self.addOne(n) * 2
    def __getitem__(self, attrname):
        "Allows attributes to accessed as if they were key/value pairs in a map."
        return getattr(self, attrname)

myObj = myobj()
assert myObj["addOneTimesTwo"](1) == 4

If you really have your heart set on trying to emulate prototyping then you could try the following. It won't work with inheritance though. You'll need to add a few more bits and pieces to make that work.

class Prototype(object):    
    def __init__(self, **attrs):
        self.__dict__.update(attrs)

def myobj():
    count = 0
    def addOne(n):
        return n + 1
    def addOneTimesTwo(n):
        """This is like a 'bound' method. The self in this context will refer to the same
        Prototype even if this function is transferred to a different Prototype."""
        return self.addOne(n) * 2
    self = Prototype(**locals())
    return self

myObj = myobj()
assert myObj.addOneTimesTwo(1) == 4

Upvotes: 1

dfb
dfb

Reputation: 13289

If you're trying to implement a closure with a map, this would work fine

mymap = {a:1,b:"foo"} # all of your previously initialized and constant data
mymap["self"] = mymap

Then you can call

mymap["self"]["b"]

Here's how you can write code using this

mymap["func"] = lambda x: return x*mymap["self"]["x"]

This is admitted ugly, but you can't you have no way to refer to the map as anything but a global variable within a lambda expression. In particular, there's no good way to self reference. A better approach is to use an object, not a map.

Upvotes: 2

Related Questions