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