My mentee asked me recently what an API is, and my fellow traveller-in-NaBloPoMo Alex asked me to try to cover a crunchy technical thing.
Let us see if we can solve both of these.
This blog is put out as the limit of my current knowledge. If I’m wrong, or you think I’ve skimped on detail, I’d be really grateful if you could let me know so I can make it better. I won’t be covering versioning APIs, although it is important and I recommend reading up on it.
An API is a description of how a thing can be interacted with programmatically. That is, by a computer-y shaped thing, rather than a person-y shaped thing. If a person-y shaped thing is interacting with your thing you will need a User Interface, or UI.
This is true at every level of abstraction.
This is also made slightly annoying by the way Python disregards concepts like encapsulation and instead lays itself open for anyone to abuse its internals, like a dog that rolls onto its back and exposes its soft belly because it trusts you implicitly.
There are, however, some nice conventions that discourage those who are not aggressively invested in fiddling with the insides. But I’m getting ahead of myself. Let’s talk about the API for a class,
class Dog: def __init__(self, age, colour): self.colour = colour self._age = age self.__goodest_boi = True def age(): self._age += 1 def bad_boy(): print("Bork!") print("Impossible, is always goodest boi")
Alright. Here’s our
Dog class. There are three attributes:
__goodest_boi. There are also methods to change these attributes. Well, to try and change these attributes.
The API of this object is the methods and attributes other computer-y type things can use to try to manipulate the object. In this case, they can reach in and alter
colour by just setting it, eg
>>>rufus = Dog(age=1, colour='a sort of white with black smudges') >>>rufus.colour = 'a muddy brown with eyes like amber' # rufus has been playing in the river again! >>>rufus.colour 'a muddy brown with eyes like amber'
colour is part of the API of this object.
We can also set Rufus’ age directly, because a single underscore prefixed to an attribute is more of a guideline than a rule:
>>>rufus = Dog(age=1, colour='a sort of white with black smudges') >>>rufus._age = 14 # it's weird how time flies; you blink and the puppy is now # a hoary old fellow with a frosted chin # who still raises his head when you call >>>rufus._age 14
The single underscore suggests to other developers that they ought not to try messing around with this attribute, so I would say it’s not part of the API.
We’ve also got a method to make our
Dog older, called
age, which is called as normal:
>>>rufus = Dog(age=1, colour='a sort of white with black smudges') >>>rufus._age 1 >>>rufus.age() # happy birthday Rufus! 🎉 >>>rufus._age 2
age method is definitely in the API, because we can use it to manipulate the object.
We’ve also got a method
bad_boy, for when our
Dog has been naughty. Since it’s a public method it forms part of the API. Let’s see what happens when we call it:
>>>rufus = Dog(age=1, colour='a sort of white with black smudges') >>>rufus.bad_boy() 'Bork!' 'Impossible, is always goodest boi'
Oh no! We can’t punish our
Dog! But wait. We could directly manipulate the attributes above. Let’s try to change
>>>rufus = Dog(age=1, colour='a sort of white with black smudges') >>>rufus.__goodest_boi = False AttributeError: 'Dog' object has no attribute '__goodest_boi' # gasp! but we definitely put that attribute in when we made Dog! # it is essential to Dog, and should not be accessed by anyone # without it, what is Dog but Wolf?
It turns out we can’t easily mess around with a double underscored attribute. Instead, we get an error and our Python interpreter pretends it’s never heard of such a thing.
It can be hacked around, but I’m not going to show you how. You’ll only try to make my
Dog into a
Bad Dog and that’s not a good use of technology.
The point of this slightly meandering stroll through a fictional
Dog class is to show that the API of a Python class comprises its publicly accessible methods and attributes.
Alright, says my mentee, but that’s not what people mean when they say ‘API’ is it?
Bugger. No, it’s not. It’s a terminological inexactitude. When people say ‘API’ they mean ‘web-based API’. But it’s the same concept: it’s the collection of public methods, this time exposed over the internet, that computer-y things can use to interact with your thing (except this time ‘your thing’ is probably your software as a whole, rather than individual classes).
For example, suppose you had to register your
Dog with an organisation for some reason. You might want to do that programmatically or, rather, your vet would probably want any
Dogs that passed through the surgery to be automatically registered. So there might be a web-based API for registering a
Dog that the vet’s surgery’s appointment booking software would link to.
In Flask, which is a Python web framework, it might look like this:
#!flask/bin/python from flask import Flask from app.models import Dog # our Dog is inside a file called models.py from app import db # oh yeah, we've got a database app = Flask(__name__) @app.route('/api/register', methods=['POST']) def register(): if not request.json or not 'age' in request.json: abort(400) # it's okay if there's no colour, but we can't # register ageless dogs. # So if there's no age we abort the request dog = Dog( age = request.json.get('age'), colour = request.json.get('colour', '') # defaults to empty string if no colour ) db.session.add(dog) db.session.commit() return 201 if __name__ == '__main__': app.run(debug=True)
So as you can see, the only API the software is exposing is a method to register a new
Dog. There’s no access to the
Alright. That’s a big, boring block of text about APIs. What did I get wrong? What else could I talk about? You should let me know.
November is National Blog Posting Month, or NaBloPoMo. I’ll be endeavouring to write one blog post per day in the month of November 2019 – some short and sweet, others long and boring.