Introduction to Python

Posted by Balaji Srinivasan

Introduction to Python

You’ll need to know how to program in Python to extend the bitcoin computing demos and build your own.

All the code on this site is Python 3 unless otherwise specified. We assume that you already know at least a little bit about how to program, so we'll quickly show you how to do all the things you expect from a high-level programming language in Python.

The Read-Evaluate-Print Loop (REPL)

Python provides a Read-Evaluate-Print Loop (REPL) that lets you test code snippets interactively. You can start it anywhere python is installed by simply typing:

python3

In the REPL, lines starting with >>> are input and lines without a prefix are results.

>>> var_string = "this is a string"
>>> var_number = 21

Python provides dynamic typing, so you can usually just assign the data you want to a variable.

>>> var_string + " cheese"
'this is a string cheese'
>>> 21 + 20
41

The plus sign allows you to append to a string or perform arithmetic addition. Other math operators include - (minus), * (times), / (divides), and ** (exponent).

>>> array_greek = ["alpha", "beta", "gamma", "delta"]
>>> array_greek[0]
'alpha'
>>> array_greek[-1]
'delta'

Arrays in Python are zero-indexed. Specific elements can be accessed using the bracket syntax shown above.

>>> foo = "bar"
>>> if foo == "baz":
...    print("Do this")
... else:
...    print("Do this instead")

If/Else statements in Python are prefaced by the keyword and have a colon (:) following the conditions. Then the conditional code to run must be indented by one or more spaces or tabs (four spaces per indentation level is traditional in Python). The Else statement should be at the same indentation level as the If statement.

>>> for number in range(1, 10):
...    print(number)

For loops have similar syntax to If/Else statements with the looping conditions followed by a colon and the repeated code indented.

>>> def my_function(arg1, arg2="default"):
...     print("{} was first; {} was second".format(arg1, arg2))
>>> my_function("foo", "bar")
foo was first; bar was second
>>> my_function("foo")
foo was first; default was second

To define a function, you use the def keyword followed by the function name followed by the names of the parameters you want to use. You can also assign default values to the parameters.

>>> try:
...   10 * 10 / 0
... except:
...   print("You can't divide by zero")
... else:
...   print("You've entered Bizzaro world")

Python lets you handle exceptions using the Try/Except keywords, which also allows an Else block to run when no exception happens.

>>> import random
>>> random.random()
0.8236943584524945

>>> from math import exp
>>> exp(-1*0.5)
0.6065306597126334

You can import whole libraries using the import keyword, or you can import one or more classes from a library using the from keyword combined with the import keyword. If you import a whole library, you will need to prefix the name of the library before each function you use; if you use from to import individual classes, they'll be available directly within the current context.

Flask and Python function decorators

Now that you’ve read through the Python review, you’ll want to understand a few more things to build Bitcoin computing apps that use Bitcoin to pay clients and servers. Specifically, you’ll want to know (a) what the Flask microframework is and (b) understand what a Python decorator is.

Flask is a website microframework. It allows you to quickly create a webpage using a minimum of Python code. Here's a quick self-contained example:

## Import Flask
import flask

## create an app
app = flask.Flask(__name__)

## Use a decorator to set the URL
@app.route('/')
def home():
    return "Welcome to BTC\n"  ## print a string when the page is loaded

## start the server in debug mode, which prints extra information
## Use debug=False because of this bug: 
## http://stackoverflow.com/a/33603641
## https://github.com/mitsuhiko/werkzeug/issues/798
if __name__ == '__main__':
    app.run(debug=False)

The code comments should be mostly self explanatory. The main tricky part is @app.route, which uses a Python decorator (we’ll get into that in the next section). Try typing that into a file called server.py and then doing this at the command line:

$ python3 server.py
 * Running on http://[::1]:5000/ (Press CTRL+C to quit)

If you then use curl to send a simple HTTP request to the localhost ([::1]) on port 5000, you will print the message specified in your Flask server:

$ curl -g -6 'http://[::1]:5000'
Welcome to BTC

This will also work from a browser running on the same computer (eg if you are running Flask on your Mac). By using Flask in this manner you can set up simple HTTP-accessible endpoints and APIs, which are ideal for Bitcoin computing. Further information on Flask is here.

