0

I'm writing a Python3.x framework that has generators and filters. I have a compact syntax for chaining the output of generators and filters into filters, but file organization feels inelegant. Here's what I mean.

Assume Renderer is the super class for both generators and filters:

# file: renderer.py -- defines the common superclass used by generators and filters
class Renderer(object):
    def render():
      # Every subclass provides a `render` function that emits some data...
      pass
# file: gen1.py -- defines a generator
class Gen1(Renderer):
    def __init__(self):
        super(Gen1, self).__init__()

    def render(self):
        ... emit some data
# file: filt1.py -- defines a filter that takes any Renderer object as an input
class Filt1(Renderer):
    def __init__(self, input, param):
        super(Filt1, self).__init__()
        self._input = input
        self._param = param

    def render():
        ... call self._input.render() to fetch and act on data before emitting it
# file: filt2.py -- defines a filter that takes any Renderer object as an input
class Filt2(Renderer):
    def __init__(self, input):
        super(Filt2, self).__init__()
        self._input = input

    def render():
        ... call self._input.render() to fetch and act on data before emitting it
# file: render_module.py -- a module file to bring in all the components
from renderer.py import Renderer
from gen1.py import Gen1
from filt1.py import Filt1
from filt2.py import Filt2

What I'd like

What I'd like is for a user of the platform to be able to write code like this, which chains the output of gen1 into filt1, and the output of filt1 into filt2:

import render_module as rm

chain = rm.Gen1().filt1(123).filt2()
chain.render()

What I've done

What I've done is add the following to renderer.py. This works, but see "The Problem to Solve" below.

class Renderer(object):
    def render():
      # emit some data...
      pass

    def filt1(self, param):
        return rm.Filt1(self, parm)

    def filt2(self):
        return rm.Filt1(self)

import render_module as rm  # at end of file to avoid a circular dependency

The Problem to Solve

It feels wrong to pollute the common superclass with specific mentions of each subclass. The clear indication of code smell is the import statement at the end of renerer.py.

But I haven't figured out a better way to refactor and organize the files. What's the pythonic approach out of this conundrum?

fearless_fool
  • 33,645
  • 23
  • 135
  • 217
  • 1
    Is this intended to run as Python 2? If so, you should probably tag it as such. – Brian61354270 Jan 16 '23 at 21:28
  • @Brian Done. It's strictly Python3. (I thought Python3 was the default by now...) – fearless_fool Jan 16 '23 at 22:01
  • In that case, it's worth noting that inheriting from `object` and invoking super with arguments are only needed for compatibility with Python 2. If you're only writing for Python 3, you can just write `class Foo:` and `super().method()`. See also [Why do Python classes inherit object?](https://stackoverflow.com/a/45062077/11082165) and [What is the difference between super() with arguments and without arguments?](https://stackoverflow.com/q/57945407/11082165) – Brian61354270 Jan 16 '23 at 22:24
  • @Brian Thanks - that will neaten up my __init__() functions, but -- if I understand correctly -- doesn't address the file org question. Did I get that right? – fearless_fool Jan 16 '23 at 22:28
  • Yes, that was a side note. – Brian61354270 Jan 16 '23 at 22:35
  • 1
    Also, if you haven't considered it already, this sort of high-level design question might get better attention over on [Software Engineering SE](https://softwareengineering.stackexchange.com/). – Brian61354270 Jan 16 '23 at 22:37
  • @Brian [Software Engineering SE] -- good suggestion. To avoid redundancy, I'll let it simmer here for a while before cross-posting. Thanks. – fearless_fool Jan 16 '23 at 22:58

0 Answers0