Necromancy engaged
[ONLY TESTED IN PYTHON 3.7]
I use:
os.sep.join(__file__.split(os.sep)[:-1])
And I prefer it over the other solutions I've seen provided. Some test code I wrote:
from timeit import Timer
from typing import List, Tuple
N = 100000
TIMEITOUT = [None]
def _p_timeit(stmt: str, setup: str = ""):
tte = Timer(f"TIMEITOUT[0]={stmt}", setup=f"{setup};from __main__ import TIMEITOUT").timeit(N)
print(f"{stmt:<54}|{tte:>17.10f} [output]: \"{TIMEITOUT[0]}\"")
return stmt, tte
def _p_header():
print(f"Testing w/ number={N} iterations\n{'=' * 72}")
print(f"{'Statement':<54}|{'Time':^17}\n{'-' * 72}")
def _p_compare(a: Tuple[str, float], b_l: List[Tuple[str, float]]):
print(f"\n{'=' * 72}\nComparing {a[0]} to all other statements:\n{'-' * 72}")
for b in b_l:
d = (b[1]-a[1])/a[1]
cmp = f"faster than" if d > 0 else f"slower than" if d < 0 else f"equally (t={a[1]}) as fast as"
print(f"{a[0]} was {(abs(d)*100):.2f}% {cmp} {b[0]}")
_p_header()
my_suggestion = _p_timeit("os.sep.join(__file__.split(os.sep)[:-1])", setup="import os")
others = [_p_timeit("os.path.abspath(os.path.dirname(__file__))", setup="import os"),
_p_timeit("Path(__file__).parent", setup="from pathlib import Path"),
_p_timeit("Path(__file__).resolve().parent", setup="from pathlib import Path")]
_p_compare(my_suggestion, others)
Output:
Testing w/ number=100000 iterations
========================================================================
Statement | Time
------------------------------------------------------------------------
os.sep.join(__file__.split(os.sep)[:-1]) | 0.0640765000 [output]: "C:\Users\abrewer\AppData\Local\Programs\Python\Python37\lib"
os.path.abspath(os.path.dirname(__file__)) | 0.6156060000 [output]: "C:\Users\abrewer\AppData\Local\Programs\Python\Python37\lib"
Path(__file__).parent | 0.7117664000 [output]: "C:\Users\abrewer\AppData\Local\Programs\Python\Python37\lib"
Path(__file__).resolve().parent | 9.3563913000 [output]: "C:\Users\abrewer\AppData\Local\Programs\Python\Python37\Lib"
========================================================================
Comparing os.sep.join(__file__.split(os.sep)[:-1]) to all other statements:
------------------------------------------------------------------------
os.sep.join(__file__.split(os.sep)[:-1]) was 860.74% faster than os.path.abspath(os.path.dirname(__file__))
os.sep.join(__file__.split(os.sep)[:-1]) was 1010.81% faster than Path(__file__).parent
os.sep.join(__file__.split(os.sep)[:-1]) was 14501.91% faster than Path(__file__).resolve().parent
Keep in mind that if __file__ returns a string using any separator (ex: os.altsep) other than os.sep, the program will fail. I haven't had this affect me, but who knows these days...
You could always find the separator in the string first if need be, then use that instead of os.sep. The speed improvements would, as per time complexity standards (time to find the separator is essentially constant), remain.
__file__
is NOT defined in all cases, e.g. statically linked C modules. We can't count on__file__
always being available. – Smalls