What does Pop do in Python

Stacks



Chapter 18

Rough translation - Please send feedback about errors and ambiguities to Mike Müller

18.1 Abstract data types

So far we have only seen specific data types for which we have fully specified how they are implemented. For example, the card class represents a card with 2 integers. As we discussed at the time, this is not the only way to display a map; there are many alternative implementations.

A abstract data type (ADT) specifies a set of operations (or methods) and the semantics of the operations (what they do), it does not specify how they are implemented. That makes it abstract.

Why is this useful?

  • The specification of an algorithm becomes easier if one can already designate the necessary operations without having to worry about the operations to be carried out at the same time.
  • Since there are typically many ways to implement an ADT, it can be useful to develop an algorithm that can be used with any of the possible implementations.
  • Frequently used ADTs, such as the stack discussed in this chapter, are often implemented in standard libraries. In this way they can be used by many programmers.
  • The operations on ADT represent a general high-level language for the specification and communication via algorithms.

When we talk about ADT, we often make a distinction between code that uses the ADT, what is called Client code and the code that implemen the DT, the Library code.

18.2 The abstract data type stack

In this chapter we will look at a common ADT, the Stack (eng. pile), employ. A stack is a collection, i.e. a data structure that contains several elements. Other collections we have come across include lists and dictionaries.

An ADT is defined by the operations that can be performed on it. These will interface called. The interface for the ADT stack consists of these operations:

__init__
Initialize a new, empty stack.
push
Add a new item to the stack.
pop
Remove an element from the stack and return it as the result of the method call. The item returned is always the last one added.
is empty
Test whether the stack is empty.

A stack is sometimes referred to as a "last in, first out" or LIFO (last in, first out) data structure because the element that was added last is removed first.

18.3 Implementation of stacks with Python lists

The list operations that Python provides are similar to those that define a stack. The interface isn't exactly what it's supposed to be, but we can write code that translates from the ADT stack to built-in operations.

This code we implementation of the ADT stack. In general, an implementation is a set of methods that meet the syntactic and semantic requirements of an interface.

Here is an implementation of the ADT stack using a Python list:

class stack:
def__init __ (self):
self.items = []

defpush (self, item):
self.items.append (item)

defpop (self):
return self.items.pop ()

defistLeer (self):
return (self.items == [])

An object stack contains an attribute called items. This was a list of elements in the stack. The initialization method sets items to an empty list.

The push method appends a new element to items and puts it on the stack. To remove an element from the stack, the pop method uses the homonymous * Note list method, with which the last element of the list is removed and returned.

Finally, the method compares isEmptyitems with an empty list to check whether the stack is empty.

An implementation like this, in which the methods consist of simple calls to existing methods, is called a veneer designated. In real life, a veneer is a thin layer of good quality wood that is used in furniture making to hide underlying low quality wood. Computer scientists use this metaphor to describe a small piece of code that hides the details of an implementation and provides a simpler or more standardized interface.

18.4 Laying and fetching

A stack is a generic data typewhich means we can add any type as an element. The following example puts two integers and a string on the stack:

>>> s = Stack ()
>>> s.push (54)
>>> s.push (45)
>>> s.push ("+")

We can use the istEmpty and pop methods to remove all elements and print them out.

while not s.isLeer ():
print s.pop (),

The output looks like this + 45 54. In other words we just used a stack to print the elements backwards! Granted, this isn't the standard format for printing a list, but using a stack made it remarkably easy to implement.

You should compare this short code to the implementation of printBackwaerts in section.

There is a natural parallel between the recursive version printback and the stack algorithms shown here. The difference is that printback uses the Python runtime stack to keep track of the nodes as it goes through the list. On the way back from the recursion, the nodes are then printed out. The stack algorithm does exactly the same thing, with the difference that it uses the stack object instead of the runtime stack.

18.5 Using a stack for evaluating Postfix notations

In most programming languages, mathematical expressions are written with the operator between the two operands, for example 1 + 2. This format is called Infix notation designated. An alternative that is used by some computers becomes Postfix notation called. In postfix notation, the operator follows the operands as in 1 2 +.

The reason that Postfix notation is sometimes useful is that there is a natural way to stack expressions in Postfix notation:

  • Starting at the beginning of the expression, take one term (operator or operant) at a time.
    • If the term is an operand, put it on the stack.
    • If the term is an operator, take two operands off the stack, perform the operations on them, and put the result back on the stack.
  • When we get to the end of the expression, there should be exactly one operand left on the stack. This operand is the end result.
Apply this algorithm to the expression 1 2 as an exercise
+ 3 * on.

This example demonstrates one of the advantages of Postfix notation - there is no need to use parentheses to control the order of operations. To get the same result with infix notation we have to (1 + 2) *
3 write.

