Becoming a Python Grown-Up

This entry is part 6 of 8 in the series Python

These Python lessons have been moving fairly quickly, and that’s primarily because of all the other great lessons on the rest of site.  Once you learn to develop code in a language, it’s relatively simple to move on to learn a new language.  I’m assuming that new language for you, is Python.

Since we have touched on variables, basic data structures, and for loops, I want to finish up some of the core basics by going over while loops and conditional statements.

The code we’re going to work with is below:

wallet_money = 150 
# We're going to invest in penny stocks
# that cost a lot more than a penny
while wallet_money > 0:
    invest_amount = 0 
    # Checking if we should give up the rest
    if wallet_money <= 15: 
        # Invest all our money in pennies
        invest_amount = wallet_money
        wallet_money = 0
    else:
        # Invest 25% in pennies
        invest_amount = .25 * wallet_money
        wallet_money -= invest_amount
    print "Investing $%d" % (invest_amount)
    print "$%d left after losing on pennies" % (wallet_money)

print 'We squandered our savings on penny stocks!'

In order to follow this code, we have to acknowledge the elephant in the code block….we don’t have brackets!  Instead of using brackets to distinguish logic blocks, Python uses indentation, so formatting becomes the lifeblood of a successfully running python program.

Indentation needs to be consistent, ie. keep using tabs if you start with tabs, or keep using 2 spaces if you start with 2 spaces.

wallet_money = 150 
# We're going to invest in penny stocks
# that cost a lot more than a penny
while wallet_money > 0:
    invest_amount = 0
    ...stuff...

Here we start with $150 and a while statement.  While statements say “while <test is true>: do stuff.”  In our case, we say “while our wallet money hasn’t run out, invest, over, and over, and over….”  The ‘:’ colon after the test indicates that logic within this statement is to follow.

invest_amount = 0 
# Checking if we should give up the rest
if wallet_money <= 15: 
    # Invest all our money in pennies
    invest_amount = wallet_money
    wallet_money = 0
else:
    # Invest 25% in pennies
    invest_amount = .25 * wallet_money
    wallet_money -= invest_amount
print "Investing $%d" % (invest_amount)
print "$%d left after losing on pennies" % (wallet_money)

While we have money in our wallet, we first check to see how much we have left.

# Checking if we should give up the rest
if wallet_money <= 15: 
    # Invest all our money in pennies
    invest_amount = wallet_money
    wallet_money = 0

Because our money amount changes each time we invest, this if is called a conditional statement, meaning the logic is subject to change depending on the condition of our check.  If we have $15 or less, then we invest the rest of our money.

else:
    # Invest 25% in pennies
    invest_amount = .25 * wallet_money
    wallet_money -= invest_amount

Here, else is exactly what it sounds like, if we have $15 or less, then invest everything, else invest 25% of the remaining money.  This is called an if/else construct.  We can also have if/elif/elif/else, etc. where elif has another check each time for something different.

One thing to note here is the wallet_money -= invest_amount line.  This is the same as saying wallet_money = wallet_money – invest_amount, but in a more concise way.

Finally we print the investment amount and remaining amounts each time the loop runs, and we end up broke again by trusting in the stock market of pennies.  These print statements should look familiar.

I saved my file as penny_stocks.py which can be run using the command python penny_stocks.py.  You can see the output of this script below:

$ python penny_stocks.py 
Investing $37
$112 left after losing on pennies
Investing $28
$84 left after losing on pennies
Investing $21
$63 left after losing on pennies
Investing $15
$47 left after losing on pennies
Investing $11
$35 left after losing on pennies
Investing $8
$26 left after losing on pennies
Investing $6
$20 left after losing on pennies
Investing $5
$15 left after losing on pennies
Investing $3
$11 left after losing on pennies
Investing $11
$0 left after losing on pennies
We squandered our savings on penny stocks!

After this, you should be pretty close to becoming a Python professional, ready to write all sorts of applications!

Python Objects (or a Bike and a Wheel)

