0

Let Instrument be a class with three functions piano(self), guitar(self) and trumpet(self). The class has also a play(self) function, which will call the proper method, according to the instrument that is represented in the class.

Is there a way, with only one class Instrument (i.e., no other abstract class), to define the method that should be invoked when calling the play method?

In other words, is there a way to do the following:

my_piano = Instrument(piano)
my_piano.play() # calls piano(self) method

my_guitar = Instrument(guitar)
my_guitar.play() # calls guitar(self) method

my_trumpet = Instrument(trumpet)
my_trumpet.play() # calls trumpet(self) method

A rather simple way of doing it, but not very code clean, is to put a variable in the constructor, and then put a lot of conditions in the play method, like this:

def Instrument(object):
    def __init__(self, instrument_type):
        self.instrument_type = instrument_type

    def play(self):
        if instrument_type == 0:
            self.piano()
        elif instrument_type == 1:
            self.guitar()
        elif instrument_type == 2:
            self.trumpet()

The proper way of doing it would be to have an Instrumentclass and then three Piano, Guitar and Trumpet classes which inherit from Instrument. But for the need I have, that would be complicate things for not much.

EDIT

As requested, I explain my actual problem, which is almost identical. I have a class ComputeSth that can use five different methods to compute that something. It can compute it in ways a(self), b(self), ..., f(self). For exampple, sometimes I want to compute according to a, some other times according to c. That is easy if I just do:

my_computer = ComputeSth()
my_computer.a()
my_computer.c()

But then I noticed that in order to easily use this class in other parts of my code, I would prefer to do my_computer.compute(). So, I thought it would be convenient to construct the instance with the actual method to compute when calling compute(self). I didn't want to write five other classes, since they really wouldn't represent five different objects, but rather five ways of doing the same. I can't pass the function a,...,f as an input of the constructor because the require the self.
In other words, same as the example with Instrument, but with less "real world inheritance".

8
  • This is what polymorphism, in any object oriented language is for. You are supposed to create separate classes for Piano/Guitar which inherit from base class Instrument Commented Nov 16, 2015 at 13:47
  • You could look into single dispatch but I'm not 100% sure it works on methods and I believe it would be more of a hassle than just playing with inheritance. Commented Nov 16, 2015 at 13:53
  • @lejlot Yes, I know that I can do it like you propose on your answer (see my last paragraph). Buy I thought that maybe there could be a simpler way, like the one I propose which is ugly but simple and works. In the instrument example it makes far more logic to do it with inheritance, but in my actual problem it will look heavy and more complicated that I want. Commented Nov 16, 2015 at 13:54
  • 1
    then you should describe your actual problem, because otherwise we cannot give you any reasonable answer. Commented Nov 16, 2015 at 13:55
  • 1
    This is actually still "way to go" with inheritance, as these are different types of computing. In python, with multiple inheritance and "mixins" it is ok to have a subclass with just one method Commented Nov 16, 2015 at 14:18

3 Answers 3

6

This is what polymorphism is for

class Instrument:

  def play(self):
    pass

class Guitar(Instrument):

  def play(self):
    print 'brau dau'

class Drum(Instrument):

  def play(self):
    print 'dum dum'


guitar = Guitar()
guitar.play()

drum = Drum()
drum.play()
Sign up to request clarification or add additional context in comments.

2 Comments

Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem.
Also Instrument.play should raise NotImplementedError
3

This is most certainly the wrong design, but definitely doable:

class Instrument:
    def __init__(self, method):
        self.method = method

    def play(self):
        self.method(self)

    def piano(self):
        print("I am a piano")

    def trumpet(self):
        print("I am a trumpet")

    def guitar(self):
        print("I am a guitar")

piano = Instrument.piano
trumpet = Instrument.trumpet
guitar = Instrument.guitar

my_piano = Instrument(piano)
my_piano.play()
#... and others

You pass an unbound class method to the __init__ of instrument, and then in play invoke that method on self.

You could even extend the available instruments by creating freestanding functions

def violin(self):
    print("I am playing a violin")

my_violin = Instrument(violin)
my_violin.play()

But the whole approach shown above does have very little use in the python world.

Comments

2

If you really don't want to make a class hierarchy, you could define all possible types in an enum class, and then just choose correct method. In order to avoid if statements you may use dictionaries:

In [1]: import enum

In [2]: class InstrumentType(enum.Enum):
   ...:     PIANO = 0
   ...:     GUITAR = 1
   ...:     TRUMPET = 2
   ...:     

In [3]: class Instrument:
   ...:     def __init__(self, type):
   ...:         self.type = type
   ...:     def piano(self):
   ...:         print('Piano')
   ...:     def guitar(self):
   ...:         print('Guitar')
   ...:     def trumpet(self):
   ...:         print('Trumpet')
   ...:     def play(self):
   ...:         return {
   ...:             InstrumentType.PIANO: self.piano,
   ...:             InstrumentType.GUITAR: self.guitar,
   ...:             InstrumentType.TRUMPET: self.trumpet
   ...:         }.get(self.type, lambda: print('Not an instrument'))()
   ...:     

In [4]: guitar = Instrument(InstrumentType.GUITAR)

In [5]: guitar.play()
Guitar

In [6]: unknown = Instrument('UNKNOWN')

In [7]: unknown.play()
Not an instrument

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.