list comprehension to do os.path.split_all ?

G

gry

[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

I expect I can do this with some simple loop, but I have such faith in
the wonderfulness of list comprehensions, that it seems like there
should be a way to use them for an elegant solution of my problem.
I can't quite work it out. Any brilliant ideas? (or other elegant
solutions to the problem?)

-- George
 
S

Stefaan Himpe

Hi,
[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

Not sure what your exact requirements are, but the following seems to work:

pathname = '/home/gyoung/hacks/pathhack/foo.py'
print pathname[1:].split("/")

Note that this would only work for absolute linux paths (i.e. starting
with "/").

Best regards,
Stefaan.
 
N

Neil Cerutti

[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's
no os.path.split_all function.

I expect I can do this with some simple loop, but I have such
faith in the wonderfulness of list comprehensions, that it
seems like there should be a way to use them for an elegant
solution of my problem. I can't quite work it out. Any
brilliant ideas? (or other elegant solutions to the problem?)

If an elegant solution doesn't occur to me right away, then I
first compose the most obvious solution I can think of. Finally,
I refactor it until elegance is either achieved or imagined.
 
A

Alan Meyer

[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

I expect I can do this with some simple loop, but I have such faith in
the wonderfulness of list comprehensions, that it seems like there
should be a way to use them for an elegant solution of my problem.
I can't quite work it out. Any brilliant ideas? (or other elegant
solutions to the problem?)

-- George

This is not properly portable to all OS, but you could simply split on
the slash character, e.g.,

pathname.split('/')

Alan
 
I

Ian Kelly

[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
  '/home/gyoung/hacks/pathhack/foo.py'  -->  ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

I expect I can do this with some simple loop, but I have such faith in
the wonderfulness of list comprehensions, that it seems like there
should be a way to use them for an elegant solution of my problem.
I can't quite work it out.  Any brilliant ideas?   (or other elegant
solutions to the problem?)

path = '/home/gyoung/hacks/pathhack/foo.py'
parts = [part for path, part in iter(lambda: os.path.split(path), ('/', ''))]
parts.reverse()
print parts

But that's horrendously ugly. Just write a generator with a while
loop or something.
 
I

Ian Kelly

path = '/home/gyoung/hacks/pathhack/foo.py'
parts = [part for path, part in iter(lambda: os.path.split(path), ('/',''))]
parts.reverse()
print parts

But that's horrendously ugly.  Just write a generator with a while
loop or something.

Also, note that if the path does not start with '/', the result will
be an infinite loop.
 
E

Ethan Furman

Neil said:
If an elegant solution doesn't occur to me right away, then I
first compose the most obvious solution I can think of. Finally,
I refactor it until elegance is either achieved or imagined.

+1 QOTW
 
I

Ian Kelly

path = '/home/gyoung/hacks/pathhack/foo.py'
parts = [part for path, part in iter(lambda: os.path.split(path), ('/', ''))]
parts.reverse()
print parts

But that's horrendously ugly.  Just write a generator with a while
loop or something.

Also, note that if the path does not start with '/', the result will
be an infinite loop.

Oh, and this won't work at all in Python 3, because it relies on the
lambda having access to the list comprehension scope.
 
A

Alexander Kapps

[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

I expect I can do this with some simple loop, but I have such faith in
the wonderfulness of list comprehensions, that it seems like there
should be a way to use them for an elegant solution of my problem.
I can't quite work it out. Any brilliant ideas? (or other elegant
solutions to the problem?)

path = '/home/gyoung/hacks/pathhack/foo.py'
parts = [part for path, part in iter(lambda: os.path.split(path), ('/', ''))]
parts.reverse()
print parts

But that's horrendously ugly. Just write a generator with a while
loop or something.


pathname = '/home/gyoung/hacks/pathhack/foo.py'
parts = [part for part in pathname.split(os.path.sep) if part]
print parts

['home', 'gyoung', 'hacks', 'pathhack', 'foo.py']
 
E

Emile van Sebille

On 7/28/2011 1:18 PM gry said...
[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
'/home/gyoung/hacks/pathhack/foo.py' --> ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

Why not just split?

'/home/gyoung/hacks/pathhack/foo.py'.split(os.sep)

Emile
 
I

Ian Kelly

On 7/28/2011 1:18 PM gry said...
[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
   '/home/gyoung/hacks/pathhack/foo.py'  -->   ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

Why not just split?

'/home/gyoung/hacks/pathhack/foo.py'.split(os.sep)

Using os.sep doesn't make it cross-platform. On Windows:
os.path.split(r'C:\windows') ('C:\\', 'windows')
os.path.split(r'C:/windows') ('C:/', 'windows')
r'C:\windows'.split(os.sep) ['C:', 'windows']
r'C:/windows'.split(os.sep)
['C:/windows']
 
N

Neil Cerutti

Using os.sep doesn't make it cross-platform. On Windows:
os.path.split(r'C:\windows') ('C:\\', 'windows')
os.path.split(r'C:/windows') ('C:/', 'windows')
r'C:\windows'.split(os.sep) ['C:', 'windows']
r'C:/windows'.split(os.sep)
['C:/windows']

Fine... So normpath it first...
os.path.normpath(r'C:/windows').split(os.sep) ['C:', 'windows']

Here's a solution adapted from an initially recursive attempt.
The tests are currently somewhat gnarled to avoid displaying
os.path.sep. A simpler solution probably reimplement os.path.split,
an inconvenient implementation detail.

import os

def split_path(path):
"""Split path into a series of directory names, and return it
as a list.

If path is absolute, the first element in the list will be be
os.path.sep.
p = split_path('/smith/jones')
p[0] == os.path.sep True
p[1:]
['smith', 'jones']
['smith', 'jones']
[]
p = split_path('/')
p[0] == os.path.sep True
len(p)
1
"""
head, tail = os.path.split(path)
retval = []
while tail != '':
retval.append(tail)
head, tail = os.path.split(head)
else:
if os.path.isabs(path):
retval.append(os.path.sep)
return list(reversed(retval))

if __name__ == '__main__':
import doctest
doctest.testmod()
 
T

TheSaint

Alan said:
This is not properly portable to all OS, but you could simply split on
the slash character, e.g.,

pathname.split('/')

more portable pathname.split(os.sep)
 
C

Carl Banks

On 7/28/2011 1:18 PM gry said...
[python 2.7] I have a (linux) pathname that I'd like to split
completely into a list of components, e.g.:
   '/home/gyoung/hacks/pathhack/foo.py'  -->   ['home', 'gyoung',
'hacks', 'pathhack', 'foo.py']

os.path.split gives me a tuple of dirname,basename, but there's no
os.path.split_all function.

Why not just split?

'/home/gyoung/hacks/pathhack/foo.py'.split(os.sep)

Using os.sep doesn't make it cross-platform. On Windows:
os.path.split(r'C:\windows') ('C:\\', 'windows')
os.path.split(r'C:/windows') ('C:/', 'windows')
r'C:\windows'.split(os.sep) ['C:', 'windows']
r'C:/windows'.split(os.sep)
['C:/windows']

It's not even fullproof on Unix.

'/home//h1122/bin///ghi/'.split('/')

['','home','','bin','','','ghi','']

The whole point of the os.path functions are to take care of whatever oddities there are in the path system. When you use string manipulation to manipulate paths, you bypass all of that and leave yourself open to those oddities, and then you find your applications break when a user enters a doubledslash.

So stick to os.path.


Carl Banks
 
A

Alexander Kapps

It's not even fullproof on Unix.

'/home//h1122/bin///ghi/'.split('/')

['','home','','bin','','','ghi','']

The whole point of the os.path functions are to take care of whatever oddities there are in the path system. When you use string manipulation to manipulate paths, you bypass all of that and leave yourself open to those oddities, and then you find your applications break when a user enters a doubled slash.

So stick to os.path.


Carl Banks

This would also be fixed with normpath() as Dennis Lee Bieber
suggested. And my solution with list comprehensions handles this too.

Still, there might be other path oddities which would break here. I
think, that something like a split_all() function should be
available in the stdlib, no?

Actually, it isn't the first time, where I wonder why
os.path.split() doesn't do this already. I mean, str.split() doesn't
only split on the first part, right?
 
M

Michael Poeltl

* Alexander Kapps said:
It's not even fullproof on Unix.

'/home//h1122/bin///ghi/'.split('/')

['','home','','bin','','','ghi',''] what about this?
' '.join('/home//h1122/bin///ghi/'.split('/')).split() ['home', 'h1122', 'bin', 'ghi']
;-)
regards
Michael
Carl Banks

This would also be fixed with normpath() as Dennis Lee Bieber suggested.
And my solution with list comprehensions handles this too.

Still, there might be other path oddities which would break here. I
think, that something like a split_all() function should be available in
the stdlib, no?

Actually, it isn't the first time, where I wonder why os.path.split()
doesn't do this already. I mean, str.split() doesn't only split on the
first part, right?
 
S

Steven D'Aprano

Carl said:
It's not even fullproof on Unix.

'/home//h1122/bin///ghi/'.split('/')

['','home','','bin','','','ghi','']


What? No. Absolutely not -- that would be a major bug. Did you actually try
it?
['', 'home', '', 'h1122', 'bin', '', '', 'ghi', '']

Exactly as expected, and correct.

Now obviously if you intend treating this as a list of path components, you
have to filter out the empty strings, but otherwise works fine.
 
D

Dennis Lee Bieber

Actually, it isn't the first time, where I wonder why
os.path.split() doesn't do this already. I mean, str.split() doesn't
only split on the first part, right?

But there is a conceptual difference... The last element of a path
is a file (or directory name) contained within the preceding path name
portion.

But a string has no context...
 
H

Hrvoje Niksic

Neil Cerutti said:
Fine... So normpath it first...
os.path.normpath(r'C:/windows').split(os.sep)
['C:', 'windows']

That apparently doesn't distinguish between r'C:\windows' and
r'C:windows'. On Windows the first is an absolute path, the second a
relative path, and both contain a drive letter.
while tail != '':
retval.append(tail)
head, tail = os.path.split(head)
else:
if os.path.isabs(path):
retval.append(os.path.sep)
return list(reversed(retval))

Note that using 'else' after 'while' is superfluous if the loop doesn't
contain a 'break' statement.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top