Search code examples
pythonclassoverloadingargument-passing

Is there a way to pass arguments using the dot operator in python?


I'd like to the make a clean, "smart" method that performs specific operations without passing in arguments. I have some code that works in principle as follows:

class Foo():
    def __init__(self, spam='spam', ham='ham'):
        self.spam = spam
        self.ham = ham

    def bar(self, arg):
        # spam operation
        if arg == self.spam:
            return self.spam*2
        # ham operation
        elif arg == self.ham:
            return self.ham*3

Notice the bar method is written to perform different operations on spam and ham. Implementing this code would return:

foo = Foo()
foo.bar(foo.spam)                      # returns 'spamspam'
foo.bar(foo.ham)                       # returns 'hamhamham'

Unfortunately, I'm having to use foo twice to access a specific operation in bar, which is awkward and tedious. Is there a cleaner, Pythonic way to get the same results without passing in arguments? For example, is it possible to overload the dot (.) operator to the get the same results with:

# Ideal 1
foo = Foo()
foo.bar.spam                          # bar knows to use spam operation
foo.bar.ham                           # bar knows to use ham operation    

or even better

# Ideal 2
foo = Foo()
foo.spam.bar                          # bar knows to use spam operation
foo.ham.bar                           # bar knows to use ham operation     

Solution

  • EDIT: Updated to parametize the options.

    Here is something using object composition:

    class Pork(object):
        def __init__(self, name, mult):
            self.name = name
            self.mult = mult
    
        @property
        def bar(self):
            return self.name * self.mult
    
        def __repr__(self):
            return self.name
    
    class Foo(object):
        def __init__(self, spam='spam', ham='ham'):
            self.spam = Pork(spam, 2)
            self.ham = Pork(ham, 3)
    

    Results:

    In [638]: foo = Foo()
    
    In [639]: foo.spam.bar
    Out[639]: 'spamspam'
    In [641]: foo.ham.bar
    Out[641]: 'hamhamham'