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