Question Details

No question body available.

Tags

python object-oriented-design

Answers (2)

January 28, 2026 Score: 3 Rep: 221,029 Quality: Medium Completeness: 60%

Your second approach fails when foo, bar and baz are not of three different types, so it is actually a bad idea. Heck, in a dynamically typed language like Python, it is not even guaranted that each foo object will have the same type.

What you can do without changing the initial scan method: you can implement a class FooCollector derived from ScanHandler, where onFoo just collects all passed foo objects in a list, and onBar as well as onBaz stay empty. Then call scan(someInput, fooCollector) and ask fooCollector afterwards for the list of foo objects it got. This is dead simple and does not require any type checks.

You could then implement scanner.getAllFoos this way:

class Scanner:
    ...
    def getAllFoos(someInput):
         fooHandler = FooHandler()
         scan(someInput,fooHandler)
         return fooHandler.listOfCollectedFoos

As a variant of this, you may implement a FooHandler in a similar manner, which does not just fill a list, but does the foo processing directly in-place.

Where I would also take a deeper look into: the section

... complicated logic ... 

should be refactored to a function which returns a result which makes he following conditionals if ... something ...: simple. This gives you the option to provide an extra function scanForFoos sharing the same logic as scan without duplicating it. Something along the lines of

def scan(someInput, handler : ScanHandler):
    for chunk in someInput:
        result=callToComplicatedLogic(...)
        if result.conditionForFoo():
            foo = result.getFoo()
            handler.onFoo(foo)
        if result.conditionForBar():
            bar = result.getBar()
            handler.onBar(bar)
        if result.conditionForBaz():
            baz = = result.getBaz()
            handler.onBaz(baz)

def scanForFoos(someInput): for chunk in someInput: result = callToComplicatedLogic(...) if result.conditionForFoo(): foo = result.getFoo() yield foo

This keeps the complicated logic in one place, hence the code DRY, with a little bit more boilerplate, of course. This approach has the advantage that it allows the caller to iterate over a subset of foo object and stop the iteration, lets say, after the first 10 objects, without making the scanner going through the full input data.

January 28, 2026 Score: 1 Rep: 31,152 Quality: Medium Completeness: 60%

Based on your comment on Doc Brown's answer, I think you are looking for something like this:

class Scanner:
    ...
    def scan(someInput, handler : ScanHandler, emit = ('foo', 'bar', 'baz')):
        for chunk in someInput:
            ... complicated logic ...
            if ... something ... and 'foo' in emit:
                foo = ...
                handler.onFoo(foo)
            if ... something else ... and 'bar' in emit:
                bar = ...
                handler.onBar(bar)
            if ... something else ... and 'baz' in emit:
                baz = ...
                handler.onBaz(baz)

If you exclude an emit parameter, it will work as it does now. If you want to just get 'foo' output, you can call like this:

scan(input, scanner, ['foo'])

There are many levels of clever fanciness that you can add to this basic idea. Another simple one would be to do this instead:

def scan(someInput, handler : ScanHandler, emit = lambda x: True):
   for chunk in someInput:
       ... complicated logic ...
       if ... something ... and emit('foo'):
          foo = ...
          handler.onFoo(foo)

Then the foo-only call becomes:

scan(input, scanner, lambda x: x == 'foo')

The advantage here being that your function allows for arbitrary logic. You can add more parameters to add more capabilities.

You might also want to consider adding the emit list (or function) to the Scanner object. This might be useful if the decision of what to emit does not change during the scanners lifetime. There's also nothing preventing you from doing both. That is, setting it on the object and allowing it on the method call. You just need to decide what you want to do if both are set.