while python install packages globally and only globally. so a virtualenv is needed to isolate different projects from one another
pip install -r requirements.txt -t libs
and added the folder to PYTHONPATH. I never bothered with virtualenv.
this is why you need to activate virtualenv every time you open a new command line, so that it can set ENV variables for the current session and force python to use the locally scoped folders.
While in C# et all. the compiler knows to check files locally. and you don't need to activate (or deactivate) environment at all.
Python isn't at all the only language to suffer this problem. e.g. "DLL hell" and its variations like Haskell's "cabal hell". With Ruby, if its packages are installed with bundler, then these packages are installed locally in the project directory.
But it's not as quick and clean as "python -m venv venv", so everyone uses that (and pyenv for different versions).