run-time construction of methods

Discussion in 'Python' started by Carlo v. Dango, Apr 6, 2004.

  1. Hello all.

    I am in the need of wrapping certain objects at run-time. My initial
    approach was:

    import inspect, new
    def make_wrapper(obj, methodName):
    cls = obj.__class__
    wrapmth = getattr(obj, methodName)
    print "************ ", methodName
    def wrapper(self, *args, **kwargs):
    print "**before ", methodName
    return wrapmth(*args, **kwargs)
    return wrapper

    class Person(object):
    def __init__(self, age):
    super(Person, self).__init__()
    self.age = age

    def getAge(self):
    return self.age

    def setAge(self, newage):
    """sets the age of the person"""
    self.age = newage

    p = Person(33)
    setattr(p, "setAge", new.instancemethod(make_wrapper(p,"setAge"), p,
    p.__class__))
    p.setAge(22)
    print "age is ", p.getAge()



    However, reflectional information is gone, such as the __doc__ string, and
    in an interactive environment, when typing a method call of setAge() the
    arguments are specified as "(..., ***)" rather than "(newage)"

    I have thus attempted to replace the make_wrapper function with a version
    where the inner function "wrapper" is a string which gets translated into
    a function with an identical signature as the method to wrap.. my closest
    attempt to a real solution is


    def make_wrapper(obj, methodName):
    cls = obj.__class__
    wrapmth = getattr(obj, methodName)
    print "************ ", methodName
    wrapperstr = """def wrapper(self, *args, **kwargs):
    print "**before ", methodName
    return wrapmth(*args, **kwargs)"""
    exec(wrapperstr, globals(), locals())
    return wrapper

    but I get the error

    NameError: global name 'methodName' is not defined

    which I don't know how to deal with... inspecting the locals() a
    'methodName' is defined.. Note that my above 'solution' is far from the
    product I want, I just figured I needed to get this to work, before
    fidling with the signature stuff...


    does anyone have an idea on how to approach this?


    -Carlo
    --
    Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
    Carlo v. Dango, Apr 6, 2004
    #1
    1. Advertising

  2. "Carlo v. Dango" <> writes:

    > Hello all.
    >
    > I am in the need of wrapping certain objects at run-time. My initial
    > approach was:
    >
    > import inspect, new
    > def make_wrapper(obj, methodName):
    > cls = obj.__class__
    > wrapmth = getattr(obj, methodName)
    > print "************ ", methodName
    > def wrapper(self, *args, **kwargs):
    > print "**before ", methodName
    > return wrapmth(*args, **kwargs)
    > return wrapper
    >
    > class Person(object):
    > def __init__(self, age):
    > super(Person, self).__init__()
    > self.age = age
    >
    > def getAge(self):
    > return self.age
    >
    > def setAge(self, newage):
    > """sets the age of the person"""
    > self.age = newage
    >
    > p = Person(33)
    > setattr(p, "setAge", new.instancemethod(make_wrapper(p,"setAge"), p,
    > p.__class__))
    > p.setAge(22)
    > print "age is ", p.getAge()
    >

    I like the approach of using a bound method to p to create a
    new instance specific method for p.

    >...
    > def make_wrapper(obj, methodName):
    > cls = obj.__class__
    > wrapmth = getattr(obj, methodName)
    > print "************ ", methodName
    > wrapperstr = """def wrapper(self, *args, **kwargs):
    > print "**before ", methodName
    > return wrapmth(*args, **kwargs)"""
    > exec(wrapperstr, globals(), locals())
    > return wrapper
    >
    > but I get the error
    >
    > NameError: global name 'methodName' is not defined
    >

    exec does not create a closure within the function calling it.
    That is its downside. So everything is either local to wrapmth,
    eg. parameters and variables created within the function, or
    global. Fortunately you can still create a closure with exec
    by declaring a function within a function. Here's my rewrite.
    I hope it works for you.

    def make_wrapper(obj, methodName):
    cls = obj.__class__
    wrapmth = getattr(obj, methodName)
    print "********** ", methodName
    wrapperstr = """\
    def _closure(_wrapmth):
    def wrapper(self, *args, **kwds):
    "I wrap method %(methodName)s"
    print "**before %(methodName)s"
    return _wrapmth(*args, **kwds)
    return wrapper
    _wrapper = _closure(wrapmth)
    """ % {'methodName': methodName} # Insert strings directly into code
    locs = {'wrapmth': wrapmth} # Keep namespace clean to avoid conflicts
    exec(wrapperstr, {}, locs)
    return locs['_wrapper'] # and here is the result

    See how the argument to _closure is the 'local' you want to keep
    around. Also I don't use globals() or locals() directly since
    they are cluttered. When you start trying to make the parameters
    of wrapper meaningful you will want to minimize the chance
    the parameter names conflict with other names within the scope
    of the exec. That is also why _closure and _wrapmth begin
    with underscores. You might want to choose even more cryptic names.
    Also, inlining strings such as methodName instead of passing them
    as variables into the exec scope reduces the chance of conflict.

    Lenard Lindstrom
    <>
    Lenard Lindstrom, Apr 7, 2004
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. flamesrock
    Replies:
    8
    Views:
    452
    Hendrik van Rooyen
    Nov 24, 2006
  2. Ook
    Replies:
    10
    Views:
    555
  3. Pierre Yves
    Replies:
    2
    Views:
    484
    Pierre Yves
    Jan 10, 2008
  4. Kenneth McDonald
    Replies:
    5
    Views:
    315
    Kenneth McDonald
    Sep 26, 2008
  5. Nephi Immortal

    Construction Time as Fluent Pattern

    Nephi Immortal, Feb 23, 2012, in forum: C++
    Replies:
    5
    Views:
    450
    Krice
    Feb 25, 2012
Loading...

Share This Page