6

First, if you guys think the way I'm trying to do things is not Pythonic, feel free to offer alternative suggestions.

I have an object whose functionality needs to change based on outside events. What I've been doing originally is create a new object that inherits from original (let's call it OrigObject()) and overwrites the methods that change (let's call the new object NewObject()). Then I modified both constructors such that they can take in a complete object of the other type to fill in its own values based on the passed in object. Then when I'd need to change functionality, I'd just execute myObject = NewObject(myObject).

I'm starting to see several problems with that approach now. First of all, other places that reference the object need to be updated to reference the new type as well (the above statement, for example, would only update the local myObject variable). But that's not hard to update, only annoying part is remembering to update it in other places each time I change the object in order to prevent weird program behavior.

Second, I'm noticing scenarios where I need a single method from NewObject(), but the other methods from OrigObject(), and I need to be able to switch the functionality on the fly. It doesn't seem like the best solution anymore to be using inheritance, where I'd need to make M*N different classes (where M is the number of methods the class has that can change, and N is the number of variations for each method) that inherit from OrigObject().

I was thinking of using attribute remapping instead, but I seem to be running into issues with it. For example, say I have something like this:

def hybrid_type2(someobj, a):
    #do something else
    ...

class OrigObject(object):
    ...
    def hybrid_fun(self, a):
        #do something
        ...

    def switch(type):
        if type == 1:
            self.hybrid_fun = OrigObject.hybrid_fun
        else:
            self.fybrid_fun = hybrid_type2

Problem is, after doing this and trying to call the new hybrid_fun after switching it, I get an error saying that hybrid_type2() takes exactly 2 arguments, but I'm passing it one. The object doesn't seem to be passing itself as an argument to the new function anymore like it does with its own methods, anything I can do to remedy that?

I tried including hybrid_type2 inside the class as well and then using self.hybrid_fun = self.hybrid_type2 works, but using self.hybrid_fun = OrigObject.hybrid_fun causes a similar error (complaining that the first argument should be of type OrigObject). I know I can instead define OrigObject.hybrid_fun() logic inside OrigObject.hybrid_type1() so I can revert it back the same way I'm setting it (relative to the instance, rather than relative to the class to avoid having object not be the first argument). But I wanted to ask here if there is a cleaner approach I'm not seeing here? Thanks

EDIT: Thanks guys, I've given points for several of the solutions that worked well. I essentially ended up using a Strategy pattern using types.MethodType(), I've accepted the answer that explained how to do the Strategy pattern in python (the Wikipedia article was more general, and the use of interfaces is not needed in Python).

2
  • +1 for creative approach Commented Jul 4, 2011 at 19:16
  • You need to assign the method on class level, then it will pass itself as argument. (Won't work if there's already a method on object-level by the same name) -- Also: Can you expand on " functionality needs to change based on outside events." ? We might be able to provide a better approach ;) Commented Jul 4, 2011 at 19:24

5 Answers 5

6

Use the types module to create an instance method for a particular instance.

eg.

import types

def strategyA(possible_self):
    pass

instance = OrigObject()

instance.strategy = types.MethodType(strategyA, instance)

instance.strategy()

Note that this only effects this specific instance, no other instances will be effected.

Sign up to request clarification or add additional context in comments.

Comments

6

You want the Strategy Pattern.

Comments

2

Read about descriptors in Python. The next code should work:

else:
    self.fybrid_fun = hybrid_type2.__get__(self, OrigObject)

3 Comments

How does this make it a descriptor?
@phant0m nope. this make it unbound method
Ah, I thought you wanted him to use a descriptor. I see what you mean now. You would want to make it a bound method though, i.e. passing self instead of None
1

What about defining it like so:

def hybrid_type2(someobj, a):
    #do something else
    ...

def hybrid_type1(someobj, a):
    #do something
    ...

class OrigObject(object):

    def __init__(self):
        ...
        self.run_the_fun = hybrid_type1

    ...
    def hybrid_fun(self, a):
        self.run_the_fun(self, a)

    def type_switch(self, type):
        if type == 1:
            self.run_the_fun = hybrid_type1
        else:
            self.run_the_fun = hybrid_type2

3 Comments

This will not work because the method isn't bound. You would need to assign the methods to OrigObject.run_the_fun This would change the behaviour of all instances of this class though.
@phant0m Seems to work here. I'm not fully aware of the subtleties of why it works in terms of bound/unbound methods. I think what's happening is that self.run_the_fun is treated as a variable that just happens to be a function.
Ah, I didn't notice that you had an extra method to run it. I thought you wanted it to work when calling: orig_object.run_the_fun("something")
0

You can change class at runtime:

class OrigObject(object):
    ...
    def hybrid_fun(self, a):
        #do something
        ...
    def switch(self):
        self.__class__ = DerivedObject


class DerivedObject(OrigObject):
    def hybrid_fun(self, a):
        #do the other thing
        ...
    def switch(self):
        self.__class__ = OrigObject

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.