Sunday, December 9, 2012
Better Python APIs
Oz Katz has a good entry over at his blog, Eventual Consistency, about making better APIs for your Python libraries.
Wednesday, December 5, 2012
PP #1: The always-true condition
Python Pitfalls are quirks of the Python programming language that
may trip up those new to the language or to programming. First in yet
another series.
My experience with Python is that my code usually does what I expect it to—in fact, more so than with any other programming language I've used. But that doesn't mean it's impossible to write code that doesn't do what you expect! Far from it. I see code like this on Stack Overflow pretty much every day:
while True:
command = raw_input("command> ").lower().strip()
if command == "stop" or "exit":
break
elif command == "help":
print "quit, exit: exit the program"
print "help: display this help message"
elif command:
print "Unrecognized command."
Can you guess the question? It's some variation of "No matter what command I enter, it always exits."
And the reason is always the same: a Boolean expression using or that is always truthy. (See PF #1: Truth and Consequences.) In this case, it's this line:
if command == "quit" or "exit":
The programmer who wrote that line intended it to match either "quit" or "exit." Instead, it matches any command. The reason for this becomes obvious if we look at how Python evaluates the condition.
As described in PF #2: And and/or or, the value of a Boolean or operation is truthy if either operand is truthy. The operands of or here are:
This error is particularly pernicious because it reads exactly how you would phrase it in English, and expresses the programmer's intent clearly. It's just that the meaning of or is more specific in Python than in English.
One way to write this test so that it works as expected is to explicitly tell Python to test command against both alternatives:
if command == "quit" or command == "exit":
Of course, there's a shorter and clearer way to write this in Python:
if command in ("quit", "exit"):
If you will be testing for a lot of alternatives and performance is important, use a set defined outside the loop. Testing for membership in an existing set is a lot faster than testing for membership in a tuple that's created new each time the test is executed.
exit_synonyms = set(["quit", "halt", "stop",
"end", "whoa", "exit"])
while True:
command = raw_input("command> ").lower().strip()
if command in exit_synonyms:
break
# and so on as above
My experience with Python is that my code usually does what I expect it to—in fact, more so than with any other programming language I've used. But that doesn't mean it's impossible to write code that doesn't do what you expect! Far from it. I see code like this on Stack Overflow pretty much every day:
while True:
command = raw_input("command> ").lower().strip()
if command == "stop" or "exit":
break
elif command == "help":
print "quit, exit: exit the program"
print "help: display this help message"
elif command:
print "Unrecognized command."
Can you guess the question? It's some variation of "No matter what command I enter, it always exits."
And the reason is always the same: a Boolean expression using or that is always truthy. (See PF #1: Truth and Consequences.) In this case, it's this line:
if command == "quit" or "exit":
The programmer who wrote that line intended it to match either "quit" or "exit." Instead, it matches any command. The reason for this becomes obvious if we look at how Python evaluates the condition.
As described in PF #2: And and/or or, the value of a Boolean or operation is truthy if either operand is truthy. The operands of or here are:
- command == "quit"
- "exit"
This error is particularly pernicious because it reads exactly how you would phrase it in English, and expresses the programmer's intent clearly. It's just that the meaning of or is more specific in Python than in English.
One way to write this test so that it works as expected is to explicitly tell Python to test command against both alternatives:
if command == "quit" or command == "exit":
Of course, there's a shorter and clearer way to write this in Python:
if command in ("quit", "exit"):
If you will be testing for a lot of alternatives and performance is important, use a set defined outside the loop. Testing for membership in an existing set is a lot faster than testing for membership in a tuple that's created new each time the test is executed.
exit_synonyms = set(["quit", "halt", "stop",
"end", "whoa", "exit"])
while True:
command = raw_input("command> ").lower().strip()
if command in exit_synonyms:
break
# and so on as above
Tuesday, December 4, 2012
PF #3: Non-short-circuiting Boolean operations
Third entry in the Python Foundations series, thoroughly exploring the basic building blocks of Python programs.
Wow, it's been months, hasn't it? Our last Python Foundations entry talked about how the Boolean operators and and or work in Python, including their short-circuting behavior. But what if you don't want short-circuiting? What if you want both operands of and or or to be fully evaluated even when the first operand's value is sufficient to determine the expression's truth value?
Here, we can take advantage of the fact that Booleans are a special kind of integer in Python (see IDKTAP #2: Booleans as fancy integers), and perform math on them.
If you create a table of the results of adding the various combinations of 0 and 1, which are the integer values of False and True in Python:
+ | 0 1
----------
0 | 0 1
1 | 1 2
It bears a striking resemblance to the results of the Boolean or operator: the result is truthy (see PF #1: Truth and Consequences) if either of the operands is truthy.
Similarly, the multiplication operator is a dead ringer for Boolean and:
* | 0 1
----------
0 | 0 0
1 | 0 1
Zero times anything is zero, just as False and anything is False.
If you have studied digital electronics, you may recognize this as Boolean arithmetic: arithmetic using only the values 0 and 1, where and is in fact equivalent to multiplication and or is equivalent to addition. The result of addition is "clamped" to 1 (that is, the highest result an addition can be is 1), and the only valid operations are addition and multiplication. It may seem like a toy arithmetic, but it's the foundation of everything a computer does.
We can use this equivalence of logical and arithmetic operations to implement non-short-circuiting ("long-circuiting"?) and and or in Python. To support truthy and falsy values, which may not actually be integers, we convert the operands to proper Booleans first using the bool() constructor.
bool(x) + bool(y) + bool(z) # x or y or z
bool(x) * bool(y) * bool(z) # x and y and z
Here, x, y, and z are always fully evaluated. If one of them is, say, a function call, the function is always called. In the equivalent expression using Boolean operators, remember, the expression will short-circuit as soon as it can, and may not evaluate all arguments. We explored how to exploit this behavior for fun and profit in PF #2.
Conveniently, multiplication has precedence over (is performed before) addition, just as and has precedence over or, so an expression like the following works exactly as you would expect:
bool(a) * bool(b) + bool(x) * bool(y) # a and b or x and y
In fact, this is an easy way to remember which Boolean operator has precedence: because and is equivalent to multiplication, it has precedence over or, which is equivalent to addition. This is a rule I had trouble remembering when I started programming.
So far, what we've discussed isn't really specific to Python; you can do the same non-short-circuiting Boolean math in virtually any language with only minor syntax changes. We can, however, use Python's any() and all() functions to help make our non-short-circuiting Boolean operators a little more readable. Both functions operate on a sequence.
However, the short-circuiting behavior is apparent only with generators, which "lazily" calculates a sequence one element at a time. (There will surely be a Python Foundations entry on generators in the future!) If you pass a sequence (i.e., a list or a tuple) to any() or all(), all the elements of the sequence will be fully evaluated when the sequence is constructed, before any() or all() is even involved. The fact that any() and all() short-circuit is then merely a performance optimization. Useful, in other words, but not exploitable for side effects as with and and or.
And so we can use any() and all() as non-short-circuiting or and and, respectively. x, y, and z below may be expressions of arbitrary complexity, and will be evaluated fully.
any([x, y]) # x or y
all([x, y]) # x and y
any([x, y, z]) # x or y or z (any number of arguments)
all((x, y, z)) # you may use a tuple instead of a list
As for the last example, I personally prefer using the square brackets and passing a list since it stands out more and makes it clearer what is happening, but constructing a tuple is faster, a fact which may be useful in some circumstances.
any() and all() can be nested to implement compound non-short-circuiting Boolean operations:
any([all([a, b]), all([x, y]]) # a and b or x and y
However, this gets difficult to read pretty quickly (unless, perhaps, you have experience with Lisp, where all operations are prefix).. Fortunately, any() and all() always return True or False (not the deciding element from the sequence, which may be merely truthy or falsy), so you can easily combine their results with + and *.
all([a, b]) + all([x, y]) # a and b or x and y, as above
We're almost finished here; this has become rather more in-depth than I intended! But while were talking about any() and all(), I should mention that it is something of a shame that neither returns the value that made the decision (i.e., the first truthy value for any() or the first falsy value for all()). This keeps them from being used to actually find the first truthy or falsy value in a sequence, which is especially a pity when you're using them with a generator because that value, having been consumed within any()/all(), is forever lost.
You can write your own versions of these functions that do return the deciding value, but because they are implemented in Python rather than in C, they will be slower than the built-ins. Still, here are such implementations.
def all(iterable):
for item in iterable:
if not item: return item
return True
def any(iterable):
for item in iterable:
if item: return item
return False
Wow, it's been months, hasn't it? Our last Python Foundations entry talked about how the Boolean operators and and or work in Python, including their short-circuting behavior. But what if you don't want short-circuiting? What if you want both operands of and or or to be fully evaluated even when the first operand's value is sufficient to determine the expression's truth value?
Here, we can take advantage of the fact that Booleans are a special kind of integer in Python (see IDKTAP #2: Booleans as fancy integers), and perform math on them.
If you create a table of the results of adding the various combinations of 0 and 1, which are the integer values of False and True in Python:
+ | 0 1
----------
0 | 0 1
1 | 1 2
It bears a striking resemblance to the results of the Boolean or operator: the result is truthy (see PF #1: Truth and Consequences) if either of the operands is truthy.
Similarly, the multiplication operator is a dead ringer for Boolean and:
* | 0 1
----------
0 | 0 0
1 | 0 1
Zero times anything is zero, just as False and anything is False.
If you have studied digital electronics, you may recognize this as Boolean arithmetic: arithmetic using only the values 0 and 1, where and is in fact equivalent to multiplication and or is equivalent to addition. The result of addition is "clamped" to 1 (that is, the highest result an addition can be is 1), and the only valid operations are addition and multiplication. It may seem like a toy arithmetic, but it's the foundation of everything a computer does.
We can use this equivalence of logical and arithmetic operations to implement non-short-circuiting ("long-circuiting"?) and and or in Python. To support truthy and falsy values, which may not actually be integers, we convert the operands to proper Booleans first using the bool() constructor.
bool(x) + bool(y) + bool(z) # x or y or z
bool(x) * bool(y) * bool(z) # x and y and z
Here, x, y, and z are always fully evaluated. If one of them is, say, a function call, the function is always called. In the equivalent expression using Boolean operators, remember, the expression will short-circuit as soon as it can, and may not evaluate all arguments. We explored how to exploit this behavior for fun and profit in PF #2.
Conveniently, multiplication has precedence over (is performed before) addition, just as and has precedence over or, so an expression like the following works exactly as you would expect:
bool(a) * bool(b) + bool(x) * bool(y) # a and b or x and y
In fact, this is an easy way to remember which Boolean operator has precedence: because and is equivalent to multiplication, it has precedence over or, which is equivalent to addition. This is a rule I had trouble remembering when I started programming.
So far, what we've discussed isn't really specific to Python; you can do the same non-short-circuiting Boolean math in virtually any language with only minor syntax changes. We can, however, use Python's any() and all() functions to help make our non-short-circuiting Boolean operators a little more readable. Both functions operate on a sequence.
- any() - Returns True if any element is truthy (equivalent to Boolean or)
- all() - Returns True if all elemens are truthy (equivalent to Boolean and)
However, the short-circuiting behavior is apparent only with generators, which "lazily" calculates a sequence one element at a time. (There will surely be a Python Foundations entry on generators in the future!) If you pass a sequence (i.e., a list or a tuple) to any() or all(), all the elements of the sequence will be fully evaluated when the sequence is constructed, before any() or all() is even involved. The fact that any() and all() short-circuit is then merely a performance optimization. Useful, in other words, but not exploitable for side effects as with and and or.
And so we can use any() and all() as non-short-circuiting or and and, respectively. x, y, and z below may be expressions of arbitrary complexity, and will be evaluated fully.
any([x, y]) # x or y
all([x, y]) # x and y
any([x, y, z]) # x or y or z (any number of arguments)
all((x, y, z)) # you may use a tuple instead of a list
As for the last example, I personally prefer using the square brackets and passing a list since it stands out more and makes it clearer what is happening, but constructing a tuple is faster, a fact which may be useful in some circumstances.
any() and all() can be nested to implement compound non-short-circuiting Boolean operations:
any([all([a, b]), all([x, y]]) # a and b or x and y
However, this gets difficult to read pretty quickly (unless, perhaps, you have experience with Lisp, where all operations are prefix).. Fortunately, any() and all() always return True or False (not the deciding element from the sequence, which may be merely truthy or falsy), so you can easily combine their results with + and *.
all([a, b]) + all([x, y]) # a and b or x and y, as above
We're almost finished here; this has become rather more in-depth than I intended! But while were talking about any() and all(), I should mention that it is something of a shame that neither returns the value that made the decision (i.e., the first truthy value for any() or the first falsy value for all()). This keeps them from being used to actually find the first truthy or falsy value in a sequence, which is especially a pity when you're using them with a generator because that value, having been consumed within any()/all(), is forever lost.
You can write your own versions of these functions that do return the deciding value, but because they are implemented in Python rather than in C, they will be slower than the built-ins. Still, here are such implementations.
def all(iterable):
for item in iterable:
if not item: return item
return True
def any(iterable):
for item in iterable:
if item: return item
return False
Wednesday, July 11, 2012
IDKTAP #3: Backticks
Did you know that quoting an expression using backticks is an alias for repr() in Python 2.x? That is, it prints the "representation" of the result of the expression, which is generally what you'd have to paste into your Python source file to produce that same object.
print `"Think, it ain't illegal yet!"`
They took this out in Python 3.
print `"Think, it ain't illegal yet!"`
They took this out in Python 3.
Thursday, July 5, 2012
IDKTAP #2: Booleans as fancy integers
I Didn't Know That About Python uncovers surprising tidbits about our favorite programming language.
In Python, a Boolean (type bool) is a subtype of integers. True is equal to 1 and False is equal to 0.
True == 1 # True
False == 0 # True
The special methods called __str__ and __repr__ define how an object is converted to a string and how it is represented in an interactive context, respectively. So bool is basically implemented like this:
class bool(int):
def __str__(self):
return "True" if self else "False"
__repr__ = __str__
True, False = bool(1), bool(0)
(There are some other details I'm glossing over, such as the fact that True and False are singletons and only ever have one instance. That is, bool(1) is always the same object. The example above does not have this behavior. For a fun exercise, try adding it.)
The surprising thing is that in Python 2, True and False are simply built-in identifiers assigned to two specific bool singletons. This means that they can be reassigned:
True = 3
This will very likely cause quite odd behavior in your programs, which is why Python 3 reserves True and False as actual language keywords and doesn't let you reassign them. Still, issubclass(bool, int) remains True.
In Python, a Boolean (type bool) is a subtype of integers. True is equal to 1 and False is equal to 0.
True == 1 # True
False == 0 # True
The special methods called __str__ and __repr__ define how an object is converted to a string and how it is represented in an interactive context, respectively. So bool is basically implemented like this:
class bool(int):
def __str__(self):
return "True" if self else "False"
__repr__ = __str__
True, False = bool(1), bool(0)
(There are some other details I'm glossing over, such as the fact that True and False are singletons and only ever have one instance. That is, bool(1) is always the same object. The example above does not have this behavior. For a fun exercise, try adding it.)
The surprising thing is that in Python 2, True and False are simply built-in identifiers assigned to two specific bool singletons. This means that they can be reassigned:
True = 3
This will very likely cause quite odd behavior in your programs, which is why Python 3 reserves True and False as actual language keywords and doesn't let you reassign them. Still, issubclass(bool, int) remains True.
Sunday, July 1, 2012
PF #2: And and/or or
Second in the Python Foundations series.
In the previous installment of Python Foundations, I described the informal nature of truth and falsity in Python (dubbed "truthy" and "falsy"). Now let's explore how the Boolean operators and and or interact with such values.
In many languages, you'd expect the result of a Boolean operation (an operation between two truth values) to be True or False. But since Python considers a wide range of values True and False in a Boolean context, it is useful to return one of the operands. That is, when you combine two values using and or or, you get one of the values you put in.
In Python, and and or are short-circuiting. They always evaluate their first operand, and if that result is sufficient to decide the truth value of the expression, they stop and don't evaluate their second operand. Say what?
OK, have a look at the truth table for and:
AND | True False
-----------------------
True | True False
False | False False
As you can see, False and anything is always False. Therefore, when the first operand of and is falsy, Python need not evaluate the second operand, and in fact does not. This not only saves time, it can be exploited for useful side effects (more on that later).
OR | True False
-----------------------
True | True True
False | True False
This truth table for or similarly reveals that when the first operand is True, the result is always True, so Python doesn't need to evaluate the second operand of or when the first operand is truthy. And once again, since it doesn't need to, it doesn't.
What does it mean to not evaluate the second operand of and or or? Among other things it means that if the second operand is a function call, that second function is never called:
v = a() or b()
If a() returns something truthy, b() is never even called. But what is the value of v? Python assigns v the return value of the operand that decided the result: a() if a's return value is truthy, or b() if a's return value is falsy. It works similarly with and, except the result is a's return value if it is falsy and b's otherwise. In short, the actual operand values are used in place of the constants True and False.
Short-circuiting lets you use and and or as substitutes for if statements in certain circumstances. It's very easy to write unreadable code using short-circuiting and and or; there was an idiom that was commonly used in an attempt to make up for the fact that Python didn't have a proper ternary conditional operator. Then Python added a proper ternary conditional operator (v = a if b else c) and everybody forgot about the hackish workaround. But simpler uses of short-circuiting are pretty readable and useful.
For example, easily provide a default value for blank inputs:
name = raw_input("What is your name? ") or "Jude"
print "Hey,", name, "don't make it bad"
Or check to make sure something is callable before calling it:
callable(func) and func()
I find those generally more readable than the alternatives using if statements:
name = raw_input("What is your name? ")
if not name:
name = "Jude"
if callable(func):
func()
Short-circuiting is, by the way, the reason Python doesn't let you override and and or using special methods on your own objects the way you can with arithmetic operators. Both operands would be evaluated before the operation is performed, so short-circuiting simply isn't possible. (There is in fact a proposal to change that, but it hasn't been implemented yet.)
In the previous installment of Python Foundations, I described the informal nature of truth and falsity in Python (dubbed "truthy" and "falsy"). Now let's explore how the Boolean operators and and or interact with such values.
In many languages, you'd expect the result of a Boolean operation (an operation between two truth values) to be True or False. But since Python considers a wide range of values True and False in a Boolean context, it is useful to return one of the operands. That is, when you combine two values using and or or, you get one of the values you put in.
In Python, and and or are short-circuiting. They always evaluate their first operand, and if that result is sufficient to decide the truth value of the expression, they stop and don't evaluate their second operand. Say what?
OK, have a look at the truth table for and:
AND | True False
-----------------------
True | True False
False | False False
As you can see, False and anything is always False. Therefore, when the first operand of and is falsy, Python need not evaluate the second operand, and in fact does not. This not only saves time, it can be exploited for useful side effects (more on that later).
OR | True False
-----------------------
True | True True
False | True False
This truth table for or similarly reveals that when the first operand is True, the result is always True, so Python doesn't need to evaluate the second operand of or when the first operand is truthy. And once again, since it doesn't need to, it doesn't.
What does it mean to not evaluate the second operand of and or or? Among other things it means that if the second operand is a function call, that second function is never called:
v = a() or b()
If a() returns something truthy, b() is never even called. But what is the value of v? Python assigns v the return value of the operand that decided the result: a() if a's return value is truthy, or b() if a's return value is falsy. It works similarly with and, except the result is a's return value if it is falsy and b's otherwise. In short, the actual operand values are used in place of the constants True and False.
Short-circuiting lets you use and and or as substitutes for if statements in certain circumstances. It's very easy to write unreadable code using short-circuiting and and or; there was an idiom that was commonly used in an attempt to make up for the fact that Python didn't have a proper ternary conditional operator. Then Python added a proper ternary conditional operator (v = a if b else c) and everybody forgot about the hackish workaround. But simpler uses of short-circuiting are pretty readable and useful.
For example, easily provide a default value for blank inputs:
name = raw_input("What is your name? ") or "Jude"
print "Hey,", name, "don't make it bad"
Or check to make sure something is callable before calling it:
callable(func) and func()
I find those generally more readable than the alternatives using if statements:
name = raw_input("What is your name? ")
if not name:
name = "Jude"
if callable(func):
func()
Short-circuiting is, by the way, the reason Python doesn't let you override and and or using special methods on your own objects the way you can with arithmetic operators. Both operands would be evaluated before the operation is performed, so short-circuiting simply isn't possible. (There is in fact a proposal to change that, but it hasn't been implemented yet.)
Tuesday, June 26, 2012
Recipe #1: An indexable file class
Python Recipes are simple bits of my code you can use freely in your own programs. First in a series. Yes, I'm gonna have a lot of different series of posts on this blog.
Here's a file-like class that allows access to lines in the file using Python indexing or slicing notation without reading the whole file into memory. It's sort of a list/file hybrid since it does have methods for incrementally reading from the file, too.
class linefile(object):
def __init__(self, path):
self.file = open(path)
self.offsets = []
def __enter__(self):
return self
def __exit__(self, *e):
self.file.close()
def readline(self):
self.offsets.append(int(self.file.tell()))
text = self.file.readline()
if not text:
self.offsets.pop()
return text
def readlines(self):
for text in self:
pass
return self
def next(self):
return self.readline()
def __iter__(self):
text = self.readline()
while text:
yield text
text = self.readline()
def __getitem__(self, index):
offset = self.offsets[index]
tell = self.file.tell()
if type(index) is slice:
if slice.step == 1: # step is 1, this is fastest
self.file.seek(offset[0])
text = [self.file.readline() for line in offset]
else:
text = []
for o in offset:
self.file.seek(o)
text.append(self.file.readline())
else:
self.file.seek(offset)
text = self.file.readline()
self.file.seek(tell)
return text
def __len__(self):
return len(self.offsets)
Unlike a regular file, a linefile is read-only and text-only. Internally, the instance stores the offsets of each line that has been read. The lines themselves are retrieved from the file on demand; the instance does not store any lines. Positive offsets index from the beginning of the file and negative offsets index from the last line read from the file, which may not be the end of the file. (Index -1, then, is the line before the current one.) You can use the readlines() method to read all the lines and thereby note the offset of each line of the file; this will allow the entire file to be accessed by index or slice as though it were a list. The linefile object is iterable and is also a context manager (i.e, it supports the with statement).
Here's a file-like class that allows access to lines in the file using Python indexing or slicing notation without reading the whole file into memory. It's sort of a list/file hybrid since it does have methods for incrementally reading from the file, too.
class linefile(object):
def __init__(self, path):
self.file = open(path)
self.offsets = []
def __enter__(self):
return self
def __exit__(self, *e):
self.file.close()
def readline(self):
self.offsets.append(int(self.file.tell()))
text = self.file.readline()
if not text:
self.offsets.pop()
return text
def readlines(self):
for text in self:
pass
return self
def next(self):
return self.readline()
def __iter__(self):
text = self.readline()
while text:
yield text
text = self.readline()
def __getitem__(self, index):
offset = self.offsets[index]
tell = self.file.tell()
if type(index) is slice:
if slice.step == 1: # step is 1, this is fastest
self.file.seek(offset[0])
text = [self.file.readline() for line in offset]
else:
text = []
for o in offset:
self.file.seek(o)
text.append(self.file.readline())
else:
self.file.seek(offset)
text = self.file.readline()
self.file.seek(tell)
return text
def __len__(self):
return len(self.offsets)
Unlike a regular file, a linefile is read-only and text-only. Internally, the instance stores the offsets of each line that has been read. The lines themselves are retrieved from the file on demand; the instance does not store any lines. Positive offsets index from the beginning of the file and negative offsets index from the last line read from the file, which may not be the end of the file. (Index -1, then, is the line before the current one.) You can use the readlines() method to read all the lines and thereby note the offset of each line of the file; this will allow the entire file to be accessed by index or slice as though it were a list. The linefile object is iterable and is also a context manager (i.e, it supports the with statement).
Subscribe to:
Posts (Atom)