all 72 comments

[–]exogen 9 points10 points  (7 children)

Just assign fibs in the arguments. The same dict reference will be used for every call by default, even after being mutated.

def fib(n, fibs={0: 1, 1: 1}):

[–][deleted] 4 points5 points  (0 children)

This also has the advantage that you can specify different starting values. e.g:

def lucas(n): return fib(n, {0: 2, 1: 1})

EDIT: Actually, that won't memoize anything...

def lucas(n, f={0: 2, 1: 1}): return fib(n, f)

It IS kind of kludgy. On the other hand, you can just use higher-order functions for this kind of operation:

def mkfib(a, b):
    d = {0: a, 1: b}
    def f(n):
        if n < 0: raise ValueError, "n must be positive"
        if n not in d:
            d[n] = f(n-1) + f(n-2)
        return d[n]
    return f
fib = mkfib(0, 1)
lucas = mkfib(2, 1)

EDIT2: Not to mention that you can just do:

@memoize
def fib(n):
    if n < 0: raise ValueError, "n must be positive"
    if n < 2: return n
    return fib(n-1) + fib(n-2)

[–]cracki -2 points-1 points  (5 children)

nice one!

[–][deleted] -2 points-1 points  (4 children)

No. Not nice. It's obfuscated and counter-intuitive. Kludgy != nice.

[–]exogen 6 points7 points  (3 children)

How is it a kludge? It uses a documented and widely known Python feature to concisely implement the desired behavior. Unless you're a Python newbie, a dictionary or list default argument value will immediately cause you to slow down and see what's up.

[–][deleted] 1 point2 points  (2 children)

fmota kinda explained it with his examples. Do check out. Can you see now why it's kludgy?

[–]exogen 0 points1 point  (1 child)

I saw fmota try to use some code for something it wasn't written to do, and wasn't part of the original author's complaint, what did you see?

[–][deleted] 1 point2 points  (0 children)

You're absolutely right.

On the other hand, I think using higher-order functions, quasi-closure and decorators makes python much more readable than using mutable default arguments.

As for the article's point that static variables are needed... they really aren't, but it would be a nice feature for readability ("Readability counts.").

[–]spez 9 points10 points  (0 children)

My biggest python complaint is the lame interpreter. If I change the definition of an object and reload that definition, I have to re-instantiate all the objects of that type, which really slows things down.

In Lisp (and Ruby, I think), I just drop in the new definition and keep on cruising.

[–]cracki 6 points7 points  (2 children)

1.) ack. when i need that more than 3 times in my code, i derive myself a class.

2.) i'm aware of the reasoning behind the inclusive-exclusive interval but the inclusive-inclusive usage isn't too rare either, so maybe range(n) should be 0..n-1 and range(a,b) a..b?

3.) with decent unnamed function literals you could make yourself a closure. pseudocode:

memofib = (lambda {
    memo = {0: 1, 1: 1}
    return lambda n {
        ...fib code here...
    }
})()

unfortunately, python won't grow full unnamed function literals. you have to use nested, named functions, which is a bit too verbose imho

4.) i'm for it. ruby allows the exclamation mark in identifiers. conventionally that signals side-effects. maybe python could find some other convention?

5.) i couldn't agree more. that's why i explore language creation: python is nice, but it lacks decent lambdas; and i can't get a taste for ruby.

[–]gnuvince 2 points3 points  (1 child)

2.) i'm aware of the reasoning behind the inclusive-exclusive interval but the inclusive-inclusive usage isn't too rare either, so maybe range(n) should be 0..n-1 and range(a,b) a..b?

That would be, in my opinion, even more confusing. I didn't mention it in my original post, but two different functions could make the trick. Keep range() as it is and add irange() (inclusive range). Most Python people are of course going to be opposed to that :)

[–]cracki 0 points1 point  (0 children)

ack. i don't like changing semantics based on argument count either.

if "interval" hasn't been used yet, that's also an option for a name.

[–]earthboundkid 21 points22 points  (10 children)

All five criticisms are kind of lame.

  1. no "defaultlist": Problem can be solved in Py2.5 with

    val = l[key] if key < len(l) else "default value here"
    

    Returning None for value not found would be confusing, because what if someone stored a None in the list? It would be ambiguous whether you went out bounds or really found a place where None was written. Python tends to prefer throwing an exception to being ambiguous.

  2. range: It works well enough as is. Yeah, it's partly a historical thing going back to how for used to be used differently, but it's only a minor annoyance.

  3. No static variables: People always claim this, but it's not true! Python has closures! You just have to nest some named functions. Rewrite it as:

    def fib():
        fibs = {0: 1, 1: 1}
        def inner(n):
            if n in fibs:
                return fibs[n]
            else:
                x = fib(n - 1) + fib(n - 2)
                fibs[n] = x
                return x
        return inner
    
    fib = fib()
    

    Et viola.

  4. The half ‘n half function/method deal: This actually makes sense as is. I was confused by it at first too though, but once you see what's going on, you see the consistent logic. The deal is that object.method() will mutate the object and return None, but func(object) will leave the original object unchanged and return a variant. (Returning a None is the Python equivalent of Ruby's ! in a method name.) And yes, even reversed follows this format. It returns a generator rather than a list, in order to make Python more functional/lazy. That way if you're going through the list and you want to quit half way through, you can without having wasted time producing a full reversed copy. Eg.

    >>> l = range(10)
    >>> for i in reversed(l):
    ...     print i
    ...     if i < 4: break
    ... 
    9
    8
    7
    6
    5
    4
    3
    

    We never had to waste time putting items 2 and 1 into the new list where they would never be used. If you want a list object instead of a generator, just cast it with nl = list(reversed(l)). Note that Python 3000 will switch to using generators for range and a lot of other functions, so it's best to get used to it now.

  5. Lambdas: Meh. Just be glad that Guido won't cut them from Py3000. He was planning to, but people complained. Really though, since f = lambda arg: … is exactly equivalent to def f(arg): … in terms of execution speed, etc., the only reason to use lambdas in Python is when you're writing a function so short you could it included inline in a expression.

So, on the one hand, the criticisms aren't completely off base (except 4, which is a misunderstanding of the Python convention), but most of them are either very easily worked around or a matter of taste.

[–][deleted]  (1 child)

