Why pass?

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

rflrob
Posts: 235
Joined: Wed Oct 31, 2007 6:45 pm UTC
Location: Berkeley, CA, USA, Terra, Sol
Contact:

Why pass?

Postby rflrob » Sat Jul 31, 2010 4:56 am UTC

I'm teaching a python programming crash-course this summer, and previous years' lecture notes included the pass statement, which I'm considering not teaching (our time is limited). I've only ever used it for stubbing out functions, and the like, e.g.

Code: Select all

def fun(my_args):
    pass

but it seems to me that if you're going to write a function(/if statement/except clause, etc.) that does nothing, you might as well just comment out the lines, or return nothing, or just have an empty string, or any number of other ways to do it. I trust that Guido had a good reason for including it in the language, but that reason is not obvious to me (possibly because I am not Dutch). Any other ideas?
Ten is approximately infinity (It's very large)
Ten is approximately zero (It's very small)

++$_
Mo' Money
Posts: 2370
Joined: Thu Nov 01, 2007 4:06 am UTC

Re: Why pass?

Postby ++$_ » Sat Jul 31, 2010 5:34 am UTC

It's to prevent indented blocks from being empty for parsing reasons, I think. It gives you something officially sanctioned to do there.

Also, it's easier to teach people to use pass if they want to do nothing than to tell them to put an empty string when they want to do nothing. The latter raises more questions than it answers, I think.

rflrob
Posts: 235
Joined: Wed Oct 31, 2007 6:45 pm UTC
Location: Berkeley, CA, USA, Terra, Sol
Contact:

Re: Why pass?

Postby rflrob » Sat Jul 31, 2010 6:34 am UTC

++$_ wrote:It's to prevent indented blocks from being empty for parsing reasons, I think. It gives you something officially sanctioned to do there.

Also, it's easier to teach people to use pass if they want to do nothing than to tell them to put an empty string when they want to do nothing. The latter raises more questions than it answers, I think.


I get that it allows indented blocks to be empty, but what's still not making sense to me is why you'd want to have an empty block. If you want to do nothing, just don't have a block where you need to do anything, rather than making something where you need to do something, then passing

As far as the empty string business, a co-teacher of mine told the students "Oh, you can use triple quotes to comment out a bunch of code" (similar to /* */ in C), and that apparently didn't set off any red flags whatsoever with the students. I bit my tongue because I didn't really want to explain why what that was doing was both a) okay, and b) really, more than a little weird, and c) how to do it in emacs (btw... how do you do it in emacs?).
Ten is approximately infinity (It's very large)
Ten is approximately zero (It's very small)

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Why pass?

Postby Xanthir » Sat Jul 31, 2010 7:04 am UTC

Empty blocks are good for a few reasons, mainly stubbing purposes. Sometimes it's good to go ahead and write down your intention to write a function or a class before you actually get around to writing the implementation.

It's also occasionally useful when you're subclassing something with slightly different expectations and want to disable a function you've inherited, or something demands to be passed a class but you don't actually need the functionality that it would use the class to provide.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

Axidos
Posts: 167
Joined: Tue Jan 20, 2009 12:02 pm UTC
Location: trapped in a profile factory please send help

Re: Why pass?

Postby Axidos » Sat Jul 31, 2010 9:57 am UTC

To add to the very good previous posts - pass exists for readability. It says "I have deliberately put nothing here because this portion of code is supposed to do absolutely nothing. If this block is empty that's absolutely deliberate and not a symptom of bad indentation or code someone should have removed."

It's a standard way of stubbing which doesn't leave you wondering why someone just wrote an empty string in that block.

User avatar
PM 2Ring
Posts: 3715
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Why pass?

Postby PM 2Ring » Sat Jul 31, 2010 10:37 am UTC

The Python docs give this example:

Code: Select all

>>> while True:
...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
...

Another situation is in complicated if statements where one of the conditions appears like it might sometimes be true, but that deeper logical analysis shows that the condition will never be true. Of course, such statements should be explicitly commented.
Here's a simplified example:

Code: Select all

def move_jump_or_finish():
    if next_to_beeper():
        turn_off()
    elif front_is_clear():
        move()
    elif right_is_clear(): # always false
        pass
    else:
        jump_one_hurdle()

Some may argue that such code should be redesigned...

Personally, I mostly use pass during development, as a placeholder. Here's an example:

Code: Select all

elif event.button == 2:
    #print 'Single press of middle button'         
    pass

I don't remove such debugging print statements until I've finished working on a program, and occasionally leave them in programs that work adequately but that I intend to get back to at some stage.

OTOH, I also do things like

Code: Select all

#Debugging output level. 0=no debugging
verbosity = 0

#Debug output routine               
def printe(v, s):
      ''' Print to stderr '''
      if v <= verbosity:
          print >>sys.stderr, s

or

Code: Select all

#Debug output routine               
def print_stderr(s):
    ''' Print to stderr '''
    print >>sys.stderr, s

def print_null(s): pass                 

printe = debug and print_stderr or print_null   


I occasionally use the triple-quote technique for temporarily commenting-out code blocks during development; I find it handy and don't see why it'd be considered confusing, unless it appears at the head of the function.

keeperofdakeys
Posts: 658
Joined: Wed Oct 01, 2008 6:04 am UTC

Re: Why pass?

Postby keeperofdakeys » Sat Jul 31, 2010 12:18 pm UTC

This may be slightly trivial, but I have found it handy in try except blocks:

Code: Select all

try:
   //code
except SpecificError:
   pass

User avatar
Cleverbeans
Posts: 1378
Joined: Wed Mar 26, 2008 1:16 pm UTC

Re: Why pass?

Postby Cleverbeans » Sat Jul 31, 2010 4:30 pm UTC

keeperofdakeys wrote:This may be slightly trivial, but I have found it handy in try except blocks


This is where I find use for it as well.
"Labor is prior to, and independent of, capital. Capital is only the fruit of labor, and could never have existed if labor had not first existed. Labor is the superior of capital, and deserves much the higher consideration." - Abraham Lincoln

User avatar
TNorthover
Posts: 191
Joined: Wed May 06, 2009 7:11 am UTC
Location: Cambridge, UK

Re: Why pass?

Postby TNorthover » Sat Jul 31, 2010 6:59 pm UTC

Unless I've missed it no-one has mentioned methods of polymorphic objects. Sometimes you want a function to exist and be callable without error, but not actually do anything.

++$_
Mo' Money
Posts: 2370
Joined: Thu Nov 01, 2007 4:06 am UTC

Re: Why pass?

Postby ++$_ » Sat Jul 31, 2010 7:38 pm UTC

rflrob wrote:c) how to do it in emacs (btw... how do you do it in emacs?).
I suggest pressing the quote key three times, then moving the cursor to the end of the region and pressing the quote key three times again.

(Did I miss something?)

Actually, I suggest not using this method to comment things out, because it will not work all the time. The code between the triple quotes ends up as an expression, so something like

Code: Select all

return a + """b""" c
is a syntax error, whereas

Code: Select all

return a + /* b */ c;
(in C) isn't.

EDIT: Semicolon!

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Why pass?

Postby troyp » Sun Aug 01, 2010 4:03 am UTC

PM 2Ring wrote:Another situation is in complicated if statements where one of the conditions appears like it might sometimes be true, but that deeper logical analysis shows that the condition will never be true. Of course, such statements should be explicitly commented.

If I test for a case that will should always be false, I always raise an exception rather than "pass"ing - that way it's pretty much self-commenting, plus it acts like an assertion.

I think it's good to have a pass keyword for readability. Sure, it's probably never necessary: you could use a dummy expression like "" wherever a pass command would go, but it's clearer to use an empty command to indicate your intent.
Apart from humans, it may also be clearer to tools. I've never used anything like PyLint, but I wouldn't be surprised if a code-checking tool warned about superfluous expressions appearing in your code.

Of course, you wouldn't need a special keyword to indicate an empty command if Python had optional semicolons like a sensible language.

User avatar
phlip
Restorer of Worlds
Posts: 7573
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Why pass?

Postby phlip » Sun Aug 01, 2010 1:02 pm UTC

Code: Select all

class SomethingWentWrongError(EnvironmentError):
   pass

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: Why pass?

Postby TheChewanater » Sun Aug 01, 2010 3:38 pm UTC

Code: Select all

class foo:
  def __iadd__ (self, from):
    if (from != '\x00'):
      chars += from
      return True
    else:
      return False

Code: Select all

class bar:
  def readNextChar ():
    if (moreChars) return nextChar
    else return '\x00'

Code: Select all

while foo += bar.readNextChar (): pass


I've never done that, but I guess it could conceivable be used sort of like this.
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

rflrob
Posts: 235
Joined: Wed Oct 31, 2007 6:45 pm UTC
Location: Berkeley, CA, USA, Terra, Sol
Contact:

Re: Why pass?

Postby rflrob » Sun Aug 01, 2010 11:32 pm UTC

This:
TNorthover wrote:Unless I've missed it no-one has mentioned methods of polymorphic objects. Sometimes you want a function to exist and be callable without error, but not actually do anything.

and this:
phlip wrote:

Code: Select all

class SomethingWentWrongError(EnvironmentError):
   pass


were the good uses that I couldn't think of. Thanks!

As a side note:
TheChewanater wrote:

Code: Select all

while foo += bar.readNextChar (): pass


gives me a syntax error in Python 2.6.4. It's a perfectly good C idiom that just isn't legal in Python.
Ten is approximately infinity (It's very large)
Ten is approximately zero (It's very small)

User avatar
Xanthir
My HERO!!!
Posts: 5426
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex
Contact:

Re: Why pass?

Postby Xanthir » Mon Aug 02, 2010 12:41 am UTC

If I recall, that's because = and friends are statements rather than expressions, and if/while/etc require expressions in their clause.

I'm pretty sure they were set up that way specifically so you couldn't do that.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: Why pass?

Postby TheChewanater » Mon Aug 02, 2010 12:54 am UTC

I'm pretty sure you make make your class have a __iadd__ function with a return value. I could be wrong.

I guess you might have:

Code: Select all

while doSomethingIfPossibleOtherwiseReturnFalse (): pass
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
phlip
Restorer of Worlds
Posts: 7573
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Why pass?

Postby phlip » Mon Aug 02, 2010 1:04 am UTC

The assignment-is-not-an-expression thing, incidentally, makes regexes in Python a pain... this entirely reasonable Perl code:

Code: Select all

if ($line =~ /regex 1/)
{
  print "It matches regex 1!\n";
}
elsif ($line =~ /regex 2 - (.*)/)
{
  print "It matches regex 2! The rest is $1\n";
}
elsif ($line =~ /regex 3/)
{
  print "It matches regex 3!\n";
}
else
{
  print "It didn't match any of them!\n";
}
directly translates into this horrible Python code:

Code: Select all

match = re.search('regex 1', line)
if match:
  print "It matches regex 1!"
else:
  match = re.search('regex 2 - (.*)', line)
  if match:
    print "It matches regex 2! The rest is %s" % match.group(1)
  else:
    match = re.search('regex 3', line)
    if match:
      print "It matches regex 3!"
    else:
      print "It didn't match any of them!"
which gets very ugly when you've got a lot of formats to match. Luckily, this "match something from a list of patterns" deal is something I'm usually only doing when I'm hacking something together anyway, reading the output of some command or webpage or something, so using a hack to fix the problem isn't unreasonable:

Code: Select all

def store(v,x):
  v[0] = [x]
  return x

match = [None]
if store(match, re.search('regex 1', line)):
  print "It matches regex 1!"
elif store(match, re.search('regex 2 - (.*)', line)):
  print "It matches regex 2! The rest is %s" % match[0].group(1)
elif store(match, re.search('regex 3', line)):
  print "It matches regex 3!"
else:
  print "It didn't match any of them!"

I guess if this came up in a not-hack program, I'd do something more pythonish, like make a list of tuples of (regex,function), and loop through them to find the find the first one that matches, and call the appropriate function with the match object. Or something like that.

TheChewanater wrote:I'm pretty sure you make make your class have a __iadd__ function with a return value. I could be wrong.

Nope... it's a syntax error, regardless of the types of the values involved. Anyway, the return value from __iadd__ is assigned to the variable... "x += y" becomes "x = x.__iadd__(y)".

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
Cosmologicon
Posts: 1806
Joined: Sat Nov 25, 2006 9:47 am UTC
Location: Cambridge MA USA
Contact:

Re: Why pass?

Postby Cosmologicon » Mon Aug 02, 2010 3:26 pm UTC

PM 2Ring wrote:Another situation is in complicated if statements where one of the conditions appears like it might sometimes be true, but that deeper logical analysis shows that the condition will never be true. Of course, such statements should be explicitly commented.
Here's a simplified example:

Code: Select all

def move_jump_or_finish():
    if next_to_beeper():
        turn_off()
    elif front_is_clear():
        move()
    elif right_is_clear(): # always false
        pass
    else:
        jump_one_hurdle()

Some may argue that such code should be redesigned...

A condition needn't always be false for pass to make sense in a compound if statement. Sometimes that's the clearest way to write conditions:

Code: Select all

if asleep():
    pass
elif hungry():
    eat()
elif tired():
    go_to_sleep()
else:
    watch_tv()
In order to get the same logic without an empty statement, you'd either need to nest your ifs, or evaluate a condition more than once (or store it in a variable), both of which are arguably less clear than a pass statement.

User avatar
phlip
Restorer of Worlds
Posts: 7573
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Why pass?

Postby phlip » Mon Aug 02, 2010 11:58 pm UTC

Cosmologicon wrote:

Code: Select all

if asleep():
    pass
elif hungry():
    eat()
elif tired():
    go_to_sleep()
else:
    watch_tv()
There's a serious bug in there... better would be:

Code: Select all

if asleep():
    pass
elif hungry():
    eat()
elif tired():
    go_to_sleep()
elif anything_good_on_tv(): # always false
    watch_tv()
else:
    play_computer_games()

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5101
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Why pass?

Postby Xeio » Tue Aug 03, 2010 1:54 am UTC

I'd also caution against going to sleep, as you'll never wake up again.... :P

User avatar
Cosmologicon
Posts: 1806
Joined: Sat Nov 25, 2006 9:47 am UTC
Location: Cambridge MA USA
Contact:

Re: Why pass?

Postby Cosmologicon » Tue Aug 03, 2010 10:46 am UTC

Well obviously waking up is taken care of in the event handler for the alarm clock. :)

not baby Newt
Posts: 110
Joined: Wed Feb 03, 2010 11:30 pm UTC

Re: Why pass?

Postby not baby Newt » Thu Aug 05, 2010 10:14 am UTC

phlip wrote:
TheChewanater wrote:I'm pretty sure you make make your class have a __iadd__ function with a return value. I could be wrong.

Nope... it's a syntax error, regardless of the types of the values involved. Anyway, the return value from __iadd__ is assigned to the variable... "x += y" becomes "x = x.__iadd__(y)".

You could implement a java-style add() method if you really like expressions that do things. Might be a tad unpythonic though?

Code: Select all

while foo.add( bar.readNextChar ()): pass

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Why pass?

Postby troyp » Thu Aug 05, 2010 10:35 pm UTC

Python has plenty of expressions with side-effects, eg. lst.reverse().
Although admittedly, they usually only return None or some success indicator.

Expressions like assignment in C/++ or stream operators in C++ where the operator does something with a value and then returns the same value with a result (allowing chaining)...yeah, those are probably unpythonic. Python has a side-effecting reverse() returning None and a pure reversed() method for lists, but combining the two in a method would be regarded as bad form.

I think the reason assignment isn't an expression in Python is specifically to control coding style. If you could use assignment as an expression, people would, and Guido wants to prevent that.
The one approved use-case (chained assignment, like a=b=0) is supported by special syntax (thus furthering the confusion of people coming from other languages ;-))

[edit: adjusted paragraph spacing for clarity]
Last edited by troyp on Fri Aug 06, 2010 3:00 am UTC, edited 1 time in total.

User avatar
RoadieRich
The Black Hand
Posts: 1037
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Behind you

Re: Why pass?

Postby RoadieRich » Fri Aug 06, 2010 12:05 am UTC

troyp wrote:Python has plenty of expressions with side-effects, eg. lst.reverse().
Although admittedly, they usually only return None or some success indicator. Expressions like assignment in C/++ or stream operators in C++ where the operator does something with a value and then returns the same value with a result (allowing chaining)...yeah, those are probably unpythonic. Python has a side-effecting reverse() returning None and a pure reversed() method for lists, but combining the two in a method would be regarded as bad form.
I think the reason assignment isn't an expression in Python is specifically to control coding style. If you could use assignment as an expression, people would, and Guido wants to prevent that.

The one approved use-case (chained assignment, like a=b=0) is supported by special syntax (thus furthering the confusion of people coming from other languages ;-))

I can't remember where I read it, but I'm pretty sure there's a PEP* which states functions, methods etc should either have a side effect, or a return value, but never both. Due to the way python functions work, if you don't explicitly return a value, it will return None, so list.reverse() complies. reversed(), on the other hand, doesn't have any side effects (the original list is unchanged), so it can return a value.

*At least, I think it's a PEP. It might be one of those generic "how to write good code" resources.
73, de KE8BSL loc EN26.

troyp
Posts: 557
Joined: Thu May 22, 2008 9:20 pm UTC
Location: Lismore, NSW

Re: Why pass?

Postby troyp » Fri Aug 06, 2010 2:57 am UTC

Exactly.
I tend to agree with them about this. It can be handy having side-effecting functions that modify an object and return the object as well, since it lets you chain a series of modifications together with dots (I've heard this style called "cascades", a term I couldn't recall earlier). However, it can become obfuscating if you start getting confused about which functions are mutating the original argument and which are returning a new object. I think in general it's better to write a series of mutations as a series of statements rather than embedding them in a complex expression. Especially in Python, which encourages a clean, uncluttered style of coding.

Anyhow, the main point I was making before is that, in general, expressions with side-effects are not at all unpythonic. Most side-effects in Python are achieved with functions or methods (which are expressions). Python3 even replaces the print statement with a print method, removing one of the few exceptions.
Statements in Python are usually either for definitions or control flow.

not baby Newt
Posts: 110
Joined: Wed Feb 03, 2010 11:30 pm UTC

Re: Why pass?

Postby not baby Newt » Fri Aug 06, 2010 4:29 pm UTC

troyp wrote: I think in general it's better to write a series of mutations as a series of statements rather than embedding them in a complex expression. Especially in Python, which encourages a clean, uncluttered style of coding.

Anyhow, the main point I was making before is that, in general, expressions with side-effects are not at all unpythonic. Most side-effects in Python are achieved with functions or methods (which are expressions).

This is handy to know. Also slightly amusing (to me) that expressions with side effects are pythonic long as they are used as statements :D

User avatar
PM 2Ring
Posts: 3715
Joined: Mon Jan 26, 2009 3:19 pm UTC
Location: Sydney, Australia

Re: Why pass?

Postby PM 2Ring » Fri Aug 06, 2010 5:25 pm UTC

Speaking of side effects, I find this sort of thing handy from time to time:

Code: Select all

#! /usr/bin/env python
''' Create an accumulator function from a 2 arg function.
    Like a cross between map() & reduce().
'''
def acc(func, v0=0):
    def f(v, a=[v0]):
        a[0] = func(a[0], v)
        return a[0]
    return f

def main():
    f = acc(lambda u,v: u+v)
    print [f(i) for i in xrange(1,12)]

    f = acc(lambda u,v: u*v, 1)
    print [f(i) for i in xrange(1,12)]

if __name__ == '__main__':
    main()

User avatar
thoughtfully
Posts: 2253
Joined: Thu Nov 01, 2007 12:25 am UTC
Location: Minneapolis, MN
Contact:

Re: Why pass?

Postby thoughtfully » Sat Aug 07, 2010 1:27 pm UTC

Here's another one: multiple inheritance.

Code: Select all

class Foo(Mixin, Ancestor):
    pass

See, for example, the SocketServer documentation.
Image
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
-- Antoine de Saint-Exupery

jagdragon
Posts: 19
Joined: Sat Feb 28, 2009 11:35 pm UTC

Re: Why pass?

Postby jagdragon » Sun Aug 08, 2010 1:14 am UTC

I find the use of pass most common while I'm working on larger programs - If one does not have a pass there, a syntax error occurs, which is really annoying if you just wanted to build up a skeleton of the program you're building.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 11 guests