Skip to content

Is it really Pythonic to continue using linq operators instead of plain old functions? #94

@mchen402

Description

@mchen402

I've started to write my own linq operators and I have observed the following issues with using @extensionmethod to install these operators at runtime:

  1. It's tiring to add @extensionmethod to every operator. In no other Python codebase I've worked with has this pattern been necessary.

  2. Leads to buggy imports as import errors are only caught at runtime. Previously, missing imports for functions would have been caught at compile time (and highlighted by IDE).

    untitled2

    Even worse, such an import bug may never be identified if you happen to import another module first which does import and install my_op.

  3. Doesn't play well with static code analysers/IDEs (I'm using PyCharm) which is annoying and reduces productivity:

    • Cannot jump to source:
      untitled2
    • Doesn't understand that my_rx needs to be run in order to install my_op at runtime -- falsely flags the import as unnecessary
      untitled2
  4. Pollutes the Observable namespace massively. Having access to all operators in one class is akin to putting all your functions into one file. Sooner or later we're going to run out of sensible names and there's going to be a clash.

    • This is made worse by the use of aliases: I wrote my own time-weighted aggregation operator and called it aggregate, only to find that it didn't work. It was only later that I realised the name aggregate was already used as an alias for reduce().
    • This pattern also doesn't make use of Python's duck typing. Before using my operators I have to make sure it's installed for the classes on which I might want to invoke them.
  5. violates PEP20

    There should be one-- and preferably only one --obvious way to do it.

    Should I do observable.select(lambda x: x + 1) or select(observable, lambda x: x + 1)?

So is it really Pythonic to continue using linq operators instead of plain old functions? I.e. instead of observable.map(lambda x: x + 1) might I suggest something rx.map(lambda x: x + 1, observable) (which is more consistent with baselib map and therefore more obvious to newcomers)?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions