6 ms·
In Python I sometimes miss "header files". Quite often you see: import x class Foo: def bar(self): import y # break circular impo
by stabbles 3y ago
In Python I sometimes miss "header files". Quite often you see:
import x
class Foo:
def bar(self):
import y # break circular import
y.something()
Would be nice to be able to import `Foo` without pulling in also `y`, or moving `y` inline.
Can be solved in different ways, but you see inline imports everywhere.
- colejohnson66 3y agoThat's not a problem with modules, per se, but a problem with Python's module system. Importing a module in Python just executes the whole file. JavaScript used to have this issue with `require` years ago. Each `require` statement essentially executed that import right then and there, and would bug out if circular dependencies existed. However, they fixed it with `import`. Almost every other module-based language does not have issues with circular dependencies. Python, in theory, could follow their lead, but they won't. EDIT: I wasn't as clear as I could be. The issue again isn't modules, but scripting languages that allow top-level statements at all. Intermixing types and method declarations with executable code makes circular decencies an issue. Compiled languages don't allow top-level statements, so they don't have the dependency resolution problem.
- paulddraper 3y ago> Each `require` statement essentially executed that import right then and there That still happens. It takes a lot of magic trickery to make cyclical require/imports work for JavaScript and a lot of times they don't/can't.
- colejohnson66 3y agoThat's true. I guess I misspoke and wasn't as clear as I could be. What I meant to say was: the problem with Python and JavaScript's module system isn't modules themselves, but top-level statements. You are correct that `import` doesn't solve such a problem; It solves the synchronous dependency resolution problem. Realistically, that's just what happens when a language allows top-level statements, as they are executed when a file is loaded. As such, scripting languages tend to fall victim to the problem, but compiled ones don't. I'll update my comment.
- rezonant 3y agoWorth noting that ES-style imports do improve the scenario for cyclic dependencies though, assuming you do not need to reference the symbols imported during the execution of the top level statements. This is because `import { bar } from './bar.js'` makes `bar` a "live binding" -- even if `bar.js` also imports `foo.js` and thus `bar` may initially be undefined due to the cyclic dependency, later when `bar.js` completes its initialization of `bar`, the identifier `bar` in `foo.js` will be updated to match. So if you just need to call that binding within, say, a function or a method call, its typically fine to do so assuming that by the time that function or method call is executed, both modules will have completed their initializations.
- paulddraper 3y agoAnd that is similar to require's behavior for properties on the returned object. const foo = require("foo"); // starts out as empty object exports.bar = function() { return foo.fn(); }
- rezonant 3y agoYes but the difference is that this: let x = require('foo').x of course, cannot do that. It's a small difference but it does make it easier to fall into the happy path in more cases.
- paulddraper 3y ago> Realistically, that's just what happens when a language allows top-level statements, as they are executed when a file is loaded. As such, scripting languages tend to fall victim to the problem, but compiled ones don't. Lots of languages allow initialization code (e.g. Java static initializer). But in the case of scripting languages, usually the classes and functions themselves are initialization code....i.e. everything is initialization code.
- bbkane 3y agoGo forbids cyclical imports, and, in most cases, I really appreciate it. Makes the code much simpler to reason about
- colejohnson66 3y agoAs someone who doesn't use Go, how would one handle a situation where two modules actually depend on each other? For example, a parent object owning a child object is normal, but what if that child object sometimes needs a (weak) reference to the parent?
- nikbackm 3y agoJust put them in the same module?
- dale_glass 3y agoWhat if the child is an user-implemented piece of code and the parent is in a library?
- Kranar 3y agoThen there wouldn't be a circular dependency. If the parent is in a library and was written independently of the child, then the parent could not possibly import the child.
- lelanthran 3y ago> Almost every other module-based language does not have issues with circular dependencies. I recall having seen a circular dependency compiler error in Go.
- melagonster 3y agowait, is this even possible? I just presume that all import statements are executed first.
- jpc0 3y agoIronically you can solve this with header guards in python as well... Old problem requires old solution