Live data from Hacker News

A few things to remember while coding in Python

satyajit.ranjeev.in

81–90 of 146 posts

Re: A few things to remember while coding in Python

#81
post #80

Earlier quoted context omitted.

Functions (methods) are first class objects also in Ruby: methods are instances of the class Method, while Procs are a lightweight alternative. Default arguments in Ruby are not mutable in any kind of function object, be it a lambda proc, a regular proc, or a method. You can use a mutable default argument as an ersatz static variable, e.g. for memoization.

> Default arguments in Ruby are not mutable in any kind of function object Maybe I misunderstood you, but they are perfectly mutable: class A attr_accessor :a def initialize @a = [] end def b x = a x # obj.b # => [1] obj.b # => [1, 1] obj.a # => [1, 1] If you mean "inline default arguments are not mutable", that's not true either. What is true is that the default argument is evaluated when the function is called, not…

Good catch, I hadn't thought of the case where the default argument is expressed in terms of another variable.

Re: A few things to remember while coding in Python

#82

Earlier quoted context omitted.

My lisp is a bit rusty, but it looks like what you're doing there is returning a function which gets redefined every time you reuse the function. The equivalent Python would be something like this: def function(): x = 3 def internal(x, foo=[]): foo.append([7]) foo.append(x) return foo return internal(x) print function() print function() Which does what you would expect: [[7], 3] [[7], 3]

No. In my example the function is created only once, and called twice.

What's that lambda thingo in the middle then? Pretty sure that's another function, redefined every time your function is called.

Re: A few things to remember while coding in Python

#83
There is a builtin function called `reversed`. You'd better remember that than the "useful" [::-1] idiom.

The recommendation on `iteritems` had better be generalized to include `iterkeys`, `itervalues`, and other opportunities for using iterators rather than building lists. A note that the 'iter...' versions are removed in Python 3 (because iterator behaviour becomes the default) would be appropriate here.

In relation to collections, itertools is a great module to get familiarized with. I import * from this module. I consider functions there as if they were builtins.

"Conditional assignment" is a weak and misleading name. "Conditional expressions" is more descriptive. There is no assignment in

  print "yes" if some_condition else "no"
as the article acknowledges later.

Using Ellipsis for getting all items is a violation of the Only One Way To Do It principle. The standard notation is [:].

I commend the good intentions of the writer, but I'm surprised that this article got 144 upvotes in HN.

Re: A few things to remember while coding in Python

#84
There is one slight mistake there - saying that [::-1] is a special case. An empty value in a slice implies the beginning or the end, and when the stride is negative, the beginning is the last index, while the end is 0 - making [::-2] for example start from the last element and go down in jumps of two.

Re: A few things to remember while coding in Python

#85
post #7
post #4

Earlier quoted context omitted.

Trailing commas are really easy to miss. When reading this line of code, I did not notice it immediately; I originally assumed that varname was being assigned a list. This sort of code would be very confusing when I'm just quickly reading through a procedure trying to find the potential bug.

Agreed. It could be written much more clearly in my opinion like this: [varname] = [x for x in l if predicate_with_single_truth_value(x)]

I had to check that on the REPL. I'm surprised that even works and I can't think of a good reason why should list syntax be allowed as a lvalue, in addition to tuple syntax.

Re: A few things to remember while coding in Python

#86

Earlier quoted context omitted.

But 'def' doesn't have to work that way. Consider, in CL: > (defvar *fn* (let ((x 3)) (lambda (&optional (y (list nil x))) (push 7 (car y)) ; modifies the list y))) *FN* > (funcall *fn*) ((7) 3) > (funcall *fn*) ((7) 3) From this example you can see two things. First, the binding of 'x' is closed over when the lambda expression is evaluated. And second, the expression that provides the default value of 'y' is evaluat…

My lisp is a bit rusty, but it looks like what you're doing there is returning a function which gets redefined every time you reuse the function. The equivalent Python would be something like this: def function(): x = 3 def internal(x, foo=[]): foo.append([7]) foo.append(x) return foo return internal(x) print function() print function() Which does what you would expect: [[7], 3] [[7], 3]

No, the lambda expression creates the function, which is returned as the value of the let block; defvar just binds the function to a name so we can use it multiple times.

Re: A few things to remember while coding in Python

#87
post #11

halve_evens_only = lambda nums: map(lambda i: i/2, filter(lambda i: not i%2, nums)) I still find it rather silly that python doesn't supper a nice list map/filter; it could be so much nicer nums.filter(lambda i: i%2 == 0).map(lambda i: i/2) If they did, even including the annoyingly long-to-type "lambda". List comprehensions are cool and all, but do not really scale visually (i.e. get rather messy) when you have more…

I think only familiarity with Javascript makes that "nice". One could argue that this

(lambda i: i%2 == 0).filter((lambda i: i/2).map(nums))

makes (marginally) more sense. But I like filter and lambda alright as they are. I agree Python's inconsistency in this is a bit unfortunate, but I haven't had much of a problem with the runtime errors you mention.

Re: A few things to remember while coding in Python

#88

Earlier quoted context omitted.

I am but an egg, but isn't this the same but shorter? def f(L=None): L = L or []

It might work the same depending on your use of it but it's not the same. In that instance L will become a blank list if it is equal to None, zero, or a zero length string. There are many cases where this wouldn't affect anything, but there can also be instances where that will cause you to define L as a blank list when you really wanted to keep L's value. I think it's always better to be explicit and test for the va…

Ah.

These are the little assumptions that keep blowing off my feet. Thanks.

Re: A few things to remember while coding in Python

#89

Earlier quoted context omitted.

It's most certainly not a mistake; Python 3 would probably have fixed it, if it were. It is an (admittedly, strange) side effect of the way 'def' works.

But 'def' doesn't have to work that way. Consider, in CL: > (defvar *fn* (let ((x 3)) (lambda (&optional (y (list nil x))) (push 7 (car y)) ; modifies the list y))) *FN* > (funcall *fn*) ((7) 3) > (funcall *fn*) ((7) 3) From this example you can see two things. First, the binding of 'x' is closed over when the lambda expression is evaluated. And second, the expression that provides the default value of 'y' is evaluat…

This translates into

    fn = lambda y=[y]: y.push(7); return y
if you accept the ; to separate statements, as the lambda in python is syntactically only allowed to contain one statement.

(The introduction of the variable x into the example is not important for the behavior of default arguments, however, it is important for a separate issue. I've stripped it out here.)

Re: A few things to remember while coding in Python

#90

Earlier quoted context omitted.

No. In my example the function is created only once, and called twice.

What's that lambda thingo in the middle then? Pretty sure that's another function, redefined every time your function is called.

Yes, the lambda creates the function. Note that defvar does not. So there is still only one function being defined here.
Post reply on HN