10

This is how it works for me:

class SomeName:
  def __init__(self):
    self.value = "something"
  def some_method(self):
    print self.value

def external_func(instance, method):
  method(instance)

external_func(SomeName(), SomeName.some_method)

This appears to work correctly. Is this the right way to do this?

AturSams
  • 7,568
  • 18
  • 64
  • 98
  • 4
    You could just pass `SomeName().some_method` then call `method()`, it is also not really a class method as you are calling it with an instance – Padraic Cunningham Jul 15 '15 at 11:48
  • 1
    @PadraicCunningham I think OP probably just means "a class's method", not a classmethod. – Rick Jul 15 '15 at 11:59
  • But it needs to be able to call it with data that will be computed later. There is some function foo(), were all the steps are the same except the method used to do (for instance) some tax computation which depends on the region or something (my goal is to prevent code duplication). – AturSams Jul 15 '15 at 12:01
  • @zehelvion, not sure what the difference is, you can call the method and pass whatever you like, maybe add more context to your question if there is something you feel might be an issue – Padraic Cunningham Jul 15 '15 at 12:05

4 Answers4

7

Your code is "technically correct" (it does what you ask for) but - at least in your example - pretty useless:

def external_func(instance, method):
  method(instance)

external_func(SomeName(), SomeName.some_method)

is the same as:

def external_func(method):
  method()

external_func(SomeName().some_method)

which FWIW is the same as:

SomeName().some_method()

but I assume you understood this already .

Now you probably have a reason to try to pass both the method AND instance to external_func(), or there might be a better way to solve your real problem...

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • oh, in the real case, the function needs to construct and preform some lengthy computation with the class instance, using one of it's specific method. – AturSams Jul 15 '15 at 12:10
  • My use-case is to be able to pass the method without the instance to avoid circular imports... – Dr_Zaszuś Jun 29 '22 at 11:09
1

I of course don't know what you're doing exactly, but it sounds to me like you're trying to do too much inside of one function. Your problem might be better solved by simply splitting up the contents of external_func.

The goals here, as I understand them, are you don't know ahead of time what the object/method pair will be, and want to reduce code repetition.

Perhaps something like this would be better:

def main():
    obj = SomeName()
    # do the setting up portion 
    complex_object = external_func_set_up(obj)
    # presumably at some point you have to designate the method to be used:
    method = get_method_name(obj) 
    # run the method:
    getattr(obj, method)()
    # finish up the external operation: 
    external_func_complete(***args***) 

I understand this is more code, but I think in the end it's a lot clearer what is happening, and also might force you to think through your problem a bit more (and potentially come up with an even better solution).

Rick
  • 43,029
  • 15
  • 76
  • 119
0

You could pass SomeName().some_method or make some_metod staticmethod or classmethod if there is no instance data used in your method.

Check documentation to know more about staticmethod and classmethod:

  • It is not a static method, it has a `self` argument and depends on the `value` attribute of the example class. It is therefore *not* static by definition. – Cory Kramer Jul 15 '15 at 12:00
  • You're correct, but as I see the `value` is constant, hence it's possible to define it as a class property, hence it's possible to use `classmethod`. – Olexander Yermakov Jul 15 '15 at 12:15
0

Depending on what you're doing. Because functions are also objects in Python it is possible to do so.

But is it a good solution? It seems though that you're trying to handle a problem which maybe could be better solved with more of an object oriented approach:

class A:
    def __init__(self):
        self.value = "class A"
    def some_method(self):
        print self.value

class B:
    def __init__(self):
        self.value = "class B"
    def some_method(self):
        print self.value

some_class = A()
some_class.some_method()

some_class = B()
some_class.some_method()

Output:

"class A"
"class B"

In my view this would be a better approach (if this is possible/reasonable in your case): You just call some_method() on your class, maybe without even knowing what exact type of object you're dealing with (regarding inheritance). The class itself knows what to do and reacts accordingly when its method has been called.

This of course doesn't work when you work with external libraries which you have no influence on.

adrianus
  • 3,141
  • 1
  • 22
  • 41
  • In my case the class itself is the same and familiar with two (or more) behaviors but the situation changes. Imagine this, a `Dog` class that can `bite`, `bark` or `growl` depending on what the owner decides. We need to pass the behavior the owner desires to some dog which isn't born yet. The dog allocation / computation process is always the same but the behavior is an argument. The dog is computed based on some data provided. – AturSams Jul 15 '15 at 12:06
  • As we try to model classes accordingly to real world objects, it would be kind of odd to ask an unborn dog to `bark`, isn't it? Is there no way to wait until the dog has been created before you decide to tell it what to do? – adrianus Jul 15 '15 at 12:51
  • I'm starting to think this is right. There is a model (instance) and a context, was the model needs to behave. Imagine a person in a formal business and the same person in a rock concert. The function / script, gets the input to generate the person instance and the situation (appropriate behavior). It is used to determine the result of the derived instance with the specified behavior. – AturSams Jul 15 '15 at 13:54
  • @wolfdawn wery late comment but anyway: what you describe above looks like the strategy pattern. – bruno desthuilliers Apr 09 '18 at 08:18