Generic constructors and duplication of internal Python logic

Sebastien de Menten sdementen at hotmail.com
Wed Apr 28 06:17:48 EDT 2004


Here is a metaclass for uber-lazy user :-)

Concretely, at the creation of the class it takes the source of the
__init__ function and add, at the first line of __init__, the line
that sets the attributes :
>         self.foo, self.bar, self.baz, self.optional1, self.optional2 = 
>                    foo, bar, baz, optional1, optional2


Nothing is done for *args or **kwargs but they could also be assigned
to some attributes of the class (like self.args and self.kwargs ?).
Moreover, for really lazy users, the line for calling the __init__ of
the father of the class (if necessary)
>         super(Father, self).__init__()
could also be dynamically added



#----------------------------------------------------
import inspect,re

class autoArgInit(type):
    """
    Replace any occurence of xxx in the class by a col version and a
row version and yyy by its complementary row/col
    """
    def __new__(cls,classname,bases,classdict):
        # get the __init__ function
        function_object = classdict["__init__"]
        
        # get the source of the function
        function_source = inspect.getsource(function_object)

        # detect indentation of the function definition and remove it
such that def __init__ is at beginning of line
        indentation_level = re.match("( *)def
(.*)\(",function_source).groups()[0]
        function_source =
re.sub("^"+indentation_level,"",function_source)
        function_source =
re.sub("\n"+indentation_level,"\n",function_source)

        # split the lines to add new lines easely
        function_lines = function_source.split("\n")

        # detect indentation inside the function
        indentation =
re.match("(\s*)\S*",function_lines[1]).groups()[0]

        # take argument list without self
        args = inspect.getargspec(function_object)[0][1:]

        # create the line for assignment
        assign_code = indentation + ", ".join(map(lambda s:
"self.%s"%(s),args)) + " = " + ", ".join(args)

        # insert it in the code
        function_lines.insert(1,assign_code)
        
        # join the code again
        new_function_source = "\n".join(function_lines)

        # evaluate it and replace the __init__ definition in classdict
        exec new_function_source in function_object.func_globals,
classdict

        return type.__new__(cls,classname,bases,classdict)

class test(object):
    __metaclass__ = autoArgInit
    def __init__(self, baz, top, foo=3, r = 5, *args, **kwargs):
        assert self.baz == baz
        assert self.top == top
        assert self.foo == foo
        assert self.r == r
        
test(3,4,6)
#----------------------------------------------------


Seb






jjl at pobox.com (John J. Lee) wrote in message news:<87ekqq41ya.fsf at pobox.com>...
> This is one of those things that I can't quite believe I've never
> needed to do before.
> 
> I've got a set classes, each of which has a set of attributes that all
> behave very similarly.  So, I have a class attribute (Blah.attr_spec
> below), which is used by a mixin class to implement various methods
> that would otherwise be highly repetitious across these classes.  I'd
> like to do the same for the constructor, to avoid this kind of
> nonsense:
> 
> class Blah(NamesMixin):
>     attr_spec = ["foo", "bar", "baz",
>                  ("optional1", None), ("optional2", None)]
>     def __init__(self, foo, bar, baz,
>                  optional1=None, optional2=None):
>         self.foo, self.bar, self.baz = \
>                    foo, bar, baz
>         self.optional1, self.optional2 = \
>                         optional1, optional2
> 
> So, I wrote a mixin class whose __init__ looks at the attr_spec
> attribute, and uses args and kwds (below) to assign attributes in the
> same sort of way as the special-case code above:
> 
> class ArgsMixin:
>     def __init__(self, *args, **kwds):
>         # set attributes based on arguments passed in, as done
>         # manually in Blah.__init__, above
>         ... lots of logic already present in Python goes here...
> 
> That immediately leads to duplication of Python's internal logic: I
> have to check things like:
> 
>  -are there too many positional arguments?
>  -any unexpected keyword arguments?
>  -multiple keyword arguments?
>  -any duplication between positional and keyword arguments?
> 
> etc.
> 
> Surely there's some easy way of making use of Python's internal logic
> here?  For some reason, I can't see how.  Can anybody see a way?
> 
> 
> John



More information about the Python-list mailing list