Learning Python – Part 4

Yan Cui

I help clients go faster for less using serverless technologies.

This article is brought to you by

Hookdeck: The Serverless Event Gateway

Hookdeck is a reliable and scalable serverless event gateway for sending, receiving, authenticating, transforming, filtering, and routing events between services in your event-driven architecture.

Learn more


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:


note that the new data attribute belongs to the instance referenced by x, and doesn’t exist on any other instance of the Person class (think Javascript)


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.

Whenever you’re ready, here are 4 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game. This is your one-stop shop for quickly levelling up your serverless skills.
  2. Do you want to know how to test serverless architectures with a fast dev & test loop? Check out my latest course, Testing Serverless Architectures and learn the smart way to test serverless.
  3. I help clients launch product ideas, improve their development processes and upskill their teams. If you’d like to work together, then let’s get in touch.
  4. Join my community on Discord, ask questions, and join the discussion on all things AWS and Serverless.

Leave a Comment

Your email address will not be published. Required fields are marked *