This entry is part 7 of 8 in the series Python

When I first tried to teach myself programming, I bought a Java book that started teaching object-oriented programming (OOP) by describing an object (a bicycle), which had properties (wheels, pedals, etc), and state (moving or stopped).  I said whaah?!  Why am I learning about a bike and whether it’s moving or not.

Needless to say, I was completely befuddled and had no idea what I was doing or how to relate this bike concept.  Funny enough, a handful of years later a co-worker of mine described programming in almost exactly the same way.  It made as much of no sense as it did the first time.

The point in this initial ramble of mine is to promise you I won’t try to explain programming and OOP by using your first grade tricycle.

I like to think of OOP as a useful way to combine similar sets of code/logic so you can use it over and over without rewriting it.  From How To Become A Hacker (not the breaking into things type, but hacker as in building killer software), Eric S. Raymond writes:

Hackers (and creative people in general) should never be bored or have to drudge at stupid repetitive work, because when this happens it means they aren’t doing what only they can do — solve new problems. This wastefulness hurts everybody. Therefore boredom and drudgery are not just unpleasant but actually evil.

 

 

To behave like a hacker, you have to believe this enough to want to automate away the boring bits as much as possible, not just for yourself but for everybody else (especially other hackers).

OOP is useful so you don’t have to write the same thing, over and over again.  The example I like to use is writing a banking application.  First I’ll briefly explain a core part of DRY (Don’t Repeat Yourself) as functions.  If you have a for loop where you perform the same logic over a list, you can separate that logic into a function so you only write it once, but you can call it as many times as you need.

"""We're going to print abreviated titles
from a list of titles
"""
def short_title(title):
    new_title = ''
    if len(title) > 20: 
        new_title = title[:21]+'...'
    else:
        new_title = title

    return new_title

titles = [ 
    'Here is Title 1',
    'Title 2', 
    "A longer title that won't fit.",
    'Something a bit longer than we want'
]

for title in titles:
    print short_title(title)

To declare a function, we use the keyword def to declare (or define) a function.  title in def short_title(title): is a parameter that is passed into this function, which we use later to create a short title.  The gist of short_title is to return an abbreviated title if it’s longer than 20 characters.  Don’t worry too much about the details, just try to understand that the short_title function allows us to repeat creating an abbreviated title and use it anywhere else we need it.

The output, in case you were curious:

$ python function.py 
Here is Title 1
Title 2
A longer title that w...
Something a bit longe...

Next lesson we’ll go over the following code about the rich, poor, and greedy:

# bank.py
class Person(object):
    """This is the base class that represents
    a Person
    """
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

    def get_full_name(self):
        return self.first_name +' '+ self.last_name

class Employee(Person):
    """Since an employee is a Person, we indicate
    that we are a Person by declaring
    class Employee(Person):
    The item inside of () shows we inherit from
    Person
    """
    def __init__(self, fname, lname, age, salary, manager):
        Person.__init__(self, first_name=fname, last_name=lname, age=age)
        self.salary = salary
        self.manager = manager

    def get_manager_salary(self):
        return self.manager.salary

    def is_owner(self):
        return True if self.manager is not None else False

class Customer(Person):
    """A Customer is also a Person
    which means we also have a first and last name
    as well as an age
    """
    def __init__(self, first_name, last_name, age, bank_account):
        Person.__init__(self, first_name, last_name, age=age)
        self.bank_account = bank_account

    def get_account_balance(self):
        return self.bank_account.balance

    def deposit(self, amount):
        # We return the new account balance
        return self.bank_account.increase_cash(amount)

    def spend(self, amount):
        return self.bank_account.decrease_cash(amount)

class BankAccount(object):
    """A class that represents a bank
    account
    """
    def __init__(self, balance, manager):
        self.balance = balance
        self.manager = manager

    def decrease_cash(self, spent_money):
        self.balance -= spent_money
        return self.balance

    def increase_cash(self, money):
        self.balance += money
        return self.balance

