Check out my new course Learn you some Lambda best practice for great good! and learn the best practices for performance, cost, security, resilience, observability and scalability.
A while back I decided to try and learn Python for the hell of it as it seems like an interesting language and has some of the most concise and user-friendly syntax. Having spent some time going through a number of different learning sources and materials (like the official site python.org which has a very helpful tutorial section) I have put together a set of notes I made as I was learning and hopefully they can be useful to you as a quick list of how-to code snippets.
All the code snapshots I’m showing here are taken from the IDLE Python shell.
Some notable differences from the class mechanism in C#:
- The class inheritance mechanism allows multiple base classes
- Classes are created at runtime and can be modified further after creation
- All class members are public by default
- All class members are virtual
- Classes themselves are objects
Like many other languages, most built-in operators with special syntax (arithmetic operators, subscripting etc.) can be redefined for class instances.
Simple class definition:
To instantiate a new class:
To define a constructor which takes in some parameters:
to instantiate this class:
When a class defines an __init__() method, class instantiation automatically invokes this method for the newly-created class instance.
To access its attributes:
To access its docstring:
There are only two kinds of valid attribute names, data attributes and methods.
Data attributes don’t need to be declared, they simply spring into existence when they are first assigned to. For instance, for the Person class defined above, we can add a new data attribute to an existing instance:
You can delete the data attribute after you’re done with it:
Data attributes can be override by users of an object, so to avoid accidental name conflicts it’s best practice to use some kind of convention that minimize the chance of conflicts. E.g. capitalizing method names, prefixing data attribute names with a small unique string, or using verbs for methods and nouns for data attributes.
It’s important to note that nothing in Python makes it possible to enforce data hiding, it’s all based on convention.
NOTES: Difference between a function and a method is that a method is a function bound to a class.
To define a method on the class:
To call the method:
You may have noticed that in the method definition say_greeting takes a single parameter self but it was called with none. This is a special rule which applies to methods where the object is passed as the first argument. The convention is to call the first argument of a method self.
It’s not necessary that the function definition is textually enclosed in the class definition:
and you can still use it like before:
To call other methods inside the class:
To find the class of an instance:
To created a derived class:
because we haven’t defined any new attributes, everything will be inherited from Person including the __init__ method:
If a requested attribute is not found in the class, the search proceeds to look in the base class and if still not found it proceeds to look in the base class of that class, and so on.
You can also extend a base method instead of replacing it, to call a base class method:
Use isinstance() function to check if an object is an instance of a class or some class derived from it:
Use issubclass() function to check if a class derives from another:
Every class keeps these built-in attributes:
- __dict__ : Dictionary containing the class’s namespace.
- __doc__ : Class documentation string, or None if undefined.
- __name__: Class name.
- __module__: Module name in which the class is defined. This attribute is "__main__" in interactive mode.
- __bases__ : A possibly empty tuple containing the base classes, in the order of their occurrence in the base class list.
Python’s garbage collector runs during program execution and is triggered when an object’s reference count reaches zero.
You can implement a destructor, __del__() method, that is invoked when the instance is about to be destroyed.
Here’s a list of some of the methods you can override in your own class:
Python supports multiple inheritance:
Whilst you can’t hide an object’s attributes you can still make them not directly visible to outsiders by adding a double underscore prefix:
What’s happened is that Python changed the name of these attributes to include the class name:
So you can still access them like this:
This is called name mangling it is mostly designed to avoid accidental name conflicts as opposed to provide data hiding.
Most container objects can be looped over using a for statement, underneath, the for statement calls iter() on the container object and gets back an object that defines the method next(). When there are no more elements, next() raises a StopIteration exception which tells the for loop to terminate.
Here’s how you might create a custom iterator which loops through a container object in reverse:
A module allows you to logically organize your Python code, a module is a file containing Python definitions and statements.
The file name is the module name with the .py extension.
Within a module, the module name is available as the value of the global variable __name__.
You can import an entire module in your code, or just specific subset of the functions defined in the module. For example, to import the definitions in a file called ‘fib.py’ in the current directory:
A module can contain executable statements as well as functions, these statements are intended to initialize the module and are executed only the first time the module is imported somewhere.
Each module has its own private symbol table which is used as the global symbol table by all the functions defined in the module. This way, you won’t have to worry about accidental clashes with global variable names. However, if required, you could still access a modules global variables with modulename.itemname.
You can import a subset of the items from a module using a variant of the import statement. To import specific functions or variables:
You can import all names from a module except those beginning with an underscore:
You can use the python executable to run a python script:
When you do this, the __name__ global variable of the script is changed to __main__, and by adding the following lines to your script you can make the file usable as a script as well as an importable module:
When you ask to import a module called fib, Python first looks for a file named fib.py in the current directory, if not found it then looks in the list of directories specified by the environment variable PYTHONPATH.
You mustn’t name your script the same as a standard module, or Python will attempt to load the script as a module when that module is imported..
To improve the start-up time of short programs that use a lot of standard modules, python generates a compiled version of the module. For instance, for the module fib, the fib.pyc contains a byte-compiled version of the fib.py file. The modification time of the version of fib.py used to create fib.pyc is recorded in fib.pyc file and is used to determine whether fib.pyc is up to date and therefore if it could be used.
However, it’s worth noting that a program doesn’t run faster when it’s read from a .pyc file instead of .py file, it’s only loaded faster.
When a script is run from the command line the byte-code for the script is never written to a .pyc file. You can improve the load time for these scripts by moving most of its code to a module and having a small bootstrap script that imports that module.
It’s possible to have a .pyc file without the corresponding .py file, this can be used to distribute a library of Python code in a form that is moderately hard to reverse engineer.
You can use the compileall module to create .pyc files for ALL .py files in a directory:
sys.py is one of the standard modules, and use sys.path you can see all the paths which Python will look when it’s trying to find a module to import:
Of course, you can add to that path:
But remember, this change is only valid in the current session, when you restart the interpreter this new path will be lost.
To find out what names are defined in a module, use the built-in dir() function:
Without argument, dir() lists all the names you have defined currently:
Another handy thing you can do with the dir() function is to use to list all the built-in functions:
You can put a collection of related modules in a package. Here’s a possible structure for a sound package in terms of file hierarchies:
The __init__.py files are required to make Python treat the directories as containing packages. It can be an empty file, but it can also execute initialization code for the package.
To import an individual module of the package:
But it must be referenced with its full name:
Alternatively you could also:
The benefit of this approach is so that you don’t need the full name to reference it:
Or, if all you want is the echofilter function, you could also:
And to reference it:
When the users write from sound.effects import * the import statement uses the following convention:
- If a package’s __init__.py code defines a list named __all__, it is taken to be the list of modules names that should be imported when from package import * is encountered:
- If __all__ is not defined, from sound.effects import * only ensures that the package sound.effects is imported and run any initialization code in __init__.py.
The recommended approach is to import specific modules using from Package import module.
You can also relative paths from the current module, so from the surround module you might:
Where . refers to the current package, .. refers to the parent package, ..filters refers to the filters package at the same level as the parent package
Because the name of the main module is always "__main__", therefore modules intended for use as the main module of a Python application should always use absolute imports.
I specialise in rapidly transitioning teams to serverless and building production-ready services on AWS.
Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.
Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. Enrol now and enjoy a special preorder price of £9.99 (~$13).
Are you working with Serverless and looking for expert training to level-up your skills? Or are you looking for a solid foundation to start from? Look no further, register for my Production-Ready Serverless workshop to learn how to build production-grade Serverless applications!
Here is a complete list of all my posts on serverless and AWS Lambda. In the meantime, here are a few of my most popular blog posts.
- Lambda optimization tip – enable HTTP keep-alive
- You are thinking about serverless costs all wrong
- Many faced threats to Serverless security
- We can do better than percentile latencies
- I’m afraid you’re thinking about AWS Lambda cold starts all wrong
- Yubl’s road to Serverless
- AWS Lambda – should you have few monolithic functions or many single-purposed functions?
- AWS Lambda – compare coldstart time with different languages, memory and code sizes
- Guys, we’re doing pagination wrong