The point at which you import the module is not expected to result in poor performance if it bothers you. Modules are single point and will not be import ed every time an import statement is encountered. However, the way you do the import and subsequent attribute checks is affected.
For example, if you import math , and then every time you need to use the sin(...) function, you must execute math.sin(...) , it will usually be slower than doing from math import sin and using sin(...) directly, as the system does not need to search for the function name in the module.
This viewing penalty applies to everything that is accessed using a period . and will be especially noticeable in the loop. Therefore, it is recommended to get a local link to something that you may need to use / call frequently in the critical loop / performance section.
For example, using the original import math example, right in front of the critical loop, you can do something like this:
# ... within some function sin = math.sin for i in range(0, REALLY_BIG_NUMBER): x = sin(i) # faster than: x = math.sin(x) # ...
This is a trivial example, but note that you can do something similar with methods for other objects (e.g. lists, dictionaries, etc.).
I'm probably a little more worried about the circular imports that you mention. If you intend to “commit” cyclic imports by moving import operations to more “local” places (for example, inside a specific function or code block, etc.), you probably have a deeper problem that you need to solve.
Personally, I would save the import at the top of the module, as usual. Deviating from this template without a good reason is likely to make your code more complex, because the dependencies of your module will not be immediately obvious (i.e. there are import statements scattered throughout the code, and not in one place).
It can also cause a cyclical dependency problem, which you find it harder to debug and easier to fall into. After all, if the module is not listed above, someone might be happy to think that your module A does not depend on module B , and then adds import A to B when A already has import B hidden in some deep dark the corner.
Performance example
Here, a reference using search notation is used:
>>> timeit('for i in range(0, 10000): x = math.sin(i)', setup='import math', number=50000) 89.7203312900001
And another test that doesn't use search notation:
>>> timeit('for i in range(0, 10000): x = sin(i)', setup='from math import sin', number=50000) 78.27029322999988
Here the difference is 10+ seconds.
Please note that your gain depends on how much time the program spends to execute this code --ie is a critical section of performance, not sporadic function calls.