dynamic method addition, problem with arg=arg in lambdas (was: 
Author Message
 dynamic method addition, problem with arg=arg in lambdas (was:

Quote:
> [david ascher]
> ....
> ... Tim says it's [defining class methods outside a class] considered
> poor style but is doable, and gives the example code:
> ... [example code deleted, lest it spread <wink>] ...
> To which Thomas answered that he would want syntactic sugar along
> the lines of:

> def C.new_method(x): ...

> I started my post thinking that one didn't need syntactic sugar,
> and that the following would work fine:

>    >>> def add_method(theclass, method):
>    ...    "Add a method to an existing class instance"
>    ...    setattr(theclass, method.__name__, method)

Best I can tell, it *does* work fine -- no?  I'd add a way to rename the
method, though.

Quote:
> Then I thought that I could add that one could also add functions to
> specific instances:

> In Python, "a method" of an instance of a user-defined class is, by
> decree, an attribute of the class whose value is a function object.

no word therein was superfluous, and in particular not the "attribute of the
class" clause.  The language wasn't designed to support instance-specific
methods.  That may be why you're finding them clumsy to add <snort>.

- Show quoted text -

Quote:
> ... [various failing ways to try to trick an instance into appearing to
> ...  support an instance-specific method]

> def add_method_always(object, func):
>    "make a function a class method or an instance method, as needed"

>    thetype = type(object)
>    if thetype == InstanceType:
>    f = lambda object=object, func=func, \
>                     *args: apply(func, (object,) + args)
>    setattr(object, func.__name__, f)

>   elif thetype == ClassType:
>         setattr(object, func.__name__, func)

>   else:
>    raise AttributeError, "Can't add methods to objects" + \
>              "of type %s" % (thetype,)

> which is only somewhat of a hack.

> But this fails because if I have an argument to the function
> it gets assigned to the object argument, and doesn't get
> "put" into the args tuple.  Similarly for the func=func argument.
> ...
> This is the first place I've found where it seems that the use of
> lambdas with the default keyword hack as a way to get around
> the lack of closures (or whatever the term is) is a real problem.
> I may very well be missing an elegant solution to this problem,
> though.  Any suggestions?

Two replies come to mind:  (1) This is "a real problem" only if one considers
the lack of instance-specific methods to be a real problem.  (2) "lambda" is
syntactic sugar for a relelentlessly simple case of function defn that comes
up often; lambda is never necessary; that lambda can't be used for everything
shouldn't be a surprise.  Closures of any desired complexity can always be
built out of classes, and in non-trivial cases are best done that way.

In this case, e.g., you could build the desired closure explicitly in a helper
class:

class InstanceMethodHack:
    def __init__(self, object, func):
        self.object, self.func = object, func
    def doit( self, *args ):
        return apply( self.func, (self.object,) + args )

and change the instance branch of add_method_always to

    if thetype == InstanceType:
        setattr(object, func.__name__,
                InstanceMethodHack( object, func ).doit )

This should work fine for any number of args.  E.g., then

class C: pass
c = C()
c.attr = "somestring"
def meth( self, *args ):
    return "attr = %s, method called with %d args: %s" % \
           (self.attr, len(args), args)
add_method_always( c, meth )
print c.meth()
print c.meth(42)
print c.meth('I am not the Hubble constant', 42)
d = C()  # show that meth is c-specific
print d.meth(3,4)

prints:

attr = somestring, method called with 0 args: ()
attr = somestring, method called with 1 args: (42,)
attr = somestring, method called with 2 args: ('I am not the Hubble constant',
42)
Traceback (innermost last):
  File "misc/test.py", line 31, in ?
    print d.meth(3,4)
AttributeError: meth

but-i'd-hate-to-have-to-maintain-code-that-uses-it-<0.5-wink>-ly y'rs  - tim


not speaking for Dragon Systems Inc.



Mon, 17 May 1999 03:00:00 GMT  
 dynamic method addition, problem with arg=arg in lambdas (was:

Quote:
> [piet]
> ...
> This makes it almost the same as Tim Peters' code. Except that he
> puts the __call__ method in the receiving object rather than the
> Closure instance, which makes it a bit faster, I presume.

Nothing so deliberate, Piet!  It's simply that "__call__" got invented shortly
before I vanished from the Net for a couple years, and so I keep forgetting
__call__ exists -- but the few times I have remembered it, it has indeed been
slower than the older alternatives <wink>.

new-ideas-give-me-headaches-ly y'rs  - tim


not speaking for Dragon Systems Inc.



Mon, 17 May 1999 03:00:00 GMT  
 dynamic method addition, problem with arg=arg in lambdas (was:

Quote:
> [piet van oostrum]
> ...
> The strange thing is that the reference manual can be read as
> to imply that these [instance attributes that are function objects]
> should be methods:

> page 14:

> User-defined method objects are created in two ways: when getting
> an attribute of a class that is a user-defined function object, or when
> getting an attributes [sic!] of a class instance that is a user-defined
> function object.

It looks like the docs got disimproved <ahem> between 1.3 and 1.4 in this
respect.  The text you quoted above doesn't appear in 1.3, and I agree it's
misleading (although the example following the quote is accurate); all it's
really trying to do is explain the difference between "bound" and "unbound"
method objects.

It's worse because the later paragraph describing "class instances" also
regressed a bit in the same respect:

Quote:
> [1.3 version]
> A class instance is created by calling a class object as a function.
> A class instance has a dictionary in which attribute references are
> searched. When an attribute is not found there, and the instance's
> class has an attribute by that name, and that class attribute is a
> user-defined function (and in no other cases), the instance attribute
> reference yields a user-defined method object (see above) constructed
> from the instance and the function.            

That at least made it clear that instance methods *always* come from a class
attribute.  1.4 explains more but I think also introduced an error:

Quote:
> [1.4 version]
> A class instance is created by calling a class object as a function
> (see above). A class instance has a name space implemented as
> a dictionary, which is the first place where instance attributes are
> searched. When an attribute is not found there, the search continues
> with the class attributes. If a class attribute is found that is a
> user-defined function object (and in no other case), it is transformed
> into an unbound user-defined method object (see above). The im_class
> attribute of this method object is the class in which the function object
> was found, not necessarily the class of the instance for which the
> attribute reference was initiated. If no class attribute is found, and
> the object's class has a __getattr__ method, that is called to satisfy
> the lookup.

I.e., this describes the way a bound (not, as it says, unbound) user-defined
method object comes into being, & it should probably also mention that that
object's im_self attribute is bound to the class instance that initiated the
dance.

In any case, this stuff has always been more clearly (if less completely)
described in the Tutorial (section "Method objects" of the "Classes" chapter).

Quote:
> ...
> So I guess the ref manual should be updated.

or-maybe-downdated<wink>-ly y'rs  - tim


not speaking for Dragon Systems Inc.



Mon, 17 May 1999 03:00:00 GMT  
 dynamic method addition, problem with arg=arg in lambdas (was:

Quote:
> > In Python, "a method" of an instance of a user-defined class is, by
> > decree, an attribute of the class whose value is a function object.

> no word therein was superfluous, and in particular not the "attribute of the
> class" clause.  The language wasn't designed to support instance-specific
> methods.  That may be why you're finding them clumsy to add <snort>.

Granted.

Quote:
> > This is the first place I've found where it seems that the use of
> > lambdas with the default keyword hack as a way to get around
> > the lack of closures (or whatever the term is) is a real problem.
> > I may very well be missing an elegant solution to this problem,
> > though.  Any suggestions?

> Two replies come to mind:  (1) This is "a real problem" only if one considers
> the lack of instance-specific methods to be a real problem.  (2) "lambda" is
> syntactic sugar for a relelentlessly simple case of function defn that comes
> up often; lambda is never necessary; that lambda can't be used for everything
> shouldn't be a surprise.  Closures of any desired complexity can always be
> built out of classes, and in non-trivial cases are best done that way.

Two things:

1) I never said that instance methods are an especially nice feature which
should be used.  In fact, I suspect in most cases one doesn't need
them, and other solutions are more robust.  My only point was that it
was possible to experiment with them in python with fairly little
work.  One of the features of Python that I like so much is that thanks
to its dynamicity, one can explore language features and then decide to
not use them.  This exploration can only be done in a language which
allows it to exist.  I'd never thought much about instance methods, and
I'm not sure I'll ever need them, but I can play around with them to
find out if I do or not.

2) Thanks for the closure as class code -- I forgot it from the last
round of discussion on the topic.

--david



Wed, 19 May 1999 03:00:00 GMT  
 
 [ 4 post ] 

 Relevant Pages 

1. dynamic method addition, problem with arg=arg in lambdas (was:

2. Arg, parse arg, command line, and ?

3. Changing actual arg via intent(in) formal arg in F90

4. Best string arg access method

5. calling superclass' method with list positional arg

6. problem in generated code: 'Invalid arg 6: Cannot coerce a SDWORD to SDWORD*

7. Possible Parse Arg problem

8. Possible 'Parse Arg' problem

9. ARG() problem

10. AIX xlf: character string (dummy arg) problem

11. present(arg) problem

12. Problem with VB-COM-Python arg passing/value returning

 

 
Powered by phpBB® Forum Software