Python help

Erik Price erikprice at mac.com
Thu Feb 5 17:08:58 EST 2004


Paul Lussier wrote:

>I'd like to see/hear others ideas on how to write this same script.
>I'm most interested in improvements or commentary on why what I did
>is either right, wrong, interesting, stupid, etc.


Below you'll find my stab at it -- it might not be correct, or even run, b/c I haven't actually got an /etc/group file to try it out on right now.  There's a couple "best practices" for Python that I've picked up over the past couple of years that I used, too:

1. Unless you're literally writing a one-shot throwaway script to do some task that you'll probably never do again, try to write your script as a set of functions or classes rather than a start-to-finish script.  This is because every Python script can be imported into any other Python script as a module (same can be done with Perl using a package declaration if I'm not mistaken) by using "import myscript" where the script file is named "myscript.py" and is located somewhere on your PYTHONPATH (an environment variable that tells the Python interpreter where to search for modules to import, that by default includes only the current working directory).

2. If you're doing the above, then the way you actually invoke your script is by providing a "main" method (or any other name you prefer).  Then, the only part of your script that is *not* located in a function or class definition is the line "if __name__ == '__main__': main()", which simply invokes the main() function you defined if the name of the magic __name__ variable is "__main__".  If you are invoking the script directly (rather than importing it into another script as a module), then this conditional test will prove true, and your main() function will be executed.

3. I read somewhere that a cool trick is setting the arguments of a Python script as the default arguments of main() if it is not passed any arguments explicitly.  The advantage of this is that, if you're debugging your script in the interactive interpreter, you can call the main() function on your script (by importing "myscript" and then calling "myscript.main()") and pass it a list of any arbitrary arguments you like.  If you pass arguments into main(), they will be used instead of sys.argv (which is good because in the interactive interpreter, there are no arguments in sys.argv).

4. I know that a class can be used as a struct in the way you were using, but I don't generally use it as such -- the reason I use Python is because it's very straightforward and easy to read, and I find that such a use of a Python class is a little un-obvious.  In fact, that technique is often used only to show how Python lets you dynamically create object attributes at runtime, though I have seen it used in real-life Python scripts too.  For my approach, I created a "Group" class in my version of your script, which of course you can now use in any Python script that imports your script by referencing it as "myscript.Group".  A dict is a more straightforward way to use a simple struct-like object, as Cole suggested.  The overhead of using a class over a dict is not very significant, though, since Python's classes are really just customized dict types behind the scenes.  But you don't need to know that.  And because dicts are used so extensively in Python, they are ultra-optimized by the Python developers.  (Also, if performance is a big concern, then you can always write some of the code in C and then import it into your Python script as a module.)

5. I try to bundle up behavior that is specific to an object into the class that defines that object, rather than act upon the object from the outside.  I tried to do that here, by providing a "toLdifFormat()" method on the Group class.  The advantage to this is that it simplifies the code that's making use of the Group objects.  Also, returning a string from this method rather than printing it makes it more flexible, since you can always print it out later but maybe you wanted to write it to a file or a database -- in that case, having it in string format is handier.  Likewise, the constructor to the Group class lets you set the name, gid, and members manually (useful if you're debugging the script from the interactive interpreter, or perhaps if using Group from another script), but if you don't, it just sets them to empty strings.  In that case you can use the convenience method "setValuesFromLine" and pass in a line read from a /etc/groups file.  Again, this is Group-specific behavior so it's nice to stuff this code in with the Group class where it can be used anywhere the Group class is used.

Anyway, those are just some thoughts on how I implemented your same script in Python.  In fact, most of my simpler Python scripts end up looking a lot like this -- they consist of one or more classes that define the objects in question, and then a simple main() function that actually performs the processing using these objects.  Although this is a little more verbose than your original script, you might find it a little easier to re-use in other scripts, too.

Good luck in studying Python!  Always glad to see this language in use.


Erik





#!/sw/bin/python

import os, sys


class Group:
    
    def __init__(self, name="", gid="", members=[]):
        self.name = name
        self.gid = gid
        self.members = members
        
    def setValuesFromFileLine(self, line):
        record = line.split(":")
        self.name = record[0]
        self.gid = record[2]
        self.members = record[3].split(",")
        
    def toLdifFormat(self):
        self.members.sort()
        output = ",".join(members)
        return "%s:%s:%s" % (self.name, self.gid, output)



def main(args=sys.argv):
    f = file(args[1], "r")
    groups = []
    
    for line in f:
        g = Group()
        g.setValuesFromFileLine(line)
        groups_dict[g.name] = g
    
    f.close()
    
    for group in groups:
        print group.toLdifFormat()


if __name__ == "__main__":
    main()






More information about the gnhlug-discuss mailing list