path() is equal to Python’s
To add paths in MATLAB, use the obviously named function
addpath(). Supply the optional
-end argument if you don’t want any potential shadowing (i.e. the folder to import has lower priority if there’s an existing function with the same name).
I generally avoid
userpath() or the graphical tools because the results are sticky (persists between sessions). The best way is to exclusively manage your paths with startup.m so you always know what you are getting. If you want full certainty, you can start with
restoredefaultpath() in MATLAB.
Python’s suggested these as equivalents of MATLAB’s
sys.path.insert(0, folder_to_add_to_path) sys.path.append(folder_to_add_to_path)
but just like MATLAB’s
addpath() which works with strings only (not
cellstr), these Python options do not work correctly with Python
lists because the methods in
sys.path are as primitive as doing
- This means you’ll end up with list of lists if you supplied Python lists as inputs to the above
(MATLAB will throw an exception if you try to feed it with
cellstrinstead of polluting your path space with garbage)
- This also means it doesn’t check for duplicates! It’ll keep stacking entries!
To address the first problem, we use
sys.path.extend() instead. It’s like doing
addpath(..., '-end') in MATLAB. If you want it to be inserted at the front (higher priority, shadows existing), you’ll need
sys.path = list_of_new_paths + sys.path. For MATLAB, you can make a path string like DOS by using
sys.path.extend() is still not polymorphic: it expect iterables so if you feed it a string, which Python will consider it a list of characters, you will get a bunch of one character paths inserted!
On the other hand, DO NOT TRY to get around it in Python with the same trick like MATLAB by doing
sys.path.append( ';'.join(path_list)). Python recognize
sys.path as a list, NOT a one long string like MATLAB/Windows
append() accepts only strings!
The second problem (which does NOT happen in MATLAB) is slightly more work. You’ll need to subtract out the existing paths before you add to it so that you won’t drag your system down by casually adding paths as you see fit. One way to do it:
def keep_only_new_set_of_paths(p): return set(p)-set(sys.path)
You should organize your programs and libraries in a directory tree structure and use code to crawl the right branch into a path list! Don’t let the lack of built-in support to tempt you to organize files in a mess. Keep the visuals clean as mental gymnastics/overheads can seriously distract you from the real work such as thinking through the requirements and coming up with the right architecture and data structures. If you constantly need to jump a few hoops to do something, do it only once or twice using the proper way (aka, NOT copying-and-pasting boilerplate code), and reuse the infrastructure.
At my previous workplaces, they had dozens and dozens of MATLAB files including all laying flat in one folder. The first thing I did when I join a new team is showing everybody this idiom that recursively adds everything under the folder into MATLAB paths:
Actually the built-in support for recursive directory search sucks for both MATLAB and Python. Most often what we need is just a list of full paths for a path pattern that we search recursively, basically
dir/w/s *. None of them has this right out of the box. They both make you go through the comprehensive data structure returned (let it be tuples from
os.walk() in Python or
dir() in MATLAB) and so some manipulations to get to this form.
genpath() itself is slow and ugly. It’s basically a recursive wrapper around
dir() that cleans up garbage like
'..'. Instead of getting a newline character, a different row (as a
char array) or a different cell (as
cellstr), you get semi-colons (
pathsep in between. Nonetheless, I still use it because despite I have recursive path tools in my own libraries, I’ll need to load the library first in my startup file, which requires a recursive path tool like
genpath(). This bootstraps me out of a chicken-and-egg problem without too much ugly syntax.
Most people will tell you to do a
os.walk() and use listcomp to get it in the typical full path form, but I’m not settling for distracting syntax like this. People in the community suggested using
glob for a relatively simple alternative to
Here’s a cleaner way:
def list_subfolders_recursively(p): p = p + '/**/' return glob.glob(p, recursive=True);
It’s also worth noting that Python follows Linux’s file search pattern where directory terminates with a filesep (
/) while MATLAB’s
dir() command follows the OS, which in Windows, it’s
Both MATLAB and Python uses
** to mean regardless of levels, but you’ll have to turn on the
** is already implied to be recursive in MATLAB’s
Considering there’s quite a bit of plumbing associated with weak set of
sys.path methods provided in Python, I created a
qpath.py next to my
''' This is the quick and dirty version to bootstrap startup.py Should use files.py that issue direct OS calls for speed''' import sys import glob def list_subfolders_recursively(p): p = p + '/**/' return glob.glob(p, recursive=True); def keep_only_new_set_of_paths(p): return set(p)-set(sys.path) def set_of_new_subfolders_recursively(p): return keep_only_new_set_of_paths( list_subfolders_recursively(p) ) def add_paths_recursively_bottom(p): sys.path.extend(set_of_new_subfolders_recursively(p)); def add_paths_recursively_top(p): # operator+() does not take sets sys.path = list(set_of_new_subfolders_recursively(p)) + sys.path;
In order to be able to import my
qpath module at
startup.py before it adds the path, I’ll have put
qpath.py in the same folder as
startup.py, and request
startup.py to add the folder where it lives to the system path (because your current Python working folder might be different from
PYTHONSTARTUP) so it recognizes
This is the same technique I came up with for managing localized dependencies in MATLAB: I put the dependencies under the calling function’s folder, and use the path of the
.m file for the function as the anchor-path to add paths inside the function. In MATLAB, it’s done this way:
function varargout = f(varargin) anchor_path = fileparts( mfilename('fullpath') ); addpath( genpath(fullfile(anchor_path, 'dependencies')) ); % Body code goes here
- Python has
__file__variable (like the good old preprocessor days in C) in place of
mfilename('fullpath')always gives the absolute path, but Python’s
__file__is absolute if it’s is not in
sys.pathyet, and relative if it’s already in it.
- So to ensure absolute path in Python, apply
os.path.realpath(__file__). Actually this is a difficult feature to implement in MATLAB. It’s solved by a MATLAB FEX entry called GetFullPath().
os.path.dirnameis the direct equivalent of
fileparts()if you just take the first argument.
and in my
startup.py (must be in the same folder as
import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) import pathtool user_library_path = 'D:/Python/Libraries'; pathtool.add_paths_recursively_bottom(user_library_path)
This way I can make sure all the paths are deterministic and none of the depends on where I start Python.
463 total views, 1 views today