bank_manager = Employee('Scrooge', 'McMuffins', salary=1000000, age=99, manager=None)
rich_bank_account = BankAccount(1000, bank_manager)
rich_customer = Customer('Rich', 'Person', age=205, bank_account=rich_bank_account)

print 'The bank manager for your bank is %s'%(bank_manager.get_full_name())
print 'Your account has $%d in it'%(rich_bank_account.balance)

interest_payment = 2500
print '%s\'s account earns $%d of interest'%(rich_customer.get_full_name(), interest_payment)
new_balance = rich_bank_account.increase_cash(interest_payment)
print '%s\'s new balance is $%d'%(rich_customer.get_full_name(), new_balance)
print '%s\'s age is %d years old'%(rich_customer.get_full_name(), rich_customer.age)

print '%s spends a little money'%(rich_customer.get_full_name())
rich_customer.spend(100)
print '%s now has $%d'%(rich_customer.get_full_name(), rich_customer.get_account_balance())

# He has the same mean bank manager as the rich person!
poor_account = BankAccount(10, manager=bank_manager)
poor_customer = Customer('Poor', 'Person', 105, poor_account)

print '%s starts with $%d'%(poor_customer.get_full_name(), poor_account.balance)
print '%s steals money from %s'%(bank_manager.get_full_name(), poor_customer.get_full_name())
poor_account.decrease_cash(5)
print '%s now only has $%d'%(poor_customer.get_full_name(), poor_customer.get_account_balance())

The output for this code is below:

$ python bank.py 
The bank manager for your bank is Scrooge McMuffins
Your account has $1000 in it
Rich Person's account earns $2500 of interest
Rich Person's new balance is $3500
Rich Person's age is 205 years old
Rich Person spends a little money
Rich Person now has $3400
Poor Person starts with $10
Scrooge McMuffins steals money from Poor Person
Poor Person now only has $5

Can you piece it together before we talk about it?  Comment below with any thoughts….

@codecrawl
@zachhilbert

Python Classes and OOP

This entry is part 8 of 8 in the series Python

So we’re back to the example I left in the last lesson:

# bank.py
class Person(object):
    """This is the base class that represents
    a Person
    """
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

    def get_full_name(self):
        return self.first_name +' '+ self.last_name

class Employee(Person):
    """Since an employee is a Person, we indicate
    that we are a Person by declaring
    class Employee(Person):
    The item inside of () shows we inherit from
    Person
    """
    def __init__(self, fname, lname, age, salary, manager):
        Person.__init__(self, first_name=fname, last_name=lname, age=age)
        self.salary = salary
        self.manager = manager

    def get_manager_salary(self):
        return self.manager.salary

    def is_owner(self):
        return True if self.manager is not None else False

class Customer(Person):
    """A Customer is also a Person
    which means we also have a first and last name
    as well as an age
    """
    def __init__(self, first_name, last_name, age, bank_account):
        Person.__init__(self, first_name, last_name, age=age)
        self.bank_account = bank_account

    def get_account_balance(self):
        return self.bank_account.balance

    def deposit(self, amount):
        # We return the new account balance
        return self.bank_account.increase_cash(amount)

    def spend(self, amount):
        return self.bank_account.decrease_cash(amount)

class BankAccount(object):
    """A class that represents a bank
    account
    """
    def __init__(self, balance, manager):
        self.balance = balance
        self.manager = manager

    def decrease_cash(self, spent_money):
        self.balance -= spent_money
        return self.balance

    def increase_cash(self, money):
        self.balance += money
        return self.balance

