Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Can anyone suggest best practice alternatives to import side-effects?


Don't run any functions at the root level of your module.

Instead, if you really need long-lasting objects which get initiated once, then use an object, and put any initialisation stuff in it's `__init__` method. Then the module can be imported whenever, but your initialisation stuff is only called when the user of your library creates a new instance of that class.

For bonus points, make your classes able to be used with the `with ...` syntax, so then lifetime is kept to a minimum, and errors/whatever are dealt with by default.

If you really really need to monkey around and take control of the whole python interpreter (gevent, twisted, and possibly some GUI frameworks come to mind...) then don't do that at import time, do it with a `run_forever()` or `take_control` type function.

But yes, a virtualenv for every project does help a lot with not accumulation cruft.


I sometimes make calls to collections.namedtuple and other class building functions at the same time as the rest of my definitions.


If you pass verbose=True to namedtuple(), you can see that it's essentially just defining a new class. This is something that people already do at the top level of a module.


Well, prohibiting all side-effects would be silly and if taken literally, you won't be able to even declare classes and functions, since these get compiled to bytecode that is essentially "instantiate a function object with this code blob", i.e. it's executable code with the side effect of binding a name in the module's scope to a new function object.

In general - do not do anything that might fail, might take a long time (>0.5 seconds), or can't be stopped easily.

Specifically - do not create windows, do not connect to the internet, do not try to create a database in a hardcoded location, do not connect to postgres. Do not do things that will require calls to the operating system other than allocating memory.

The one thing I'm willing to concede would be reading a default configuration file. But only if you're certain you've written it in a way that won't blow up if for whatever reason the default path is not readable for the current user, or in other edge cases.

If your library needs to talk to the OS, it will need to be passed initialization parameters. Just provide a top-level class that the user instantiates and passes all the needed initialization parameters. Resist the urge to do silly things in it like having a module-global instance variable for it that is set when the class is first instantiated and overloading the __new__ method to return that instance if it's not None.


Keep your heavy runtime code and invocations behind "if __name__ == '__main__'" blocks. Alternately, if you're using a framework keep your runtime code constrained to the appropriate entry class/function. Outside of those blocks, your modules should be mostly constants, functions, and classes.


Just don't run code in the root of your modules except in the entry point(s) of your program. In the modules, only declare.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: