views:

244

answers:

3

I have been messing around with pygame and python and I want to be able to call a function when an attribute of my class has changed. My current solution being:

class ExampleClass(parentClass):
   def __init__(self):
      self.rect = pygame.rect.Rect(0,0,100,100)
   def __setattr__(self, name, value):
      parentClass.__setattr__(self,name,value)
      dofancystuff()
Firstclass = ExampleClass()

This works fine and dofancystuff is called when I change the rect value with Firsclass.rect = pygame.rect.Rect(0,0,100,100). However if I say Firstclass.rect.bottom = 3. __setattr__ and there for dofancystuff is not called.

So my question I guess is how can I intercept any change to an attribute of a subclass?

edit: Also If I am going about this the wrong way please do tell I'm not very knowledgable when it comes to python.

+1  A: 

You could try __getattr__, which should be called on Firstclass.rect.

But do this instead: Create a separate class (subclass of pygame.rect?) for ExampleClass.rect. Implement __setattr__ in that class. Now you will be told about anything that gets set in your rect member for ExampleClass. You can still implement __setattr__ in ExampleClass (and should), only now make sure you instantiate a version of your own rect class...

BTW: Don't call your objects Firstclass, as then it looks like a class as opposed to an object...

Daren Thomas
+3  A: 

Well, the simple answer is you can't. In the case of Firstclass.rect = <...> your __setattr__ is called. But in the case of Firstclass.rect.bottom = 3 the __setattr__ method of the Rect instance is called. The only solution I see, is to create a derived class of pygame.rect.Rect where you overwrite the __setattr__ method. You can also monkey patch the Rect class, but this is discouraged for good reasons.

Oliver Andrich
+1: Create your own subclass of rectangle and use that for everything.
S.Lott
A: 

I think the reason why you have this difficulty deserves a little more information than is provided by the other answers.

The problem is, when you do:

myObject.attribute.thing = value

You're not assigning a value to attribute. The code is equivalent to this:

anAttribute = myObject.attribute
anAttribute.thing = value

As it's seen by myObject, all you're doing it getting the attribute; you're not setting the attribute.

Making subclasses of your attributes that you control, and can define __setattr__ for is one solution.

An alternative solution, that may make sense if you have lots of attributes of different types and don't want to make lots of individual subclasses for all of them, is to override __getattribute__ or __getattr__ to return a facade to the attribute that performs the relevant operations in its __setattr__ method. I've not attempted to do this myself, but I imagine that you should be able to make a simple facade class that will act as a facade for any object.

Care would need to be taken in the choice of __getattribute__ and __getattr__. See the documentation linked in the previous sentence for information, but basically if __getattr__ is used, the actual attributes will have top be encapsulated/obfuscated somehow so that __getattr__ handles requests for them, and if __getattribute__ is used, it'll have to retrieve attributes via calls to a base class.

If all you're trying to do is determine if some rects have been updated, then this is overkill.

SpoonMeiser