3

Here's my first Python program, a little utility that converts from a Unix octal code for file permissions to the symbolic form:

s=raw_input("Octal?  ");
digits=[int(s[0]),int(s[1]),int(s[2])];
lookup=['','x','w','wx','r','rx','rw','rwx'];
uout='u='+lookup[digits[0]];
gout='g='+lookup[digits[1]];
oout='o='+lookup[digits[2]];
print(uout+','+gout+','+oout);

Are there ways to shorten this code that take advantage of some kind of "list processing"? For example, to apply the int function all at once to all three characters of s without having to do explicit indexing. And to index into lookup using the whole list digits at once?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
murray
  • 737
  • 2
  • 10
  • 28
  • 2
    Are you purposely wanting to use `;` at the end of every line in this script? This is *Python*, [after all](http://stackoverflow.com/a/8236402/1167750)... :) – summea May 23 '13 at 19:39
  • 2
    Entirely for fun (don't use this), here is a one-liner: `print(','.join(c+'='+''.join('xw r'[i-1:i&b] for i in (4,2,1)) for c,b in zip('ugo',map(int,raw_input("Octal? ")))))` – Andrew Clark May 23 '13 at 19:51

5 Answers5

11
digits=[int(s[0]),int(s[1]),int(s[2])];

can be written as:

digits = map(int,s)

or:

digits = [ int(x) for x in s ]  #list comprehension

As it looks like you might be using python3.x (or planning on using it in the future based on your function-like print usage), you may want to opt for the list-comprehension unless you want to dig in further and use zip as demonstrated by one of the later answers.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    +1 for using both [map](http://docs.python.org/2/library/functions.html#map) and [list comprehension](http://docs.python.org/2/tutorial/datastructures.html#list-comprehensions) – Andy May 23 '13 at 19:44
  • 2
    OP's using `raw_input`, so it's 2.X – Blender May 23 '13 at 19:52
  • @Blender -- Good point. I hadn't noticed that (I just saw the function-like print statement). – mgilson May 23 '13 at 19:53
  • Aha! `map` was precisely the sort of function I was looking for, something I'm used to using in *Mathematica* and *J*. – murray May 23 '13 at 20:10
  • @Blender: I'm still stumbling around a bit here, and the explicit reading of input was while testing my program interactively (in Spyder). What I'll do is make this an executable script, and for that I'll use `sys.argv[1]` to obtain the input directly from the command line, instead of prompting for and reading input. – murray May 23 '13 at 20:14
5

Here is a slightly optimized version of your code:

s = raw_input("Octal?  ")
digits = map(int, s)
lookup = ['','x','w','wx','r','rx','rw','rwx']
perms = [lookup[d] for d in digits]
rights = ['{}={}'.format(*x) for x in zip('ugo', perms)]
print ','.join(rights)
Lev Levitsky
  • 63,701
  • 20
  • 147
  • 175
  • That just taught me more about Python than a whole lot of searching around through docs. (And of course I'm impatient enough not wanting to go through the typical tedious introductory tutorials I've seen.) – murray May 23 '13 at 20:16
  • Oops: I'm not seeing the "u=", etc., parts when I run that code interactively using Python 2.7. And the same thing if I execute it as a script under Python 3.3, replacing the `raw_input` line with `import sys` and then `s=sys.argv[1]`, and the last line with `print(','.join(perms))`. – murray May 23 '13 at 22:30
4

You can also do it with bitmasks:

masks = {
    0b100: 'r',  # 4
    0b010: 'x',  # 2
    0b001: 'w'   # 1
}

octal = raw_input('Octal? ')
result = '-'

for digit in octal[1:]:
    for mask, letter in sorted(masks.items(), reverse=True):
        if int(digit, 8) & mask:
            result += letter
        else:
            result += '-'

print result
Blender
  • 289,723
  • 53
  • 439
  • 496
2

Here's my version, inspired by Blender's solution:

bits = zip([4, 2, 1], "rwx")
groups = "ugo"

s = raw_input("Octal?  ");
digits = map(int, s)

parts = []
for group, digit in zip(groups, digits):
    letters = [letter for bit, letter in bits if digit & bit]
    parts.append("{0}={1}".format(group, "".join(letters)))

print ",".join(parts)

I think it's better not to have to explicitly enter the lookup list.

Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • "Six of one, half dozen of the other"?? Terser code if one forms the `lookup` list vs. longer code but longer clode while closer to the spirit of how permissions are calculated with the `bits` manipulation. I like little problems that are so rich in possible solutions. Thanks to all contributors here. – murray May 24 '13 at 04:01
1

Here's my crack at it (including '-' for missing permissions):

lookup = {
    0b000 : '---',
    0b001 : '--x',
    0b010 : '-w-',
    0b011 : '-wx',
    0b100 : 'r--',
    0b101 : 'r-x',
    0b110 : 'rw-',
    0b111 : 'rwx'
}

s = raw_input('octal?: ')
print(','.join( # using ',' as the delimiter
               r + '=' + lookup[int(n, 8)] # the letter followed by the permissions
               for n, r  in zip(tuple(s), 'ugo'))) # for each number/ letter pair
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174