Re: Python help
On Feb 8, 2004, at 9:31 PM, [EMAIL PROTECTED] wrote: Java requires even more verbosity. This is my general impression of Java. Is the verbosity a good thing or not? It seems verbose to the point of redundancy. Is this helpful, or does it just get in the way? The answer to that is that it's a matter of perspective, but I don't think that Java would be as successful as it is if a majority of people found it -too- redundant. There's no doubt about it -- the verbosity of Java is overkill for simpler tasks like what you've accomplished with your Python script, especially when there are languages like Python, Perl, Ruby, and bash which can make this kind of thing a lot easier. And, if you know you'll only ever invoke the program on a Unix machine, you can do what another poster suggested and simply glue a bunch of existing Unix tools together like awk, sed, grep, find, et cetera, reducing the burden of actually programming the task to a level of merely asking other tools to perform certain actions on the text. But when things get a bit more complicated, this verbosity can be helpful. (see below) It took longer to write, even though I had already prototyped the design in Python (the two designs are nearly identical), Was it just the verbosity of Java which made it take so long? Yes and no. No if you mean did the verbosity take longer to type. Yes, because Java requires a great deal more syntax to say the same thing that can be said with less syntax in Python, and subtle issues surfaced when trying to compile the code and then run it. For instance, there were numerous times I was attempting to use a class without having imported it first. Another thing is that the verbosity of Java means that there is more text to be conscious of using correctly, so that right there leads to greater potential to make a mistake. I wrote that Java implementation in a text editor, which is a useable but relatively primitive tool for a language that can be as verbose as Java. It would have been faster to use an IDE. At work we use WSAD, which speeds development by automatically importing any classes I attempt to use, automatic method completion, popup API documentation (sparing me a trip to the docs), realtime compiler error-checking, and other luxuries. So, using a text editor, I'd make a change and then jump back to the shell and type the command to compile the file. This doesn't take long, because I use keyboard shortcuts and the command history, but then I have to examine the compiler output when there's a problem and jump back to the text editor and fix the mistake -- an IDE will highlight the erroneous code, making it much faster to figure out what you've done wrong. But Python offers the interactive interpreter, which is a godsend when trying to debug a problem or even just sketching out the script. If you write a python script and invoke it with the -i option (python -i scriptname), then after the script completes you are automatically dumped into the interactive interpreter and any variables in your script are now local to your python session so you can invoke functions using arbitrary arguments, evaluate the values of variables, and other conveniences. (Yeah, I know that Real Programmers (tm) use vi/emacs/ed, but Real Programmers also don't consider Java a Real Programming Language. ;) and IMHO would also be more work to modify/extend. That said, if handed a several million-line application written by some other development team, I would rather the application be written in Java than Python. Why? Performance, cleaner code, more robust language? What makes Java better than Python for some things? What types of things is Java best at? In this particular case, the reason I say that is because Java is a statically typed language, and Python is dynamically typed. There are a lot of arguments about which is better, and I won't say one is better than the other for all occasions -- but I happen to find a statically typed language like Java to be easier to read once the application exceeds a certain level of complexity. I anticipate some dissent on this topic, mind you. But when I'm trying to navigate my way through a twisted legacy framework of poorly-written source code to find a bug, it's nice to see the type declarations reminding me that foo is a FileManager and bar is a BufferedXmlMessage. Of course, static type declarations are a pain in the ass in smaller programs, or programs that I'll be writing entirely myself, since I can't enjoy such flexibilities as this: for item in iterableSequence: item.doSomething() Where iterableSequence could be a reference to a file object (so item would be a line), or a database resultset object (so item would be a row), or an object representing a list of users (so item would be a user), etc. Unlike Java, Python doesn't require that your objects actually declare that they implement a specific
Re: Python help
In a message dated: Sat, 07 Feb 2004 09:33:19 EST Erik Price said: Here's the Java implementation. Very cool! Thanks! Java requires even more verbosity. This is my general impression of Java. Is the verbosity a good thing or not? It seems verbose to the point of redundancy. Is this helpful, or does it just get in the way? It took longer to write, even though I had already prototyped the design in Python (the two designs are nearly identical), Was it just the verbosity of Java which made it take so long? and IMHO would also be more work to modify/extend. That said, if handed a several million-line application written by some other development team, I would rather the application be written in Java than Python. Why? Performance, cleaner code, more robust language? What makes Java better than Python for some things? What types of things is Java best at? import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; This almost seems rediculous :) 9 library imports, it seems that some should be so commonly used that they'd just be built-in or at least combined into something like stdio. Thanks a bunch. Part of trouble with learning languages is the lack of real world applications to try them out with. One reason I know perl so well is because the language is designed to do exactly what I do all the time; text munging. Something C and Java aren't especially efficient at it seems :) -- Seeya, Paul -- Key fingerprint = 1660 FECC 5D21 D286 F853 E808 BB07 9239 53F1 28EE It may look like I'm just sitting here doing nothing, but I'm really actively waiting for all my problems to go away. If you're not having fun, you're not doing it right! ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
On Feb 5, 2004, at 10:47 AM, Paul Lussier wrote: I'm not overly interesting in shell, perl, tcl, or other language solutions to this problem, since I already know how to write this in the first 3. (a java or c implementation might be interesting :) Here's the Java implementation. You can see that the way I went about it is just like the Python version, except Java requires even more verbosity. It took longer to write, even though I had already prototyped the design in Python (the two designs are nearly identical), and IMHO would also be more work to modify/extend. That said, if handed a several million-line application written by some other development team, I would rather the application be written in Java than Python. import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; class Group { private static final int NAME_INDEX = 0; private static final int GID_INDEX = 2; private static final int MEMBERS_INDEX = 3; private String name; private int gid; private List members; Group(String name, int gid, List members) { this.name = name; this.gid = gid; this.members = members; } Group (String lineFromFile) { String[] record = lineFromFile.split(:); this.name = record[NAME_INDEX]; this.gid = Integer.parseInt(record[GID_INDEX]); if (hasMembers(record)) { String[] membersArray = record[MEMBERS_INDEX].split(,); this.members = Arrays.asList(membersArray); } } private boolean hasMembers(String[] record) { return (record.length == MEMBERS_INDEX + 1); } String toLdifFormat() { String memberString = ; if (this.members != null) { SortedSet sorted = new TreeSet(this.members); for (Iterator i = sorted.iterator(); i.hasNext();) { memberString += (String)i.next(); if (i.hasNext()) { memberString += ,; } } } return this.name + : + this.gid + : + memberString; } } public class EtcGroupToLdif { public static void main(String[] args) throws IOException { File etcGroup = new File(args[0]); BufferedReader br = new BufferedReader(new FileReader(etcGroup)); String line; while ((line = br.readLine()) != null) { if (line.startsWith(#)) { continue; } Group group = new Group(line); System.out.println(group.toLdifFormat()); } br.close(); } } ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
On Thursday, February 05, 2004, at 06:14PM, Cole Tuininga [EMAIL PROTECTED] wrote: This is a very subtle one. The point is that in a method definition within an object, you never want to assign a mutable type (namely, the empty list) to an argument. Weird behavior will occur. I totally forgot about that! Excellent observation. According to the language reference, it's because: explanation src =http://python.org/doc/current/ref/function.html; Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that that same ``pre-computed'' value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified. This is generally not what was intended. /explanation def main(args=sys.argv): One point about the above line - if you're testing from a python prompt, you'll need to pass your arguments in as a list. So for instance, main( [/etc/group] ) rather than just main ( /etc/group ) Otherwise, the next line would generate some rather peculiar behavior. f = file(args[1], r) Yes, exactly. Although, I think you meant that the file name should be in the second element of the list passed into the main() function, because in sys.argv the first element (sys.argv[0]) is the script name, and arguments start at sys.argv[1]. Also, below, it looks like you started out wanting to use a list, but switched to a dict: Yes, I started out by reproducing Paul's original script (which used a dict) exactly, and then I refactored the dict to a list when I realized that the main() function didn't really need to know the Group object's name anymore. I should have tested this code, but I was in a rush. These items aren't meant as bashing - I know you said you wrote it without testing. I'm just trying to clarify. 8) Not at all. Thanks for your input, esp about the mutable default argument in a class method declaration. And, my apologies to the list for sending the email thrice; mail client problems. Erik ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
On Fri, 2004-02-06 at 09:12, Erik Price wrote: def main(args=sys.argv): One point about the above line - if you're testing from a python prompt, you'll need to pass your arguments in as a list. So for instance, main( [/etc/group] ) rather than just main ( /etc/group ) Otherwise, the next line would generate some rather peculiar behavior. f = file(args[1], r) Yes, exactly. Although, I think you meant that the file name should be in the second element of the list passed into the main() function, because in sys.argv the first element (sys.argv[0]) is the script name, and arguments start at sys.argv[1]. Oopsie - you are quite correct. Good catch. -- Brooks's Law: Adding manpower to a late software project makes it later. Cole Tuininga Lead Developer Code Energy, Inc [EMAIL PROTECTED] PGP Key ID: 0x43E5755D ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
On Thu, Feb 05, 2004 at 10:47:56AM -0500, Paul Lussier wrote: Obviously a quite rudimentary script, which at this point does nothing more than re-create the /etc/group file in sorted order and removes the passwd field. 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. I'm not overly interesting in shell, perl, tcl, or other language solutions Then, how about a python script to feed the group file to awk to get rid of the password field, then pipe the results to sort? Granted, this doesn't go a long way to teaching you python, but it does save a whole lot of typing... If you don't already know how to do that in python, it might still be a worthwhile exercise, too. The resulting script, I would imagine, would probably be less than 5 lines, and perhaps as few as 1. Though, since I don't know a lick of python, I can't really say. :) -- Derek D. Martin http://www.pizzashack.org/ GPG Key ID: 0xDFBEAD02 -=-=-=-=- This message is posted from an invalid address. Replying to it will result in undeliverable mail. Sorry for the inconvenience. Thank the spammers. pgp0.pgp Description: PGP signature
Re: Python help
First of all, welcome to python! 8) I do a fair amount of python coding and am always happy to help out if I can. Quick question - what version of python are you running? #!/sw/bin/python import os import sys class gr_struct: # I found this in the docs and it seems pass # to work rather well. Keep in mind that instantiation of objects is one of python's slower operations. If speed matters, you'd do much better to use a dictionary. gr_hash = {} group = gr_struct() fp=open(sys.argv[1], 'r') for i in fp: i=i.strip(\n) line = i.split(':') group.name = line[0] # what I don't quite get is *why* this works group.gid = line[2] # but it certainly beats the dealing # with nested/anon dictionaries # (though I think I like # perl's hashes better group.members = line[3].split(',') gr_hash = {group.name: group} Why this works is that unless you do some work to prevent it, objects will create member variable references on the fly. You can't do a test against one before you set it, so for instance, doing something like: if group.bob == something: would throw an exception, unless you explicit define group.bob before hand. You're essentially using an instance of an object as a dict, which again leads me to think you should just use a dict. 8) Note, the code as I'm reading it right now will not work. You will end up with a single entry repeated many times in the output. The reason for this is that you are using the same object over and over. Keep in mind that python is heavily referential. When you say gr_hash = {group.name: group} but not changing the reference to group, each of the items are going to point to the same object. What I *think* you want is to put the group = gr_struct() line within the loop. fp.close() skeys = gr_hash.keys() string = '' for key in skeys: members = gr_hash[key].members # why can't I do # gr_hash[key].members.sort() members.sort() The reason you can't do this in place is that sort does not return the sorted list - it sorts it in place. I believe the return value for the sort() method is a None object. One neat thing about sort is that you can pass it your own comparison function. for i in members: # I would expect to be able use if string == '': # i.join() somehow, but that doesn't string = i # seem to work else: string += ',' + i print key + : + gr_hash[key].gid + : + string string is not a great name for a variable, IMNSHO. 8) I believe the join syntax you want is: member_names = ','.join( members ) -- I have one plan for linux. World Domination. -Linus Torvalds Cole Tuininga Lead Developer Code Energy, Inc [EMAIL PROTECTED] PGP Key ID: 0x43E5755D ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
In a message dated: Thu, 05 Feb 2004 11:17:29 EST Cole Tuininga said: First of all, welcome to python! 8) I do a fair amount of python coding and am always happy to help out if I can. Thanks, I've heard so much about it, I figured it couldn't hurt to add tool to my tool-box :) Quick question - what version of python are you running? 2.2 I think. Keep in mind that instantiation of objects is one of python's slower operations. If speed matters, you'd do much better to use a dictionary. In this case, speed doesn't matter, but that's good to know, as I'll eventually be doing some web/CGI programming where it may well matter (is python good for web/CGI ?) group.name = line[0] # what I don't quite get is *why* this works group.gid = line[2] # but it certainly beats the dealing # with nested/anon dictionaries # (though I think I like # perl's hashes better group.members = line[3].split(',') gr_hash = {group.name: group} Why this works is that unless you do some work to prevent it, objects will create member variable references on the fly. You can't do a test against on e before you set it, so for instance, doing something like: if group.bob == something: would throw an exception, unless you explicit define group.bob before hand. Ahh, okay. That's good to know. I can see where I might end up with an empty gun and foot full lead doing this :) You're essentially using an instance of an object as a dict, which again leads me to think you should just use a dict. 8) Well, my thinking was that it was more a long the lines of a 'struct' in C, as noted in the python tutorial. Note, the code as I'm reading it right now will not work. You will end up with a single entry repeated many times in the output. The reason for this is that you are using the same object over and over. Keep in mind that python is heavily referential. When you say gr_hash = {group.name: group} but not changing the reference to group, each of the items are going to point to the same object. Ahh, I had assumed that by changing the object member values, I'd get those. But I see your point now. Of course, with my one line test file, this wasn't an issue :) What I *think* you want is to put the group = gr_struct() line within the loop. I tried that, but it too seemed to exhibit the above described behavior. (of course I didn't look too closely at it in other than to just run the script. At some point when I have more time, I'll play with it in the debugger). members = gr_hash[key].members # why can't I do # gr_hash[key].members.sort() members.sort() The reason you can't do this in place is that sort does not return the sorted list - it sorts it in place. I believe the return value for the sort() method is a None object. One neat thing about sort is that you can pass it your own comparison function. Hmmm, okay. That's similar to perl's sort { func } list thingy. string is not a great name for a variable, IMNSHO. 8) It's better than 'str' which is what I started with ;) I know. I'm just messing around right now, and will clean this all up a great deal when I figure out what I'm doing. I believe the join syntax you want is: member_names = ','.join( members ) Interesting. I didn't know you could do that! Thanks, Seeya, Paul -- Key fingerprint = 1660 FECC 5D21 D286 F853 E808 BB07 9239 53F1 28EE It may look like I'm just sitting here doing nothing, but I'm really actively waiting for all my problems to go away. If you're not having fun, you're not doing it right! ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
On Thu, 2004-02-05 at 13:21, Paul Lussier wrote: Quick question - what version of python are you running? 2.2 I think. Fair enough. My opinion is: so long as you're running something newer than 2.1. 2.3.x is the latest, but until you get a bit more used to the language, you probably won't notice much difference. In this case, speed doesn't matter, but that's good to know, as I'll eventually be doing some web/CGI programming where it may well matter (is python good for web/CGI ?) As a CGI language, it suffers much the same as many interpreted languages. The interpreter has to fire up, read in the code (plus included modules), then process. Python has one minor advantage over perl in that the first time you run it, it compiles it to bytecode and saves that as a file. Saves a step next time you run it if you haven't modified the source file. The other option is that there is an apache foundation project (mod_python). It's not quite as mature as mod_perl, but it does have some pretty cool features. Ahh, okay. That's good to know. I can see where I might end up with an empty gun and foot full lead doing this :) Big time. 8) Well, my thinking was that it was more a long the lines of a 'struct' in C, as noted in the python tutorial. Ayup. Dicts are faster anyway. What I *think* you want is to put the group = gr_struct() line within I believe the join syntax you want is: member_names = ','.join( members ) Interesting. I didn't know you could do that! Essentially, everything in python is an object, including a hardcoded string like ','. Which means that you can call any string methods (such as join for instance) on them. -- Experience is something you don't get until just after you need it. Cole Tuininga Lead Developer Code Energy, Inc [EMAIL PROTECTED] PGP Key ID: 0x43E5755D ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
Cole Tuininga writes: As a CGI language, it suffers much the same as many interpreted languages. The interpreter has to fire up, read in the code (plus included modules), then process. Python has one minor advantage over perl in that the first time you run it, it compiles it to bytecode and saves that as a file. Saves a step next time you run it if you haven't modified the source file. If this really is an important consideration (and 99.9% of the time it simply isn't) Perl has a bytecode back end that can be configured to dump the compiled bytecode out. (perlcc -B yourfile.pl) Regards, --kevin -- Kevin D. Clark (cetaceannetworks.com!kclark) | Will hack Perl for Cetacean Networks, Inc. | fine food, good beer, Portsmouth, N.H. (USA)| or fun. alumni.unh.edu!kdc (GnuPG ID: B280F24E)) | ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss
Re: Python help
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-opt! imized 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-spec! ific 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
Re: Python help
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-opt! imized 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-spec! ific 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
Re: Python help
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-opt! imized 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-spec! ific 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
Re: Python help
First things first - bravo to you Erik for offering up some more general (and IMHO) quite correct advice on approaching python scripts rather than giving quickie advice like I did. That said, there are two things in your post I wanted to respond to. 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. This may appear to be true for simple situations. However, take a look at http://www.bagley.org/~doug/shootout/bench/objinst/ This is admittedly old data, using python 2.2, but should still give a general idea. Python is quite slow for object instantiation with large numbers of objects. Far less true for dictionaries. But it all depends on what your needs are, of course. But you don't need to know that. And because dicts are used so extensively in Python, they are ultra-opt! imized by the Python developers. Very true. Paul - when you get a little more used to all things pythonic, check out the setdefault and getdefault methods for dictionaries. Very slick. [snip] #!/sw/bin/python import os, sys class Group: Next point - the following line has a bug: def __init__(self, name=, gid=, members=[]): This is a very subtle one. The point is that in a method definition within an object, you never want to assign a mutable type (namely, the empty list) to an argument. Weird behavior will occur. Example: #!/usr/bin/python class bob: def __init__( self, list_ref = [] ): self.list_ref = list_ref def printListRef( self ): print str( self.list_ref ) b1 = bob() b2 = bob() b1.list_ref.append( data ) print b1:, b1.printListRef() print b2:, b2.printListRef() # End of script When this is run, you will see: ./test.py b1: ['data'] b2: ['data'] Hey, wait a minute? We only assigned data into b1! Why did this happen? The answer is again due to python's heavy use of references. When you instantiate an object, you are copying a lot of items from the class definition - including references to arguments. When I do b1 = bob(), I'm getting a reference to that empty list defined in the __init__ method's signature. When I do b2 = bob(), I'm getting a reference to the exact *same* list. So from then on, any instantiated object will be sharing that list. Instead, what you want is something like this: def __init__(self, name=, gid=, members=None): self.name = name self.gid = gid if members is None: self.members = [] else: self.members = members self.name = name self.gid = gid self.members = members def setValuesFromFileLine(self, line): Can I recommend changing the following to record = line.strip().split(:) 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): One point about the above line - if you're testing from a python prompt, you'll need to pass your arguments in as a list. So for instance, main( [/etc/group] ) rather than just main ( /etc/group ) Otherwise, the next line would generate some rather peculiar behavior. f = file(args[1], r) Also, below, it looks like you started out wanting to use a list, but switched to a dict: groups = [] for line in f: g = Group() g.setValuesFromFileLine(line) groups_dict[g.name] = g f.close() for group in groups: print group.toLdifFormat() Instead, to pick a list which seems reasonable appropriate to me: groups = [] for line in f: g = Group() g.setValuesFromFileLine( line ) groups.append( g ) f.close() for group in groups: print group.toLdifFormat() if __name__ == __main__: main() These items aren't meant as bashing - I know you said you wrote it without testing. I'm just trying to clarify. 8) -- ... one of the main causes of the fall of the Roman Empire was that, lacking zero, they had no way to indicate successful termination of their C programs. -- Robert Firth Cole Tuininga Lead Developer Code Energy, Inc [EMAIL PROTECTED] PGP Key ID: 0x43E5755D ___ gnhlug-discuss mailing list [EMAIL PROTECTED] http://mail.gnhlug.org/mailman/listinfo/gnhlug-discuss