The Magic of @conditional_method: Selective Method Implementation in Python
Excited to share my new open-source Python library “conditional-method” that enables conditional method implementation based on runtime conditions and class building time constraints.
With a simple @conditional_method decorator, you can define multiple versions of the same method and have only the appropriate one activated based on conditions like environment variables, feature flags, or platform specifics.
Rather than cluttering your code with complex if/else statements or strategy patterns, this library automatically selects the right implementation during class construction.
Your classes become leaner and more focused - each method exists only post-class construction only when needed, reducing cognitive load and eliminating the possibility of selecting the wrong implementation at runtime.
Each conditional method clearly declares its own activation criteria, making the code self-documenting and easier to maintain as your application evolves.
For example, a single DatabaseConnector class can handle connection logic (note the keyword here is logic, not configurations) differently across environments without maintaining separate implementations or complex factories.
The result? Cleaner, more maintainable code with no runtime overhead, strong type safety, and elegant solutions to problems that typically require complex design patterns.
Check out conditional-method on GitHub and soon PyPI - zero dependencies, type-safe, and ready to simplify your conditional implementation needs!
is quite a bit of magic behind the scenes to get it to work at all
only works within classes
probably noticably increases import time (runtime cost should vanish)
has the potential to break in new an exciting ways if you use the same function name more than once across different classes and/or files.
is going to have issues with e.g. property setters
(Some of these are guesses based on reading the source code and my fairly-good-but-not-perfect understanding of python semantics)
But it does complain early if no condition is true which can be a genuine benefit. (By default it doesn’t complain early if more than one condition is true, although I think it should and thr code is there)
Okay, yes, that IS a real benefit. I’m not sure if there’s an easy way to enforce that otherwise. But there’s a lot of complexity for something fairly minor.
Of course the safest way is to use if CONDITION: ..., I am just providing a tool if anyone needs it. If most people don’t need it, can leave the tool untouched.
Anyway, thank you for offering constructive feedback on how property.setter will not work - I will change the selection rule to match Python’s default (the last one is the selected one) and this will allow property.setter to work.
Very nice! One instant thought for me was when I was a student doing performance studies or other experiments, we always had things like “fun_1”, “fun_1_faster”, “fun_1_more_faster”… Okay crude but at the end of the day you wanted to produce a chart with different features turned on and going back in the code could make it painful to reproduce. This seems like a cleaner solution to implement the same functionality for experimentation.
Additionally, I could see feature toggles working with this nicely in a backend.
d:\Temp\conditional-method-main\src>py
Python 3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:34:34) [MSC v.1928 32 bit (Intel)] on win32
Type “help”, “copyright”, “credits” or “license” for more information.
import test
Hello
modify file, change string ‘Hello’ to ‘World’
from importlib import reload
reload(test)
Hello
<module ‘test’ from ‘d:\Temp\conditional-method-main\src\test.py’>
modify file, comment out @conditional_method(condition = True)
reload(test)
World
<module ‘test’ from ‘d:\Temp\conditional-method-main\src\test.py’>
This has more to do with the fact that the reload function can not work in general and should not be relied upon. References to objects/functions/classes from the module that are held outside of the module (as is happening in this case) will not get changed.
The primary use case of your library appears to be of switching code definitions between development and production, which is sort of weird to me.
In my opinion, the core logics between development and production should be kept identical, with the only variables being things like credentials, logging target, directory names, and number of workers, etc., which can all be parameterized and read from a configuration file or service based on the environment settings.
Conditionally defining methods for different environments you like do is almost always going to end up with lots of duplicate code for the core logics in all of the conditionallly defined methods. And it’ll become a maintenance nightmare when you have to update any of the core logics, which you’ll have to apply to all the duplicate codes.
I think you should try coming up with a different use case (what that might be I have no idea of) for the library to be more compelling.
It’s not a new comment and it is also a comment that can be applied to most people’s attempt to write a library of sort.
I appreciate the time you took to pen these words.
I would say, if for your use case, you’re able to have the same logics all environments, differing only on configurations - then you’re right, you don’t have a need for this library.
I would also say that there are use cases for such a library because what I am attempting to create is similar to compilation attribute macros, e.g. #[cfg(..)] in Rust and even the preprocesser macro#DEFINE in C - albeit, these are aimed at different builds on different targets but fundamentally, the idea is similar, you want a different logic when something (build target, environment, etc.) is different.