if __name__ == '__main__':
    bank_manager = Employee('Scrooge', 'McMuffins', salary=1000000, age=99, manager=None)
    rich_bank_account = BankAccount(1000, bank_manager)
    rich_customer = Customer('Rich', 'Person', age=205, bank_account=rich_bank_account)

    print 'The bank manager for your bank is %s'%(bank_manager.get_full_name())
    print 'Your account has $%d in it'%(rich_bank_account.balance)

    interest_payment = 2500
    print '%s\'s account earns $%d of interest'%(rich_customer.get_full_name(), interest_payment)
    new_balance = rich_bank_account.increase_cash(interest_payment)
    print '%s\'s new balance is $%d'%(rich_customer.get_full_name(), new_balance)
    print '%s\'s age is %d years old'%(rich_customer.get_full_name(), rich_customer.age)

    print '%s spends a little money'%(rich_customer.get_full_name())
    rich_customer.spend(100)
    print '%s now has $%d'%(rich_customer.get_full_name(), rich_customer.get_account_balance())

    # He has the same mean bank manager as the rich person!
    poor_account = BankAccount(10, manager=bank_manager)
    poor_customer = Customer('Poor', 'Person', 105, poor_account)

    print '%s starts with $%d'%(poor_customer.get_full_name(), poor_account.balance)
    print '%s steals money from %s'%(bank_manager.get_full_name(), poor_customer.get_full_name())
    poor_account.decrease_cash(5)
    print '%s now only has $%d'%(poor_customer.get_full_name(), poor_customer.get_account_balance())

Here I’m going to explain each part in detail, enough detail that you’ll hopefully be able to follow along and understand what classes are used for and how to use them.

Let’s first get some basic terminology out of the way.  Class is simply the blueprint of an object, where a class is the code that describes how an object should behave, and an object is a single instance.

# person.py
from bank import Person

zach = Person('Zach', 'Hilbert', 109)
slim = Person('Slim', 'Jim', 25)

# zach is an instance of a Person
print 'My name is ', zach.get_full_name()
print 'But his name is', slim.get_full_name()

Here is an example using Person from bank.py.  The first line of code: from bank import Person, simply says, from the bank module (ie. bank.py), import the Person class.  Then we use Person to create zach.  zach is an instance of Person.  slim is also an instance of Person.  Each instance of person has the ability to utilize the methods of Person (functions of class).

class Person(object):
    """This is the base class that represents
    a Person
    """
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

    def get_full_name(self):
        return self.first_name +' '+ self.last_name

In the Person class, we have 2 methods, __init__ and get_full_name.  get_full_name provides us a simple way to concatenate a person’s first and last names without having to write this every time we want their name.  The __init__ method is called a constructor, because it is what is called when you construct an instance of the class Person.

When we call zach = Person(‘Zach’, ‘Hilbert’, 109), Person() is instantiating the Person class and assigning the object to zach, which is an instance of Person.  When you call Person(‘Zach’, ‘Hilbert’, 109), __init__ gets called with ‘Zach’ as first_name, ‘Hilbert’ as last_name, and 109 as age.  For each method in a class, self is also passed in as a reference to the current object, so anytime you call self.<method/property>, it calls the data from that instance.  You can see this with zach and slim.  In zachself.first_name contains ‘Zach’, but slim contains ‘Slim’ for self.first_name.

The constructor of a class allows us to set necessary properties up front that may be use later.  All other methods can be created for use as necessary.  Employee and Customer inherit from Person, because they are people too, but both Employee and Customer have different properties and implementations.  Employee has a salary and a manager, who happens to be another employee, and Customer has a bank account which is a class itself.

These classes allow us to represent real-world objects that we might need to manipulate and represent in an application.  You can create as many people, employees, customers, and bank accounts as you want/need (as long as you have enough memory).

Often times you will have a database that saves information for each of these, and when you have customer ‘zach’ log into your application, your code gets zach’s information and bank account information and instantiates their respective classes to work with the zach object.  You might have lots of these objects and they can interact with each other as you need.

The exact implementation isn’t important here.  What is important is to understand that classes and objects help developers create applications that mimic real-world scenarios, and this is how many applications are built and used.

Beneath all of the classes you can see the use of each class and how each instance contains is own, separate data, as well as how these classes might be used to create a basic program.

Now that we have some of this more foundational material out of the way, I’ll focus more time on showing more realistic examples and implementations.