Thursday, November 11, 2010

"Selfless Python"

Proof of concept of Python methods using no explicit declaration of the parameter Self.

I mean:


class A:
   __metaclass__ = Selfless
   def __init__(a , b):
      self.a = a
      self.b = b


Is made to work from this example.

One common critic to Python is the need of the explicit "self" parameter declaration on function Methods. No matter how this design is optimal in terms of internal consistency, keeping things explicit, and reducing the keyword count, people keep complaining about it.

Given the access granted to the inner workings of the Language, it is easy to arrange an alternate mechanism for class methods that could preclude the declaration of a first parameter to a method. Therefore, people who are seeing complaining could try some shots of "selfless" Python.

My approach here is just as follows: I create a metaclass that uses an alternate way to fetch function methods inside the classes. With the normal "type" metaclass - i.e. normal "new style" classes - any functions declared in a class body are substituted, at class creation time, for a descriptor. When the attribute with the function name in a class, or instance of it, is retrieved, this descriptor creates a method object that wraps the original function. (And yes, the method object is created at attribute access time). This method object, when called, prepends the "instance" object as the first parameter in the list, and forwards the call to the wrapped function.

In this "selfless" proof of concept, I change the functions in the class body for descriptor objects, but descriptors that behave differently: instead of wrapping the function in a method object, they recreate the function when the method is acessed.
A new function object is created with the same code object, the same name, the same closure, but, with a different global dictionary. The global dictionary of the original function is copied and changed to include an entry named "self" which points to the object instance. (Or "None" if the function is retrieved from the class, not from an instance).

Therefore, inside these selfless methods, the name "self" can be used without ever being declared: it will exist as a global vairable at execution time, and point to the instance. Just like the parameter self exists as a local variable and points to the instance in regular Python methods.

This should not be used in production code - it is a good proof of concept - but of course there should be lots of strange and inconsistent behavior associeated with it. One of which is the fact methods can not be used "unbounded" as one does with ordinary python methods, explicitly passing an instance object as the first parameter.


Asa final consideration, Pedro Werneck had an implementation of this "feature" before here: http://www.python.org.br/wiki/NoSelf - but unlike Werneck's example, I don't fiddle with the bytecode or anyother thing to "rebuild" the bytecode so that references to an unexisting local self variable are rewritten, and "self" is artificially inserted as a local variable at runtime. The work presented here is an order of magnitude simpler, and possibly less subject to colateral effetcs.

Nonetheless: I warn again - this is just a toy! Don't use this in "real life"!

Other than that, it works finner and in a simpler way than I expected.

# -*- coding: utf-8 -*-
from types import FunctionType

"""
Changes classes so that each method has its instances changed so
that "self" is a global variable pointing to the instance. 
"""

class SelflessDescriptor(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, class_):
        new_globals =  self.func.func_globals.copy()
        new_globals["self"] = instance
        new_func = FunctionType(self.func.func_code,
             new_globals, self.func.func_name,
             self.func.func_defaults,
             self.func.func_closure)
        return new_func

class Selfless(type):
    def __new__(cls, name, bases, dict_):
        for key, val in dict_.items():
            if isinstance(val, FunctionType):
                dict_[key] = SelflessDescriptor(val)
        return type(name, bases, dict_)


if __name__ == "__main__":
    __metaclass__ = Selfless

    class A:
        def __init__(a, b):
            self.a = a
            self.b = b
    
    a = A("Hello", "World!")
    print a.a, a.b

3 comments:

  1. Now you just need to extend it to act like Java does put all variables in the class into the scope, not just self - they'll really like that. ;-)

    class A:
        a = None
        b = None
        def __init__(a_, b_):
            a = a_
            b = b_

        def get_a():
            return a

    (Yuck. Having to use non-breaking spaces for spacing is definitely not a nice way to have it.)

    ReplyDelete
  2. In follow-up on that, I've got it to work, provided you don't do any assignment. Due to the required use of globals rather than locals, it can't work with locals.

    Here's the code to laugh at (won't work directly with copy and paste as I had to use nbsp - Ctrl-V 160 - to get spacing to display right).


    # -*- coding: utf-8 -*-
    from types import FunctionType

    """
    Changes classes so that each method has its instances changed so
    that "self" is a global variable pointing to the instance.
    """

    class SelflessDescriptor(object):
        def __init__(self, func):
            self.func = func
        def __get__(self, instance, class_):
            if hasattr(self, 'getting'): return self.func
            self.getting = True
            new_globals = self.func.func_globals.copy()
            new_globals["self"] = instance
            new_globals.update((i, object.__getattribute__(instance, i)) for i in dir(instance))
            new_func = FunctionType(self.func.func_code,
                 new_globals, self.func.func_name,
                 self.func.func_defaults,
                 self.func.func_closure)
            del self.getting
            return new_func

    class Selfless(type):
        def __new__(cls, name, bases, dict_):
            for key, val in dict_.items():
                if isinstance(val, FunctionType):
                    dict_[key] = SelflessDescriptor(val)
            return type(name, bases, dict_)


    if __name__ == "__main__":
        __metaclass__ = Selfless

        class A:
            def __init__(a_):
                # Unfortunately, a = [a_] would not work.
                self.a = [a_]

            def add_word(s):
                # a += [s] would not work
                a.extend([s])
        
        a = A('Hello')
        a.add_word('World!')
        print ' '.join(a.a)

    Patches welcome ;-)

    ReplyDelete
  3. Hi Chris - yes, a ++ fan, friend of mine told me that he'd like this "complete selfless" approach. After all, having "self." is so much herder to read than having those -> and "*" and "Virtual Void" all over the place. :-)

    I've been thinking on it - it could be done by declaring the variables as global inside the methods, and turning the global dictionary that is created to the method into a specialized object that would commit its changes back to the instance.

    ut I am bit afraid of doing that now that friend said he'd actually _use_ such a thing. :-)

    ReplyDelete