Augmented assignment again

Roeland Rengelink r.b.rigilink at cable.a2000.nl
Wed Aug 30 17:37:29 EDT 2000


Thomas Wouters wrote:

Hi Thomas,

thanks for the reply,

I'll be stubborn and continue this a bit.

> 
> On Wed, Aug 30, 2000 at 08:28:58AM +0200, Rpoeland Rengelink wrote:
> 
> > I've been working recently on wrapping an image manipulation library
> > that only provides in-place arithmetic operators. I've tried
> > to overload the normal arithmetic operators in terms of
> > copies and augmented assignment operators with somewhat mixed results.
> 
> It's not possible to do *only* in-place operations, in Python, unless you
> forgo all the normal arithmatic operators. And that means not writing
> 
> d = a+b+c
> 
> but
> 
> d = a # or a.copy()
> d += b
> d += c
> 
> As soon as you mix normal binary operators in there, you will get new
> objects.
> 

The objective of the exercise was not to completely avoid the creation
of new objects, just the superfluous creation. I.e. can we implement
__add__(self, other) in terms of copies and augmented assignment in such
a way that 

d = a+b+c

only results in one copy (of a), and not two (of a and the result of
a+b)



> > What I would really like is something like:
> 
> > class Obj:
> >     ...
> >
> >     def __assign__(self):
> >         self.is_temporary = 0
> 
> > I.e., a function that's invoked whenever an instance is named
> 
> An instance isn't "named", it's bound to a name in the local namespace. But
> that doesn't mean it's not "finalized" until then! And a lot of other
> bindings are possible. Consider
> 
> l[1] = a+b+c
> 
> and
> 
> x.d = a+b+c
> 

I'm glad I posted, because I hadn't thought of these.

> Those aren't 'named', but they should be considered 'finalized'. I think
> you'll either have to do it manually, or stick to augmented assignment
> operators (or just create the new objects when the programmer asks for it,
> and educate them on the consequences. If they want to create copies, they
> can do that anyway!)

Ah, the Numpy solution.

My users will be astronomers that want to do

reduced_data = (raw_data-bias)/flatfield

where raw_data may be a stack of, say 30 2k x 4k images. They will not
like an extraneous copy of 1 GB worth of data, and they will not read
the manual.
So, either I don't supply binary arithemtic operations (which they will
not 
like/understand), or I solve this.
 
Well, I give it one more try below. But note that this would have been
trivial if we had an additional 'magic' method __assign__ (or
__finalize__) which is called whenever an instance is (first) bound. I
think this post shows that binding an instance can be a significant
moment in an instance's lifetime, on par with creation (__init__) and
deletion (__del__). So why not have a special class method that is
called at these moments?


import sys, types

def object_contains_reference(obj, instance):
    '''Helper function to see if obj or any object "contained" in obj
refers to instance'''

    if obj == instance:
        return 1
    obj_type = type(obj)
    if obj_type == types.ListType or obj_type == types.TupleType:
        for item in obj:
            if object_contains_reference(item, instance)
                 return 1
    if obj_type == types.DictType:
        for item in obj.items():
            if object_contains_reference(item, instance):
                return 1
    if obj_type == types.ClassType or obj_type == types.InstanceType:
        return object_contains_reference(obj.__dict__.items(), instance)
    return 0            

class Object:
    def __init__(self, data):
        self.data = data
        print 'Creating', self
        self.is_temporary = 0

    def copy(self):
        result = Object(self.data)
        print self, 'data copied to', result
        return result

    def finalize(self):
        '''temporary objects that are referred to shoud not be
temporary'''
        if self.is_temporary:
            try:
                raise "GetTB"
            except "GetTB":
                frame = sys.exc_info()[2].tb_frame
                while frame:
                    for key, val in frame.f_locals.items():
                        if key != 'self' and
object_contains_reference(val, self):
                            print "Reference to", self, "found"
                            self.is_temporary = 0
                            return
                    frame = frame.f_back

    def add(self, other): # __add_ab__
        print 'Add', self, other
        self.data = self.data+other.data
        return self

    def __add__(self, other):
        self.finalize()
        if self.is_temporary:
            self.add(other)
            return self
        else:
            result = self.copy()
            result.is_temporary = 1
            return result.add(other)

def test():
    a = Object(1.0)
    b = Object(2.0)
    c = Object(3.0)
    class T:
        pass
    d = T()
    d.d = {}
    d.d['d'] = [a+b+c]
    e = d.d['d'][0]+a
    
if __name__ == '__main__':
    test()


Cheers,

Roeland

> --
> Thomas Wouters <thomas at xs4all.net>
> 
> Hi! I'm a .signature virus! copy me into your .signature file to help me spread!



More information about the Python-list mailing list