Python Decorators

A decorator is a “higher-order function”, a function that accepts another function as an argument. You use decorators to factor out repeated work from functions, by adding prefix and postscript actions.

An example will make this clear. Suppose we have some functions where we want to validate whether a user is in a database before performing an action. Here’s an example:

# APPROACH 1
# MANUALLY ENCODE THE SAME CHECK INTO EVERY FUNCTION

USERS = ['ronak', 'david', 'justin']

def is_valid_user(user):
    return user in USERS

def update_address(user, address):
    if is_valid_user(user):
        return "Updated address for %s to %s" % (user, address)

def add_friend(user, user2):
    if is_valid_user(user):
        return "Added new friend for %s: %s" % (user, user2)

# ... this continues for all functions
#     that require checking of the user

Notice the code above that is doing the same check for whether a user is valid? Because this will be copy-pasted into function after function, it’s important. But because it’s being copy-pasted, it’s error prone. Every function would have to be updated if your mechanism for validating users changed.

Here is an alternate approach:

# APPROACH 2
# FACTOR OUT THE REPEATED CODE INTO A DECORATOR
# USE IT TO BUILD NEW FUNCTIONS
USERS = ['ronak', 'david', 'justin']

def is_valid_user(user):
    return user in USERS

def validate_user(fn):
    def newfn(user, *args):
        if is_valid_user(user):
            return fn(user, *args)
    return newfn

@validate_user
def update_address2(user, address):
    return "Updated address for %s to %s" % (user, address)

@validate_user
def add_friend2(user, user2):
    return "Added new friend for %s: %s" % (user, user2)

This looks more complicated. And it is. But take a moment to understand what’s going on.

  • We created a new function called validate_user

  • That function itself takes a function (fn) as an argument

  • It uses that function to build a new function (new_function) within its scope

  • This new function does the same check for user validity, but in an abstracted-out way that can be applied to ANY function (fn) that was passed as an argument to validate_user

  • Python then allows you to put the @validate_user decorator in front of a function to indicate that said function is the argument (fn) to the decorator

Another way to achieve roughly the same goal is as follows:

# APPROACH 3
# EXPLICIT DECORATORS
USERS = ['ronak', 'david', 'justin']

def is_valid_user(user):
    return user in USERS

def validate_user(fn):
    def newfn(user, *args):
        if is_valid_user(user):
            return fn(user, *args)
    return newfn

def update_address2(user, address):
    return "Updated address for %s to %s" % (user, address)
update_address2 = validate_user(update_address2)

def add_friend2(user, user2):
    return "Added new friend for %s: %s" % (user, user2)
add_friend2 = validate_user(add_friend2)

Rather than putting @validate_user in front of update_address2, we instead explicitly passed this function as an argument to the validate_user higher-order function, and used the return value to then replace update_address2.

The use of decorators (the @ symbol) is thus just syntactic sugar for the above, but it makes things more readable. It allows you to factor out repeated code. In particular, you can specify multiple decorators before a function definition and they'll all be used in sequence to produce the function template that gets used.

The payment.required decorator

Why are decorators important for Bitcoin computing? We have a payment.required decorator which allows you to require that a payment is sent by a client before a particular request is processed. In the example below, an extra decorator is added to the function that requires a payment of 1,000 satoshis (1/100,000th of a bitcoin) in order to load the endpoint.

## Import Flask
import flask
## import from the Bitcoin library
from two1.lib.wallet import Wallet
from two1.lib.bitserv.flask import Payment

## Get the user's wallet 
wallet = Wallet()
payment = Payment(app, wallet)


## create an app
app = flask.Flask(__name__)

## Use a decorator to set the URL
@app.route('/')
@payment.required(3000)
def home():
    return "Welcome to Oz"  ## print a string when the page is loaded

## start the server in debug mode, which prints extra information
if __name__ == '__main__':
    app.run(debug=True)

To make sure the payment goes to the person serving the page, all we had to do was import an extra couple libraries and load the recipient's wallet.

Further reading

Those are the bare basics of Python. For a longer (but still very short) guide, try this resource. The definitive reference is Python.org, and if you want to get really good you should go through Learn Python the Hard Way.