Class for comparing objects in Python
Use functions whenever possible

I think there are too many classes and you should consider using functions instead: it is easier to test (unit testing).

Use a logger

First thing: the code below make me think that you should use a logger instead of reinventing the wheel:

report = reporter.format_report()
save_to_file.send(report)
show_on_console.send(report)


Here is how you can define a root logger which can log in the console as well as in a file:

import logging


def setup_logger(log_dir, log_file_name):
    # root logger
    logger = logging.getLogger()

    formatter = logging.Formatter(
        "%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")

    file_hdlr = logging.FileHandler(
        "{0}/{1}".format(log_dir, log_file_name))
    file_hdlr.setFormatter(formatter)
    logger.addHandler(file_hdlr)

    console_hdlr = logging.StreamHandler()
    console_hdlr.setFormatter(formatter)
    logger.addHandler(console_hdlr)

    logger.setLevel(logging.DEBUG)


This setup_logger function should be called at program startup, in your main, for instance:

if __name__ == "__main__":
    setup_logger("my/work/dir", "my_app.log")


You can use this logger anywhere: in each Python module, you can define a global variable named LOG, and use it like this:

LOG = logging.getLogger(__name__)


def my_function():
    LOG.info("log message")


See the How To documentation

wrong usage of generator

In your, competitors is a generator, not a tuple. After having iterate a generator the first time, then the generator is exhausted. It means that the second loop will end immediately.

self.competitors = (item for item in competitors if self.valid_item(item))


The call to rule_compared_smaller will iterate the competitors, then the call to rule_compared_two_times_smaller will do nothing because the competitors is exhausted.

You need to create a list:

self.competitors = [item for item in competitors if self.valid_item(item)]