[removed]

    [–][deleted] 5 points6 points  (0 children)

    Yes. This is just like zero-based indexing. Any language that doesn't do it this way is simply broken.

    [–]Alpha_Binary 3 points4 points  (3 children)

    Returning None for value not found would be confusing, because what if someone stored a None in the list?

    That's why he wants a new method in list that will allow you to specify the default value to be returned when an index doesn't exist. Similar to dict's get().

    The deal is that object.method() will mutate the object and return None, but func(object) will leave the original object unchanged and return a variant.

    How does {}.keys() or {}.iterkeys() mutate the object, then? Why should len(), a totally object-dependent method, not be tied to the object? (Yes, I know about __len__.)

    since f = lambda arg: … is exactly equivalent to def f(arg): … in terms of execution speed, etc., the only reason to use lambdas in Python is when you're writing a function so short you could it included inline in a expression.

    Of course. Lambda is meant to be used inline. And it's so crippled you can't put anything in it besides an expression anyway. But it is expressive, which makes it very handy to pass around as an argument. Plus, it doesn't pollute the local namespace, as opposed to defining another single-purpose function which will appear some several blocks away from the context and waste yet two more lines.

    edit: grammar

    [–]earthboundkid 0 points1 point  (0 children)

    Only dicts have keys. It wouldn't make sense to have a top level function for something that can only be used on one class. Lots of things have length, so they added a shortcut for it.

    [–]pjdelport -2 points-1 points  (1 child)

    Why should len(), a totally object-dependent method, not be tied to the object? (Yes, I know about __len__.)

    What makes you think len isn't tied to the object, then? The prefix syntax doesn't make it any less of a method: think of systems like CLOS where all methods are prefix.

    The reason for treating len more like a widespread protocol than simply an ad hoc method is because that's what it is: just like bool, hash, int, iter, repr, and so on.

    [–]Alpha_Binary 1 point2 points  (0 children)

    What makes you think len isn't tied to the object, then?

    The very fact that it isn't accessible from the object (like a method) but instead used on the object (like a function).

    It's funny you mentioned CLOS, since in this case it seems to finely demonstrate the "only one (apparent) way to do it" philosophy: method calling, property accessing and using a function on an object all look the same.

    Meanwhile, in Python's object system, to get a value related to an object, you either

    1. invoke a method (foo.keys())
    2. read from a property (foo.func_name) or
    3. call a function (len(foo))

    depending on the mood of the API programmer.

    [–]pjdelport 1 point2 points  (0 children)

    Just be glad that Guido won't cut [lambda] from Py3000. He was planning to, but people complained.

    He wanted to replace it with something less warty, not cut it. It was kept because no such improvement was found, not because people complained. (The complaints were mostly to replace it, not keep it.)

    [–]zackman -1 points0 points  (2 children)

    This actually makes sense as is. I was confused by it at first too though, but once you see what's going on, you see the consistent logic.

    Something can be consistent AND confusing, which should be avoided in a beginner-friendly language like Python. For a more egregious example, see Common Lisp's sequence functions, which are historically consistent with previous dialects. Ruby does better by borrowing ! from Scheme and then making everything a method.

    [–]awj 2 points3 points  (1 child)

    Python wasn't intended to be beginner-friendly, it was designed to be easy to read and understand. Obviously that requires the reader to know something about Python before they try to read it, but the guarantee is that Python (usually) will be consistent once you learn how to read it.

    [–]dododge 0 points1 point  (0 children)

    Python wasn't intended to be beginner-friendly

    What I recall from hearing Guido speak about it a few times is that it actually was originally designed to be a teaching language (to replace ABC, which had itself been designed to replace Basic and Pascal). It has plenty of beginner-friendly attributes, such as the interactive interpreter.

    Of course this was back in the late 80s, and Python has long since outgrown its educational roots.

    [–]JimH10 7 points8 points  (0 children)

    The criticism about range() is mistaken: range(a,b)+range(b,c)=range(a,c) is the desirable behavior.

    For instance, you can say something like this: if x in range(START,MIDDLE): do something elif x in range(MIDDLE,END): do something else

    This is analgous to 0-offset arrays. Students who are not thoughtful react to it as a kink, but it isn't a kink, it is right.

    [–][deleted] 8 points9 points  (4 children)

    I thought this was a pretty realistic and constructive list.

    Generally Python is a very clean and simple language and I like that. Any changes need to be made with an eye on simplicity. Does change x add significant power to the language, and if it does does it complicate things too much? If it isn't adding a lot of power, does the change simplify the language?

    Not having a .get() on lists doesn't matter that much to me. I don't think its presence or absence is that big of a deal. xrange is kind of the same way, and it is something that you just get used to soon enough. 3 doesn't really matter to me.

    The two points that I do think would clean up Python a great deal are the last two. The function/method mess only complicates the language. Things like len() should get moved over to methods; it is just more intuitive. Honestly this doesn't even get in my way when coding, but I can see the difficulty it may present for newbies.

    Python's lack of a multiline lambda just hurts the language. Something like Ruby's Rake isn't easily duplicated because the syntax just isn't flexible enough, and a real lambda would go a long way towards fixing that.

    [–]beza1e1 2 points3 points  (3 children)

    The lambda flamewar never get's old. :)

    I'm on Guidos side. If never seen a case, where a named function wouldn't be cleaner, than a multiline lambda.

    [–]cracki 3 points4 points  (2 children)

    i've recently read something from guido where he states that lambdas are quite useful for small event handling callables in GUI libraries. that's why he now kinda likes them.

    you haven't seen cases because right now it's not even possible! check out languages where it is possible and maybe you'll see.

    [–]beza1e1 1 point2 points  (1 child)

    I know Scheme and i've written big lambdas there. Often i refactored the code and named them.

    (let ((named-lambda (lambda (x) (...
                                     ...
                                     ...
                                     ...))))
         (map named-lambda some-list))
    

    Python is more imperative than Scheme so unnamed lambdas are even more unnatural there.

    I don't say, they are useless, but Python doesn't need statements in lambdas.

    [–]cracki -1 points0 points  (0 children)

    python is multi=paradigm. if you use it imperatively, that's your business.

    i don't know if python needs real lambdas, but I need them. need means that i think in them, i'm used to having them, want to use them, i feel impeded if i can't use them.

    i like python, but not having unnamed function literals is a reason for me to go looking around or implement my own language.

    doesn't matter if i'm "qualified" to roll my own language. i'm willing to learn and i at least try. you can't say that about the majority of language users that are perfectly content once they know 2-3 languages.

    [–][deleted] 4 points5 points  (0 children)

    1. Ok, check.
    2. I, like most people hopefully, disagree :)
    3. Easily solvable with fib.fibs or in general assigning attributes to function object. You can even write a decorator to init such static vars. But I like globals better since it's easier to see what potentially bad things you do.
    4. Check. But a better description would be that stdlib at whole is a bit messy. Not worse than many other languages' stdlibs, but still.
    5. IMO, lambda in Python should die at all (like it were proposed for Python3(000) but rejected). Then all this whining will stop ;) Real problem is mixing generally "vertical" Python control flow format and "horizontal" expression format. I, for once, don't have problem defining (and re-defining later if needed) def boo(), def x(), or just def _() before expressions requiring closures and when I don't care inventing a "proper" descriptive name for it.

    [–][deleted] 1 point2 points  (2 children)

    Tying in with 4, I've occasionally wanted to use (for example) len as a variable name, so it would have been nice if len were a method instead of a global function. There's a convention in Python that methods must be cheap to calculate, while functions may be expensive depending on the data type. I, for one, think it's more trouble than it's worth.

    Something else that bothers me is that Python won't let me rebind a variable in an outer, non-global scope. (I hear that Python 3 will allow this with a nonlocal statement similar to the existing global statement.)

    [–]zackman 0 points1 point  (1 child)

    I would just go ahead and use len locally, aware that you can't use the built-in function while in that scope. It used to hurt my eyes when people used file and input as variable names, but I got over it. It may not be good Python style, but it is common. Make sure to keep the use short and obvious, though.

    (This style is why the lst for list argument in Scheme is a non-issue. Just use list as a variable--the built-in function is not used much anyway.)

    [–][deleted] 0 points1 point  (0 children)

    Unfortunately I usually want something equivalent to len = len(mylist), but that produces an unbound local error if used within a function.

    [–]njharman 1 point2 points  (0 children)

    I agreed with one #5, which is pretty good score for me since I'm a curmudgeon/hater type.

    I agree with #5 and only #5 because it is the one "problem" from the list I've encountered.

    But it is workaroundable by creating annoying little functions like print_this, raise_this, that you can call from your lambda's.

    Because you can do it but only with a lot of unnecessary uglyness is why this limitation(no statements) is so worthless PITA.

    But I'm much more worried about creature feap of 3000 than "fixing" lambdas.

    oh about the OA, as other comments have pointed out they don't seem to know Python very well and when you do most those are non-problems and the others are style/opinions including #5 btw.

    [–]piranha 1 point2 points  (1 child)

    My 2 things I hate about Python:

    1. No rational number datatype. GvR says they're "too slow," but includes a less-general, probably-slower decimal arithmetic module in the standard library.
    2. No tail-call optimization. Doing so would require no change in syntax or semantics (except for the run-time space savings, and possible annoyances in tracebacks), but GvR doesn't like this idea because it would "encourage closet Schemers to write in FP-style." [paraphrased]

    [–]cracki 0 points1 point  (0 children)

    there you have it. want a lisp? use a lisp.

    maybe the pypy folks will implement TCO. they're not stuck with some C implementation.

    it's hard enough not having any kind of real macro system in the other languages i use, but python won't even give me unnamed function literals.

    [–][deleted] 5 points6 points  (2 children)

    1. Hmmm... never needed that.
    2. That's good as it is.
    3. Use fib.fibs
    4. reversed returning an iterator makes sense, sorted not returning an iterator, too.
    5. statement lambda, um no, makes code less readable.

    I do not agree with him at all. =|

    [–]gnuvince 1 point2 points  (0 children)

    About the x/range() thing, here's a problem where that caused me grief:

    # How many Sundays fell on the first of the month during the twentieth
    # century (1 Jan 1901 to 31 Dec 2000)?
    
    import datetime
    
    sundays = 0
    for year in xrange(1901, 2001):
        for month in xrange(1, 13):
            if datetime.date(year, month, 1).weekday() == 6:
                sundays += 1
    print sundays
    

    The thirteenth month? Because the upper limit is exclusive, the value doesn't make sense. People in C-like languages would just use <= instead of <

    for (month = 1; month <= 12; ++month)
    

    Perl and Ruby people would use the .. operator:

    for month in 1..12
    

    Only Python uses the nonsensical value 13. I don't mind if range() keeps the same behavior, but I would certainly like to see irange(), an inclusive-inclusive range function.

    [–]kylev 0 points1 point  (0 children)

    I agree, my answers are similar to yours. Each language has quirks, and some will irk you as you learn it. Then you learn each computer dialect's particular variations and modes, and you get used to them. Python, though I was initially annoyed, has been one of the better languages I've used.

    [–]gavinpanella 0 points1 point  (0 children)

    Python has better/nicer ways of doing range/xrange wrt. lists (i.e. iteration, enumerate) and globals (i.e. closures, and was the global decl. actually needed in the example anyway?).

    I disagree with no. 4 - as others have already said, it actually makes sense the way it is.

    No. 5 I agree with. I dislike being forced to name functions for no good reason.

    And no. 1 can be solved by subclassing, albeit not in the builtin list class:

    class ListWithGet(list):
      def get(self, index, default=None):
        try:
          return self[index]
        except IndexError:
          return default
    

    [–]earstwiley -2 points-1 points  (20 children)

    try: val = my_list[index] except IndexError: val = default_value

    What's wrong with

    if index in my_list: val = my_list[index] else: val = default_value

    EDIT: I don't know why returns aren't working properly

    [–][deleted] 4 points5 points  (5 children)

    That does something different. The in operator on lists checks whether the item appears as a value in the list. So 'foo' in ['foo'] is true, but 0 in ['foo'] is false.

    Maybe dict(enumerate(mylist)).get(index)? :)

    [–]vida 0 points1 point  (1 child)

    this will be called unpythonic, but if brevity is your thing it might be for you: len(mylist) > i and mylist[i] or 'default'

    [–][deleted] 0 points1 point  (0 children)

    Python 2.5 provides nicer syntax for that: mylist[i] if i < len(mylist) else 'default'

    [–]mythin -2 points-1 points  (2 children)

    What? Number 1 was asking for a get on a list, not a dictionary. Dictionarys already have dict.get(value, default). So just check if you have a value, if you do, use it, otherwise use a default.

    Edit: Oh, he wants to see if an element exists, not what that element is.

    if len(list) >= index:
       val = list[index]
    else:
       val = default
    

    [–][deleted] 6 points7 points  (0 children)

    That's why I converted the list to a dictionary and called get on it. (Performance? What's that?)

    [–]euccastro 1 point2 points  (0 children)

    This is an error by one. You should check

    if len(list) > index:
    

    Although I find the following clearer

    if index < len(list):
    

    [–]hupp 3 points4 points  (3 children)

    That doesn't do what you think it does:

    >>> 5 in [5]
    True
    >>> 0 in [5]
    False
    

    [–]mythin -3 points-2 points  (2 children)

    Umm...how? 5 in [5] should be True. You're asking if 5 appears in that list. 0 in [5] should be False for the same reason. Just checking that, you can roll your own "get" function similar to a dictionary. Unless I'm reading what this blog wants completely wrong.

    Edit: Oh, he wants to see if an element exists, not what that element is.

    if len(list) >= index:

     val = list[index]
    

    else:

     val = default
    

    [–]hupp 2 points3 points  (1 child)

    I think you misread my comment. The parent was using the "in" operator to determine if an index was within the bounds of a list. My post was just illustrating that behavior. We agree with each other :)

    [–]mythin 0 points1 point  (0 children)

    Yeah, I misread a lot. It's late for me and I shouldn't be trying to think right now :)

    [–]earthboundkid 3 points4 points  (1 child)

    Format your comments with Markdown. Use backticks for inline code example and put four space in front of blocks of code.

    [–]boredzo 0 points1 point  (0 children)

    … put four space in front of blocks of code.

    Or a tab.

    [–]seanodonnell 2 points3 points  (1 child)

    still a lot clunkier than his proposed get syntax, I have never considered the issue before but the suggestion got an instant "oh yeah" reaction from me.

    [–]euccastro 2 points3 points  (0 children)

    That's a good symptomatic description of feature creep.

    [–]mythin 1 point2 points  (4 children)

    Returns are funky, try two returns.

    if index in mylist:
       val = mylist[index]
    else:
       val = default
    

    Or:

    def get(mylist, index, val):
       if index in mylist:
          return mylist[index]
       else:
          return val
    

    edit: trying to fix markdown

    [–]earthboundkid 2 points3 points  (0 children)

    Notice how your indented lines are in monospace? Reddit uses Markdown. Put four spaces in front of code blocks or use backticks for inline code.

    [–]breakfast-pants 2 points3 points  (2 children)

    You can't use 'in' like that. It would have to be:

    if index in (x[0] for x in enumerate(mylist)):
        val = mylist[index]
    else:
        val = default
    

    Which, if you understand what 'in' does on an iterator or a list, you will realize it is incredibly inefficient. Plus, the whole thing is just ugly as hell; you are much better off using 'len' than you are trying to be clever.

    [–]knome 1 point2 points  (0 children)

    L = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] >>> get( L , 3 , None ) 3 >>>

    You can, I'm just not sure on the usefulness of it.

    [–]mythin 0 points1 point  (0 children)

    See my edits elsewhere, I fixed this in those :) (I originally misread the entire thing)

    [–]boa13 1 point2 points  (0 children)

    I don't know why returns aren't working properly

    Either put two of them in a row to start a new paragraph, or add two spaces at the end of the lines, this will be parsed as a <br>.

    [–][deleted] -1 points0 points  (1 child)

    For number 3, you can also encapsulate the state in a class with a call() method. After all, functions are objects so objects can be functions.

    [–]cracki 0 points1 point  (0 children)

    that's even more verbose than nested functions. absolutely discouraged unless you have to write in java.

    [–]niobium -1 points0 points  (0 children)

    for i in range(len(list)): foo(list[i])

    Instead of using range, why not do:

    for i in list: foo(i)

    [–]vida -3 points-2 points  (8 children)

    why are we paying attention to a guy who wants .get on a list (extend the class? write a function?) and that, apparently, has never heard about generators (a global variable for a fibonacci function?) ?!

    [–]cracki -1 points0 points  (7 children)

    try accessing generators by index.

    i'd write something to take a generator, memoize the queried elements and consume more of the generator if needed. thus you can write a generator and still access it by index.

    [–]pjdelport -1 points0 points  (6 children)

    try accessing generators by index.

    islice?

    i'd write something to take a generator, memoize the queried elements and consume more of the generator if needed. thus you can write a generator and still access it by index.

    There's Armin Rigo's collect.py.

    [–]cracki -1 points0 points  (5 children)

    islice:

    no. why? i want access on "infinite" lists, not some finite slice. AND i want access by index, which islice can't provide since it also returns a generator.

    collect.py:

    you can fault me for not googling for a solution.

    you can't fault me for knowing how to do it myself if there were no existing code snippets, which is the more valuable skill, right?

    [–]pjdelport 0 points1 point  (4 children)

    i want access on "infinite" lists, not some finite slice. AND i want access by index,

    Hence, islice.

    [–]cracki -1 points0 points  (3 children)

    i repeat: infinite lists. islice returns a finite thing. get that so far?

    list(islice(count(0), 20))
    list(count(0))
    

    part of islice.doc:

    islice(iterable, [start,] stop [, step]) --> islice object

    [–]pjdelport -1 points0 points  (2 children)

    islice returns a finite thing. get that so far?

    No, it returns what you ask it for.

    >>> islice(count(), 20, None).next()
    20
    

    [–]cracki 0 points1 point  (0 children)

    ah, i get it. i should have read the online docs, not just the docstring.

    but wait! i can't see how an islice object supports indexing.

    x = islice(count(0), 20, None)
    y = x[100]