As an exercise, write an expression in Postfix notation that corresponds to 1 + 2 * 3.

18.6 parsing

To implement the previous algorithm we have to go through the string and break it down into operands and operators. This approach is an example of that Parsing. The results of the individual parts of the string are called tokens. You may remember these words from Chapter 1.

Python offers a split method in both the string module and the re module (regular expressions). The string.split function splits a string into a list using a single character as a delimiter. For example:

>>> import string
>>> string.split ("Now is the time", "")
['Now', 'Is', 'The', 'Time']

In this case, the space is required as a separator. The string is consequently split at every space.

The re.split function offers more in that we can use a regular expression instead of a separator. A regular expression allows a set of strings to be specified. For example, [A-z] is the set of all letters (without umlauts and ß) and [0-9] is the set of all digits. The ^ operator negates a set so that [^ 0-9] represents the set of all characters that are not digits. This is exactly the amount we need to break up expressions in Postfix notation:

>>> import right
>>> re.split ("([^ 0-9])", "123 + 456 * /")
['123', '+', '456', '*', '', '/', '']

Note that the order of the arguments differs from that in string.split; the separator comes before the string.

The resulting list contains the operands 123 and 456 and the operators * and /. It also contains two empty strings inserted after the operands.

18.7 Evaluation of Postfix notations

To evaluate expressions in Postfix notation, we will use the parser from the previous section and the algorithm from the previous section. To keep things simple, let's start with an evaluation program that only implements the + and * operators:

defevalPostfix (expr):
import right
tokenList = re.split ("([^ 0-9])", expr)
stack = stack ()
for token in tokenList:
if token == '' or token == '':
continue
if token == '+':
sum = stack.pop () + stack.pop ()
stack.push (sum)
elif token == '*':
product = stack.pop () * stack.pop ()
stack.push (product)
else:
stack.push (int (token))
return stack.pop ()

The first condition takes care of spaces and empty strings. The next two conditions deal with the operators. We initially assume that everything else must be an operand. Of course, it would be better to check for incorrect input and issue an error message, but we'll get to that later.

We test the whole thing by evaluating the postfix expression (56 + 47) * 2:

>>> print evalPostfix ("56 47 + 2 *")
206

That's close enough.

18.8 Users and Providers

One of the fundamental goals of ADT is to strictly separate the interests of the provider who writes the code that implements the ADT and the user who applies the ADT. The provider only cares that the implementation is correct in accordance with the specification of the ADT, not how it will be used.

Vice versa goes the user assumethat the implementation is correct and doesn't care about the details. If you use one of the built-in types of Python you have the luxury of thinking solely as a user.

If you are implementing an ADT, you must of course also write the user code to test it. In this case, you play both roles, which can be confusing. You should always try to know what role you are currently playing.

18.9 Glossary

abstract data type (ADT)
A data type (usually a collection of objects) that is defined by a set of operations but can be implemented in different ways.
abstract data type (ADT)
A data type (usually a collection of objects) that is defined by a set of operations but that can be implemented in a variety of ways.
interface
A set of operations that define an ADT.
interface
The set of operations that define an ADT.
implementation
Code that meets the syntactic and semantic requirements of an interface.
implementation
Code that satisfies the syntactic and semantic requirements of an interface.
client
A program (or its author) that uses an ADT.
client
A program (or the person who wrote it) that uses an ADT.
provider
The code (or its author) that implements an ADT.
provider
The code (or the person who wrote it) that implements an ADT.
veneer
A class definition that defines an ADT with method definitions that call other methods and sometimes perform simple transformations. A veneer does not do any significant work itself, but it does improve or standardize the interface that is seen by the user.
veneer
A class definition that implements an ADT with method definitions that are invocations of other methods, sometimes with simple transformations. The veneer does no significant work, but it improves or standardizes the interface seen by the client.
generic data structure
A type of data structure that can contain data of any type.
generic data structure
A kind of data structure that can contain data of any type.
Infix notation
A way of writing mathematical expressions with the operators between the operands.
infix
A way of writing mathematical expressions with the operators between the operands.
Postfix notation
A way of writing mathematical expressions in which the operators come after the operands.
postfix
A way of writing mathematical expressions with the operators after the operands.
parse
Reading a string of characters or tokens and analyzing its grammatical structure.
parse
To read a string of characters or tokens and analyze its grammatical structure.
Token
A set of characters that are treated as a unit for the purpose of parsing, like words in a natural language.
token
A set of characters that are treated as a unit for purposes of parsing, such as the words in a natural language.
delimiter
A character used to separate tokens. It is comparable to the punctuation marks in natural languages.
delimiter
A character that is used to separate tokens, such as punctuation in a natural language.