Tim Hargreaves
Tim Hargreaves

Reputation: 332

Grouping class methods using dot notation in Python

I've found many questions on StackOverflow that come close to solving my problem but none that quite do it. I have a class in Python and I would like to split its methods up so that I can use the class in the following way:

my_greeter = Greeter(name='Bob')
my_greeter.sayHello()
#> "Hello Bob"
my_greeter.international.sayHola()
#> "Hola Bob"

I'm not quite sure how to structure my files and their contents to achieve this. Could someone please give an example?

Note: An example of this behaviour in the wild is pandas.Series.str.contains() although the source code was too dense for me to pick apart how they did this.

Upvotes: 2

Views: 1313

Answers (3)

Jack B
Jack B

Reputation: 111

I came across this while looking for a way to group methods. The accepted answer (fluent design) is great for chaining methods - where each .method is doing something - but I found a simpler option for simply grouping things together for organisational reasons:

from types import SimpleNamespace

class Example():
    def __init__(self):
        self.property1 = "foo"
        self.property2 = "bar"

        self.group1 = SimpleNamespace()
        self.group1.property3 = 1.23
        self.group1.property4 = 4.56

Upvotes: 1

heemayl
heemayl

Reputation: 42097

This is an example of Fluent API design, which uses method chaining in it's core. The implementation idea of this in Python is to return the instance from each method after doing what they are supposed to do.

Here is a naive example:

In [1]: class Fluent: 
   ...:     def __init__(self, num): 
   ...:         self.num = num 
   ...:     def add_two(self): 
   ...:         self.num += 2 
   ...:         return self  # this allows us for chaining
   ...:     def result(self): 
   ...:         return self.num 
   ...:                                                                                                                                                                                                     

In [2]: f = Fluent(10)                                                                                                                                                                                      

In [3]: f.add_two().result()                                                                                                                                                                                
Out[3]: 12

You can make add_two a property to avoid the method call, and refer as an attribute:

f.add_two.result()

Edit: Example with property:

In [4]: class Fluent: 
   ...:     def __init__(self, num): 
   ...:         self.num = num
   ...:     @property
   ...:     def add_two(self): 
   ...:         self.num += 2 
   ...:         return self  # this allows us for chaining
   ...:     def result(self): 
   ...:         return self.num 
   ...:                                                                                                                                                                                                     

In [5]: f = Fluent(10)                                                                                                                                                                                      

In [6]: f.add_two.result()                                                                                                                                                                                
Out[6]: 12

Upvotes: 2

Monolith
Monolith

Reputation: 1147

One way to do this would be to import a module named international into your so called Greeter class. Then, you could do things as normal and put your methods under the international namespace into a different file. In your case:

# main.py
class Greeter:
    import international # To import the international file's contents into the class
    def __init__(self, name="someone"):
        self.name = name
    def sayHello(self):
        print(f"Hello {self.name}")

And the international file:

# international.py
def sayHola(self):
    print(f"Hola {self.name}")

Some notes:

  • Always add self as the first argument in every method you define in international.py

  • When you import the files into a class when the method is run the function automatically has self passed to it

  • If you need helper functions that you do not want to be used by the user I would recommend you put them in a different file (and import them inside the method)

Upvotes: -1

Related Questions