From: Tom Christiansen <tchrist@mox.perl.com> Subject: More random python observations from a perl programmer Date: 19 Aug 1999 08:22:49 -0700 Per your collective requests, here is one of the documents with my collective observations from learning Python. This article was posted to the Perl newsgroup, but I have duplicated the posting here without crossposting to avoid flame wars. --tom SIMILARITY: When Python talks about tuples, lists, and dictionaries, Perl people should think of lists, arrays, and hashes respectively. Except that tuples in python are 1st-class citizens. (Unclear whether this is good; see below.) COOLNESS: Python's print() function is more like the Perl debugger's "x" command; that is, it's recursive and pretty-printed. GOTCHA: (medium) The whole mutability versus immutability thing causes many confusions. For example: t = ([1,2],3) t[0][0] = "fred" # legal t[1] = "fred" # ILLEGAL I don't understand why tuples weren't just lists that weren't somehow marked "read-only" or "constant". That way you could do the same with dictionaries or any other data types. GOTCHA: (low) You can't use "in" on dicts. Instead, you must use d = { "fred":"wilma", "barney":"betty" } if d.has_key("fred"): # legal if "fred" in d: # ILLEGAL I don't understand why this was done. SIMILARITY: Both "and" and "or" return their last evaluated value in both Python and Perl. GOTCHA: (low) I don't understand why we have so many C operators, such as "&" and "|" and "~", but we don't have "&&" or "||" or "!" We also have "<<" and ">>". It's bizarre that the relational and "?:" are missing. Python uses so much from C to make people see what they expect to see, but this is missing. Why? GOTCHA: (high) Local variables are never declared. They just *happen*. If a variable is assigned to, then it belongs to that "scope". If it's only looked at, it may be from the current global scope. GOTCHA: (medium) If you have a function that expects two arguments, such as: def fn(x,y): return x + y and you have a tuple of the two elements: t = (1,2) You can't just call that function: print fn(t) Or you get a "TypeError: not enough arguments; expected 2, got 1" error. You have to use this apply() function to get around this issue: print apply(fn,t) 3 SIMILARITY: The number zero, the empty string, and the special value None are all false. Non-zero numbers and non-empty strings are all true. This is all like Perl. But here an empty tuple () is false, as is an empty list [] or an empty dictionary {}. In Perl, the last two are true if you write them that way, because they're references. GOTCHA: (medium) Assignment is a statement, not an operator. That means that people are constantly writing loops that aren't really testing the exit condition, as in: while 1: line = os.readline() if not line: break ..... Because they can't write: while (line = os.readline()): # ILLEGAL I feel that this hides the real test condition in a place it doesn't belong. GOTCHA: (low) There are no compound assignment operators, like +=. DISSIMILARITY: There is no "test at the bottom" loop as in C's do{}while. DISSIMILARITY: There is no "three part for" loop from C. GOTCHA: (low) There are no loop labels, and therefore "break" and "continue" are only through the next level. This encourages the proliferation of spurious boolean condition variables. It was annoying when Pascal made you do the same thing. There is no "goto", which is how C works around it. As with many things in Python, here you force the user to be tangled up with exceptions just to do very simple things. That's not as clear a solution. SIMILARITY: Both Python and Perl are 0-based for indexing. DISSIMILARITY: Python uses [] to create a new empty list, which you then reference with [] subscripts. Perl uses [] to create a new empty array, which you then reference with [] subscripts. Python uses {} to create a new empty dictionary, which you then reference with [] subscripts. Perl uses {} to create a new empty hash, which you then reference with {} subscripts. GOTCHA: (high) I had hoped that having *only* reference semantics would spare the beginner from having to keep in mind the difference between a reference and its referent as we have in C or Perl. Alas, it is not to be. x = [1,2,3] y = x x[1] = "fred" print y [1,"fred",3] COOLNESS: You can "foreach" across multiple items. a = [ [1,2], [3,4], [5,6] ] for (i,j) in a: print i+j 3 7 11 COOLNESS: Python's "longs" are actually built-in, arbitrary precision integers (i.e. BigInts) COOLNESS: (?) Named parameters are built into the language. So are default paramters and variadic ones, although order matters of course. It's quite elaborate. COOLNESS: Namespaces seem to be *the* thing in this language. You can always get a listing of anything's names/attributes, even all the built-ins (which you can't do in perl) or all the names in the local scope (which you also can't do in perl). DISSIMILARITY: A class method call gets no class name as its implicit extra argument the way an instance method would. DISSIMILARITY: In Python, there is no difference between single and double quotes: both interpolate C-style escapes like \t, and neither interpolates variables. In Perl, single quotes do neither escapes nor variables, but double quotes do both. GOTCHA: (low) Single and double quoted strings can't cross line boundaries. You need triple quotes for that! SIMILARITY: Python does a depth-first recursive search on class ancestors looking for resolution of a method call. So does Perl. DISSIMILARITY: Python also does an inheritance search for data members that are missing. This is probably cool, however. There is no big difference between a functional and a data member. DISSIMILARITY: There are no private variables in a module or a class. They can always be fully-qualified and accessed from without. Perl can do this with file-scoped lexicals, but still can't do so with data members (modulo obscure tricks such as using a non-hash for an object). GOTCHA: (medium) Perl's hex() function is the opposite of Python's. Perl's hex function converts 22 into 37. Python's hex function converts 37 into 0x22. Oh my! Like hex, Python has oct() completely backwards from Perl. Python's oct(44) returns 054, but Perl's oct(44) return 36. SIMILARITY: Constructors and destructors (__init__ and __del__) aren't automagically called in parent classes in Python. Neither in Perl. DISSIMILARITY: Because there are no variable markers in Python, there are no variable interpolation in strings. That makes something like print "this $x is $y here\n" become in python print "this %d is %d here\n" % (x, y) But since "stringification" is handy, Python usurps backticks for that purpose: print "this is", `x`, "here" Is going to call the x object's stringification method (x.__str__) to get a print value. In perl: print "this is $x here" also does so, although the stringification method is oddly named in Perl. use overload '""' => .... GOTCHA: (medium) Things that return lists can't be used where a tuple is expected. A function that returns a list must be coerced into a tuple to use this, though. def fn: return [1,2] print "this %d is %d here\n" % fn() # ILLEGAL print "this %d is %d here\n" % tuple(fn()) The illegal part points out that this is an TypeError: illegal argument type for built-in operation Which isn't very helpful. GOTCHA: (high) Python has no manpages! The horror!!!!!!!!!!!!!!!!!!!!!! ENODOC GOTCHA: (low) Often Python's error messages leave something to be desired. I don't know whether GOTCHA: (medium) All ranges are up to but *not including* that point. So range(3) is the list [0,1,2]. This is also true in slices, which is the real gotcha part. A slide t[2:5] does not include t[5] in it. SIMILARITY: Negative subscripts count back from the right. Same as in Perl. COOLNESS: Slices can omit either the starting point or the ending point. t[2:5] # elts 2 through 4 (but not 5!) t[:5] # elts up to 4 (but not 5!) t[:-1] # all but the last element (up to but not to -1) DISSIMILARITY: Python doesn't convert between strings and numbers unless you tell it to. And if you don't tell it to, you get an exception. Therefore you have no idea what these x = y + z x = y - z x = y * z are really doing. The first would concat strings or lists, the last would repeat them. Personally, I don't care for this, because I always wonder what subtracting one string or list from another would be. COOLNESS: Python has a reduce() built-in that works somewhat like map(). COOLNESS: Python's filter() [same-ish as Perl's grep()] and its map() operators can operate on items from separate sequences in parallel. GOTCHA: (high) This is a big surprise: strings are not atomic (although they are immutable). They are instead sequences of characters. This comes up in strange places. For example: >>> for c in ("weird", "bits"): ... print c ... weird bits >>> for c in ("weird"): ... print c ... w e i r d The second case autosplit the characters! Here's another: >>> print map(None, "stuff") ['s', 't', 'u', 'f', 'f'] >>> print map(None, "stuff", "here") [('s', 'h'), ('t', 'e'), ('u', 'r'), ('f', 'e'), ('f', None)] (Python's map None is like Perl's map $_.) DISSIMILARITY: This also means that instead of substr() in Perl, you slice a string as in s = "string" print s[2:4] ri Yes, that's all you got. Strange, eh? See below on ranges. GOTCHA: (high) Because everything is a reference, and there's no way to dereference that reference, it turns out that there is no trivial way to copy a list! This fails: x = [1,2] y = x Because you don't get a new list there, just a copy to an old one. Suggested work-arounds include y = x[0:] y = x[:] y = x + [] y = x * 1 This forces people to think about references, again. So much for lists being first class citizens! Compare this with Perl's @x = (1,2); @y = @x; Or even with references: $x = [1,2]; $y = [ @$x ]; or @y = @$x; GOTCHA: (medium) Slices in Python must be contiguous ranges of a sequence. In Perl, there's no such restriction. GOTCHA: (medium) You can't slice dictionaries at all in Python. In Perl, it's easy to slice a hash, and just as sensible. GOTCHA: (high) As we saw with lists, because everything is a reference, and there's no way to dereference that reference, this means that again there is also no built-in, intuitive way to copy a dictionary. Instead, the suggested work-around is to write a loop: new = {} for key in old.keys: new[key] = old[key] But this is guaranteed slower, because it's not at the C level. It also shows that dictionaries aren't first class citizens in python as they are in Perl: %old = ( "fred" => "wilma", "barney" => "betty" ); %new = %old; or even with references: $old = { "fred" => "wilma", "barney" => "betty" }; $new = { %$old }; or %new = %$old; GOTCHA: (high) Lists don't autoallocate the way dictionaries do. So while this works on two dictionaries: new = {} for key in old.keys: new[key] = old[key] This fails on two lists: new = [] for i in old: new[i] = old[i] Because Python refuses to grow the list as it did the dictionary. Perl will grow both arrays and hashes just fine. GOTCHA: (medium) There's no way to set up a permitted exports list. The caller may have anything they ask for. COOLNESS: DBM files seem (?) to automatically know about nested datatypes. GOTCHA: (medium) Importing a variable only gives you a local copy. In Perl, it makes two names for the same object. GOTCHA: (low) Because you can't declare a local variable, you can't create variables local to something other than a module (ie. file), class, or function. There are no loop-local variables; they would persist through the end of the function. You can't have a scope you can't name (well, almost; see lambdas) GOTCHA: (high) Scopes don't nest in Python, but they they do in Pascal, Perl, or C. This is supposedly "simpler", but it's very suprising in many ways: x = 1 # global def fn1(): x = 10 # implicit local def fn2(): print x # whose? fn2() fn1() The answer is that it prints 1, because fn2 has no x in its local scope, so gets the global. The suggested work-around is def fn2(x=x): using default parameters. GOTCHA: (medium) You can't cut and paste these examples because of the issue of white space significance. :-( GOTCHA: (low) List objects have built-in methods, like l.append(x) But string objects don't. You have to import from the string module to get them, and then they're functions, not methods. GOTCHA: (low) You have insert and append methods for lists, but only a del function. Why is it a function not a method, and why isn't it spelled out? Apprently the fashionable wayto do this is now a[2:3] = [] Which of course, deletes only one element. ARG. DISSIMILARITY: Where Python says "elif", Perl says "elsif". SIMILARITY: Neither Perl nor Python supports a case or switch statement, requiring multiway if's or a lookup of a function dispatch table. DISSIMILARITY: Where Python says "break" and "continue", Perl says "last" and "next". DISSIMILARITY: Where Perl says "continue", Python uses "else" (for a loop block). DISSIMILARITY: Constrast Python: import os data = os.popen('grep %s %s' % (patstr, srcfile)).read() with Perl: $data = `grep $patstr $srcfile`; GOTCHA: (high) There doesn't seem to be away other than using low-level hand-rolling of posix functions to supply things like os.popen and os.system a list of shell-proof arguments. Looks like it always goes through the shell. This has security ramifications. GOTCHA: (high) Because you can't use readline() to get a number, people seem to enjoy calling eval() just to get a string turned into a number: import sys str = sys.stdin.readline() num = eval(x) This is scary. Even scarier is the propensity for calling input(), which auto-eval()s its input to make sure it's the right "type". (Funny that a soi-disant "typeless language" should be so danged picky about this.) That means you see people write: num = input("Pick a number? ") But the person can supply as an answer 0x23 or 2+9 or pow(2,4) or os.system("rm -rf *"). I am stunned. GOTCHA: (medium) The expression 3/4 is 0, which is false. In Perl, 3/4 is 0.75, which is what you'd expect. You need to force the floatness. Sometimes Python is just too tied to C, other time not enough. This is one of the former. GOTCHA: (low) Regexes default to emacs style, not egrep style! Gads! This is surprising to anyone except an emacs weenie. Sigh. And just *try* to read the manpage on the differences based on passed in arguments establishing the style. There aren't any manpages! Have a nice day. GOTCHA: (low) An out-of-bounds list reference raises an "IndexError: list index out of range" exception, but not if you use a slice to get at it! t = range(5) print t[2:17] [2, 3, 4] COOLNESS: Relationals stack: x < y < z means x < y and y < z COOLNESS: There's no distinction between the mechanism used for operator overloading and tying, as there is in Perl. Both use special method names. GOTCHA: (high) Python's lambda's aren't really lambdas, because they are only expressions, not full functions, and because they cannot see their enclosing scope. DISSIMILARITY: What Perl calls eval(), Python calls exec(). What Perl calls exec(), Python calls os.execv(). GOTCHA: (low) Python's eval() only works on expressions, not full code blocks full of statements. You need exec() for the whole thing. GOTCHA: (medium) This is a tuple of two elements (1,2) But this is a tuple of one element: (1,) Whereas this is merely the expression 1: (1) Yes, the trailing comma counts. GOTCHA: (low) Normally, a print statement in python adds a newline. If you don't want one, you need a trailing comma! DISSIMILARITY: Python uses a pow() function for Perl's ** operator. Wait, no, they just added "**" later, with the same semantics as in Perl, except that something like 2**3**4 in Perl gives you an answer, and in python, an exception. GOTCHA: (low) Python has a round() function, but it seems to ignore the IEEE rules of rounding. However, its sprintf operator doesn't: >>> print "int of %f is %.0f" % (1.5, 1.5) int of 1.500000 is 2 >>> print round(1.5,0) 2.0 >>> print "int of %f is %.0f" % (2.5, 2.5) int of 2.500000 is 2 >>> print round(2.5,0) 3.0 And I'd jolly well like to know why I wasn't allowed to use print "int of %f is %.0f" % (2.5) * 2 or if needed, print "int of %f is %.0f" % ( (2.5) * 2 ) GOTCHA: (medium) Anything that python doesn't like, it raises an exception about. There is no fail-soft. Even non-exception issues raise exceptions. It's pervasive. K&P curse languages that force people who want to open() a file to wrap everything in exception code, saying that "failing to open a file is hardly exceptional". GOTCHA: (medium) You can't just interchange tuples and lists in python the way you can lists and arrays in Perl. This is an error: import sys # otherwise, no argv for you, buddy print "First arg %s and second %s" % sys.argv[1:3] because you need print "First arg %s and second %s" % tuple(sys.argv[1:3]) GOTCHA: (low) I can't figure out how to write a class destructor for at exit handling the way I can with Perl's END{} GOTCHA: (medium) Python has no 2nd GC pass at thread shutdown time to find lost objects and destruct them. This is a problem on embedded systems, and breaks correctness issues. SIMILARITY: Like Perl, Python requires access to members through a self reference. This gets rid of C++'s icky hidden scope. This is good. COOLNESS: I believe that Python checks the prototype signature on methods as well as on functions. I wonder whether Tim Bunce's ideas run-time evaluation of prototypes for Perl might be able to do this. GOTCHA: (low) sort and reverse are in-place. This leads to verbose code, such as: old = [1,2,3] new = old new.reverse() Likewise for sort. GOTCHA: (low) You have to compiled the definition for a function before you may compile the call it. This is ok def fn(): print "hi" fn() But putting the fn() call first: fn() def fn(): print "hi" Produces the ever helpful: NameError: fn error message. So much for autoloading. Then again, this would save me from the darned legnth() bug in Perl, that is, switching characters. Maybe a "no autoload" pragma for perl? I always though "use strict subs" shoud ldo this. But another python mystery is why it's ok to compile code to methods that you don't know whether will be legal, but not functions. ob = some_fn() ob.scattter() That will compile, even though I used an extra 't'. It will die on run-time, just as in Perl. But the fn() case at the start of this looked like a compile-time death. ADDENDA: It turns out that def() happens *AT RUN TIME* in python. That's the root of this problem. In Perl, a sub{} is a compile-time thing. GOTCHA: (low) When you need to make an empty copy of the same type, you write x = y[:0] So much for being typeless. Sigh. SIMILARITY: In theory, the Python rexec() module should be like Perl's Safe module. I haven't tested to see whether they chroot the namespace, or whether the evil method name leakages occur through the box. COOLNESS: Any function of class can have a significant string called a "doc string" that can be accessed as whatever.__doc__ QUESTION: What is and what is not thread safe? SIMILARITY: Python uses None in many places where Perl uses undef(). But not all. You can't over-subscript an array and get away with it. -- I know it's weird, but it does make it easier to write poetry in perl. :-) --Larry Wall in <7865@jpl-devvax.JPL.NASA.GOV>