0

I'm a former c++ coder and have made the plunge into python for several months, which has been great. I'm doing a bit of code porting and have come across a problem of which i'm not sure the best path. There are many ways to skin a cat - but i'm looking for advice on what would be the 'best' and/or most pythonic way to do something similar to the section of c++ code below.

I've cut the code to a trivial 'reproducer' just to highlight what was happening. Essentially there is a well-defined interface of callbacks the server will call when certain events happen. When a server is created, it is provided a callback interface as an argument.

In the below case, the client has implemented these callbacks on itself, and thus when it is creating the server, it provides the *this pointer.

Is this something similar in python? Any suggestions would get greatly appreciated.

#include <iostream>

// Client Feed Handler function Interface
class ClientFeedHandlersI
{
public:
    virtual void HandlerFeed1() = 0;
    virtual void HandlerFeed2() = 0;
};

// A very basic server
class Server
{
public:
    Server(ClientFeedHandlersI& handlers) 
        : mHandlers(handlers) {}

void Start()
{
    // Create some random events to illustrate
    for (int i = 0; i < 10; ++i)
        EventLoopHandler();
}
private:
    void EventLoopHandler()
    {
        if (rand() % 10 > 5)
            mHandlers.HandlerFeed1();
        else
            mHandlers.HandlerFeed2();
    }

    ClientFeedHandlersI& mHandlers;
};

// A really simple client
class Client : private ClientFeedHandlersI
{
public:
    Client() 
      : mMyServer(*this)
    {
        mMyServer.Start();
    }

private:
    void HandlerFeed1() override { std::cout << "HandlerFeed1 called\n"; }
    void HandlerFeed2() override { std::cout << "HandlerFeed2 called\n"; }

    Server mMyServer;
};

int main()
{
    auto c = Client();
}

So here's an attempt at porting to python. Note that in the real world example, there are 20+ client feed handler functions, hence why i want to force the interface using the @abstractmethod.

# python 3.6
from abc import ABC, abstractmethod

class ClientFeedHandlersI(ABC):

    def __init__(self):
        pass

    @abstractmethod
    def handler_feed_1(self):
        pass

   @abstractmethod
   def handler_feed_2(self):
        pass

class Server:

    def __init__(self, clientCallBacks:ClientFeedHandlersI):

        self.clientCallBacks = clientCallBacks

    def start(self):
        for ii in range(10):
            self.event_loop_handler()

    def event_loop_handler(self):
        import random
        if random.randint(0,10) > 5:
            self.clientCallBacks.handler_feed_1()
        else:
            self.clientCallBacks.handler_feed_2()


class Client(ClientFeedHandlersI):

    def __init__(self):

        self.server = Server(self)
        self.server.start()

    def handler_feed_1(self):
        print("HandlerFeed1 called\n")
    def handler_feed_2(self):
        print("HandlerFeed2 called\n")

if __name__ == '__main__':
    c = Client()

Edit: The above code sample now works as per the c++ code.

Drew
  • 517
  • 5
  • 16
  • 4
    Not sure what you are asking about, functions are first class objects in Python so you can easily pass them around. You should not be thinking about how to translate C++ code into Python code. When you learn a new language you are basically starting out from scratch, if the language is worth learning. –  May 15 '18 at 22:46
  • Python is completely dynamic, as long as the functions are there it will work. If you really need to be strict about an interface, you can use the [abstract base class](https://docs.python.org/3/library/abc.html) module. – Paul Rooney May 15 '18 at 23:20
  • Added my attempt at porting to python. Happy to rewrite it, but I'm just not sure the best way to go about it. Thanks – Drew May 15 '18 at 23:54
  • Why all the uneccessary ABC-meta stuff. Just pass the functions directly. Why bother with a class for `ClientFeedHandlersI`? Just define the functions and pass the callbacks directly. In a list if necessary – juanpa.arrivillaga May 15 '18 at 23:59
  • 1
    But anyway, your code is failing because in your `Server` class definition you did `def __main__` instead of `def __init__(...)`. It's sort of pointless to even bother with a `if __name__ == '__main__'` guard when you post example code anyway... – juanpa.arrivillaga May 16 '18 at 00:03
  • the interface is *desirable* but not mandatary, as there are 20+ functions on the interface, and sometimes more get added or changed. – Drew May 16 '18 at 00:06
  • Juanpa - you just solved it for me. Changing that errornous __main__ in Server into the __init__ fixed it. The above code sample now works. – Drew May 16 '18 at 00:08
  • @Drew right. Why would the interface be desirable over say, a list of callbacks, when the number of functions *is variable*? – juanpa.arrivillaga May 16 '18 at 00:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171119/discussion-between-drew-and-juanpa-arrivillaga). – Drew May 16 '18 at 00:18
  • So the upshot is that this is a duplicate of [this question](https://stackoverflow.com/questions/625083/python-init-and-self-what-do-they-do) right? – senderle May 16 '18 at 11:27

1 Answers1

-1

Python sample code below C++ in OP now working.

Drew
  • 517
  • 5
  • 16