dynamic type changing

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun May 28 14:58:10 EDT 2006


andychambers2002 at yahoo.co.uk a écrit :
>>>I'm working on a "TempFile" class that stores the data in memory until
>>>it gets larger than a specified threshold (as per PEP 42).  Whilst
>>>trying to implement it, I've come across some strange behaviour.  Can
>>>anyone explain this?
> 
> 
>>>The test case at the bottom starts a TempFile at size 50 and prints its
>>>type.  It then increases the size to the threshold at which point
>>>"self" is changed to being a TemporaryFile.
>  
>>Changed how ?-)
>  
> Just by being assigned with a TemporaryFile object.
 >
>  I thought that if
> you do
> 
> instance = TempFile()
> 
> that "instance" and "self" defined in the Class

They are not defined "in the class".

> were the same thing so
> that if you changed the class of self, 
> the class of instance would also
> change.

Yes, of course - but you didn't change the class of 'self' !-)

Python's "variable" are really just names "bound to" (referring to) 
objects. Rebinding (ie: assignment) does not impact the object (well, 
not directly), it just associate the name to another object. This is 
totally different from changing the state of the object.

There's nothing magical about the name 'self' - FWIW, you could replace 
it by any other valid python identifier. In Python, a method is just a 
plain function that takes the instance as the first argument. This 
function is wrapped into a method descriptor (google for python's 
descriptor protocol - it's the same thing that is used for properties) 
that takes care of feeding the function with the instance.

FWIW, given:
   class Obj(object):
     def someMethod(self):
       pass

   obj = Obj()

then
   obj.someMethod()

is the same as
   Obj.someMethod(obj)

or also:
   obj.someMethod.im_func(obj)


So, inside someMethod's code, normal scoping rules apply. This means 
that 'self' is a *local* name, and rebinding it only affect the local 
scope. And it doesn't "change the type" of the object (previously) bound 
to 'self', it really re-bind 'self' to another object (IOW: makes 'self' 
a reference to another object). Just like it does in any other Python code.

As I wrote, to dynamicall change the class of an object, you must rebind 
obj.__class__ to another class, ie:

class Other(object):
   def __init__(self, name):
     self.name = name

obj = Obj()
print type(obj)
obj.__class__ = Other
print type(obj)

Now a big warning : this is not garanteed to work seamlessly ! 'obj' 
will keep it's original instance attributes, and the instance attributes 
normally set up by the new class (here, 'Other') won't exist since the 
class initializer won't be called.

So, while this may not be a problem if the original and new classes are 
designed to be used that way (which makes a very straightforward 
implementation of the state pattern), it's usually not a good idea to do 
such a thing. FWIW, it's usually simpler and safer - evn if a bit less 
elegant - to implement the state pattern just like I did in the example: 
by using composition/delegation.

> Thanks very much for your example.

<reverence>votre humble serviteur, Messire</reverence>

>  It has solved my problem and helped
> me understand a new pattern at the same time.

<ot>
FWIW, there's no clear, well defined boudary between State and Strategy 
- the main difference depends mostly on the intention. Your use case 
could also be viewed as a State pattern, with 2 states : buffer < 
capacity, and buffer >= capacity. But the intention is not to know in 
which state is the object - on the contrary, you're trying to hide away 
the chosen implementation (StringIO or TemporayFile) - so it's really a 
Strategy IMHO.
</ot>



More information about the Python-list mailing list