DEVASC Python – PEP8, PyPI, Functions, Procedural vs OOP, Classes, Methods, Inheritance, Modules, and tons of exam day info!

Oh yes, we are going to start small and expand as this article goes on, so get comfortable if you are reading this for the long haul – This is creating Classes in Python towards the bottom!

There is a ton more Python 3 to get through together for DEVASC Exam Day!

I felt like Jim Carrey from “Liar Liar” telling the traffic cop how many laws he just violated writing the title to this thread, there will be a LOT covered in this specifically aimed for DEVASC, but this is also the ground floor foundations of Python 3 so I will cover in depth.

If you are not familiar with that hilarious scene, you should treat yourself for 30 seconds:

I feel like this thread is that rant and the rest of the DEVASC coverage is my unpaid parking tickets in the glove box, so its time to get to speeeeeeeeding through this material!

First things first – “pip install (package)” is installing modules from PyPI!

I didn’t clearly state this when going through creating and using “Python Virtual Environments” via “python3 -m venv (name)” which creates its own separate environment if you need certain versions of program packages that is installed via “pip install (package)” or as shown creating a requirements.txt file to install from that.

“pip install …” is considered installing modules from PyPI – Know the for exam day!

I’d practice the venv / pip install in a Linux VM or on an AWS EC2 VM or whatever you can use to work with it, but I would know that cold as that is easy points on exam day!

Reviewing PEP8 / PEP-8 – The “Python Enhancement Proposal” for writing clean code!

I didn’t realize it but this is where it is listed that 4 white spaces should be used rather than tabs for indentation when writing code, and the PEP8 itself is really a guideline of a sort of variation to the “Zen of Python” to make code clean / readable / reusable as possible.

This is way too much information for me to re-type here, as the official doc is found here:

That is a verbose “official” website for PEP8, which in there it has links to a lot more Pet Peeves / Proper ways to make code as readable and simple as possible, I don’t think you will need to know all of that for DEVASC but there are some other websites / blogs that have a more condensed version of PEP8 important concepts that I won’t rattle off here.

The one big take away for me from this for DEVASC is using 4 white spaces when indenting after every block of code, keeping things inline, and using comments to explain your code where necessary if you are adding a function to existing code – That is exam day PEP8 stuff.

That being said, lets get this Python Party started with “Functions” !

Functions are named blocks of code, which are meant to be reusable throughout the script playing off the coding mantra of DRY (Don’t Repeat Yourself) and KISS (Keep It Simple Stupid), so you’re not typing the same code blocks in a script that perform the same function.

Python has two different types of Functions – Built-In “print()” and ones that are built via “def …” command to “define” a function name, and giving it an intuitive name which is actually part of the PEP8 mantra so those principles are definitely worth a read.

When defining a function / giving it a name there are a few key rules for exam day:

  • Must not start with a number!
  • Must not be a reserved word in pythons built in functions (print, type, etc)
  • Name can be Upper/Loser Case Alpha-Numeric, with both underscores and hyphens

Below is a quick demonstration of a basic defined function used to just print something:

This is a defining a function at its most basic with a simple print statement, the one big take away here is not only just knowing how the this is written at its most basic, but the triple single quotations that are referred to as a “docstring” within a function.

The “docstring” is a place to comment what the function does, not just so it can be read in the script by simply using a #comment in the script, but also by using the built-in help function:

As seen this “docstring” can be used when perhaps the script is not readily available, and you are just looking for a quick general description of the function, you can use the help() built in function that contains your created function and it will output the docstring text.

Using Arguments and Parameters to make Functions work in Python!

Arguments are the variable(s) that are passed into a function that it runs against, for example with “print(‘Hello World!’)” – ‘Hello World!’ is the argument being passed into the print function.

Function can also be run by themselves as seen above with my created function, or like in a Bash Shell in Linux, you would type “exit()” to invoke the exit function to return from a Python prompt back to the Bash shell.

Here it can be seen using the built-in function “max()” that doesn’t ‘print’ but rather ‘returns’ the output of the result of the arguments run against the function shown here:

I did check out the help output for a built-in function, it is extensive (and good), however this was to demonstrate that I passed 3 arguments into the “max” function which then based in “Parameters” of that function it returns the output of the highest integer in the argument.

“Parameters” are the instructions within a function that tells it how to process the arguments passed into it, so it can return the expected result, this is done by way of using the “return” keyword in your function telling it to return output from the arguments.

Below is a demo of a function that appears to need an IDE to work return output:

This is a good reason to have both VSC to learn coding, but an actual IDE to test in:

Whoop there it is! Ahem, anyways.

The “result = var1 + var2” and “return result” are the “Parameters” within this “Function” that are run against the “Arguments” within the (), its actually almost like assigning a Value to a Variable in how you add Parameters to a Function, that is a good way to think of it.

Now what is seen ^^^ up there is a “local result”or “local variable” that is not accessible for the Python script as a whole to call later on down the script (or it would throw an error ‘result is not defined’ or something similar, which uses “positional arguments” where I’ve statically defined (var1, var2) and expects / will only accept 2 variables for the function.

What if you need more flexibility in your script? Or Variables that can be called throughout the script to run against other variables, such as Interface Status / IP Addresses / VLANs?

That is what we have “global variables” come into play using “keyword arguments” rather than “positional arguments” to essentially assign a variable to an argument in the function:

Given addition you can put it in forwards, backwards, sideways and it will come out the same so probably not the best demo, but it illustrates the point of keyword arguments 🙂

But what if we don’t know what variables to expect in our functions? Great question!

For this have two types of Python wildcard characters * and ** named *args and **kwargs, which tells the function to expect the unexpected in terms of Arguments or… Kwarguments?

After seeing the error I realized I had not defined ‘Loopy’ anywhere as a string or any type of Variable, so Python had no idea what to do with it, but after assigning the String Value ‘Loopy’ to the Variable Loopy, it then passes through the *arg function and prints!

The big takeaway here is that this is a “for” loop, which allows this to iterate through any amount of arguments I throw into it, and it will use the set Parameters to return as many results as needed for the amount of Arguments passed into the Function:

I just noticed that the extra space I added was not needed so it prints a little gross, but it gets the point of what is being accomplished here across, but what is we passed non-string to it?

It works so long as it recognizes the data type, whereas if I had not put ‘ ‘ around ‘Tom’ it would not know what / who Tom is, and would throw an error that it doesn’t know what on Earth I am trying to pass into it – However it understand all these arguments and returns a result for each of them no matter the data type (so far)!

Kwargs work a bit differently, as they use basically a Dictionary style Key/Value pair:

I wanted to leave this error in here as well as this was an honest goof up that I specifically set a “Parameter” that is saying “for the key / value pair in kwarg items sent into this function, print something for the value from that key / value” – But I never defined a Key anywhere!

The “kwarg.items():” portion in the for loop is telling the function to iterate through each kwarg item or argument passed into it to find the value / print it / iterate again until done.

A quick demonstration on defining static items for saying Hello in the defined function:

Apparently Python only speaks the Spanish I program it to speak, but that is OK, I might have been a little scared if my IDE engaged in a discussion with me that I didn’t program 🙂

Procedural vs Object-Oriented Programming Techniques

Up to this point my DEVASC / Python studies, my coding has all be “Procedural” technique in that it is meant to execute the script top to bottom until it ends, whereas “Object-Oriented” technique breaks up code blocks / class models throughout the script to be referenced further on into the script (though both Procedural and OOP can both be used in a script).

Object-Oriented Programming technique is what allows for code re-usability of code, which is necessary for writing of “Modular” scripts, which is defining Functions / Class Models / Methods to recycle code blocks to be used with different Variables for flexibility of the code.

This understanding of Python has broken the kryptonite like confusion that I’ve had with Python since I’ve began studying it, I am sure there still has some hurdles to overcome, but understanding defining Functions to re-use and the upcoming Classes and Methods I will run through as really cleared it up in my mind – I hope this demystifies it for others as well!

Python “Classes” – Using Classes to Create Models / Templates for common objects!

Classes will be shown to be created as a kind of “Blueprint” for how certain objects that share common attributes should be treated, how they gather information through “Methods” in the next segment, but first I want to look at creating a simple Class first.

I will try this again in Visual Studio Code for clarity, but will run within IDE on my Ubuntu VM if needed, and may even be able to run against my GNS3 Switched Lab I have ready to go for Network Automation labbing that I put on hold to nail DEVASC topics!

Per the PEP8 rules of writing good legible code, when creating a class, its a common courtesy to capitalize the class name for better readability, also to demonstrate how Tabs vary from 4 white spaces between different coding environments:

I didn’t realize VSC was actually correcting me when I did previously use tabs (though I did actually stop once I read the PEP8), I figured I would test tabs out in an actual raw IDE, and sure enough 2 tabs equaled close to 12 spaces of indentation.

However – What you see above in VSC is what I wrote below in my Ubuntu VM and tested:

The first value of “self” refers to this class “Switch” itself, then goes on to describe 3 attributes of a switch, which I then statically assigned based off the actual GNS3 Switched lab I have at the ready for use in the near future, and can see I can now use Methods to manipulate this data similar to the methods seen in the last page with other data sets.

Note that I do use “docstrings” at each new level so the “help(Switch)” has details in it:

NOTE – This may drop you into a VIM, which you simply hit tab + :q + enter to exit gracefully to not lose your work by having to close out of the editor / panicking 🙂

In the line where I “def __init__(values)” I am defining a function that calls __init__ which is a special function type called “dunders” or “magic methods” in Python, that sets up a class with the Values used for “Initialization” of a new object (as shown below).

Then finally it is shown that I use “SW1 = Switch(values)” to set the values of SW1, then suddenly we can use those extensions to get the correlating value for that object:

You can also add attributes to the Object as needed without effecting the Switch class:

Though this shows how flexible defining an object can be, its always best practice to define as many attributes as possible that will be needed within the script in the Class itself, as this goes back to the PEP8 / Python of Zen to keep your code concise and clean!

You can also just define a class with the keyword “pass” to define it with no attributes:

I am actually not sure the case use for this yet, but wouldn’t to throw that out there, that this a valid way of creating a Class with no attributes, but not sure how it applies to Python (yet).

Python “Methods” – A deeper look!

In the previous post I worked a bit with different data sets to use methods to perform interact with some objects such as Lists / Sets / Etc, and that is what we use them for when working with the classes that we setup as well – Class Objects are created for the manipulation of its data via Methods!

We can actually build them, and I’ll see if I can use that Router object I created just above:

(I did have to use Visual Studio Code on my Ubuntu to work out indentation!)

I was getting caught with the following f strings needing to 4 white spaces under ‘desc’ line, rather than lining it up with the top f string line, so don’t be afraid to use VSC to make the code then copy / paste it into your IDE to test as shown here:

Now I am not quite sure how best to extract this info, so let me take a look here:

On the second print statement I accidentally printed the R1 values, however this demonstrates on the very top print line what it does exactly (mostly):

  • Printers the Header / Device Name (Not defined in the Class here), \n to go to next line
  • Define the Object to run the method against, and use the method .getdesc as Parameters
  • Define ‘\n’ to be inserted per iteration / value in the Class that will be printed
  • Format it a bit better with the sep=” however that seemed not to work

I actually did a little extra playing around with commands / output, and the typing of that long print statement is not unnecessary, that was sort of like manually adding to a class:

The R1 = Router(stuff) was still needed to fill in this information, but just a ‘print(R1.getdesc())’ has the formatting kind of sloppily built into the class which I’m not worried about the formatting, as long as the point here is very clearly illustrated.

Inheritance in Python!

Inheritance is adding two classes together so that one class inherits the sub-class of the other if they share the same attributes, but one has extra attributes that the other can use, like how a Router and a Switch have almost identical descriptors / objects – So I made a change:

I could call it something1, something2, it really doesn’t matter so I just renamed this from iosver to swversion to match my Switch Class, so I could then combine Switch with Router Class so that Switch ‘inherits’ the descriptor / Method in the Router Class (DRY!).

I will exit and re-enter and demonstrate how inheritance works here visually:

“class Switch(Router):” is the line that tells Python “This class is inheriting the objects defined within this class upon initialization” so all those values are ‘inherited’ to the Switch Class, and then the output portion I just copy / pasted from VSC and forgot to swap out the word Router for Switch which is what I should meant to do but I’m really trying to mash through this section to cover some ground here – But the print statements both look good!

That is really all there is to inheritance at a DEVASC level, so I would know that concept for exam day, as I might be ready to answer some very basic Python questions like this.

Working with Modules in Python!

Modules are not just available to “import” into a script, but are actually made by defining functions and classes as shown above, as the goal of OOP is to make code Modular in the way that by writing certain pieces of code that can be re-used not just in this same script but to be imported into other scripts for use via “import” of your own blocks of code (hence modular)!

Some very important benefits to modular code to know for exam day:

  • Easy Readability / Maintainability – Code that is written in a Modular way is easier to read because it is written in blocks rather than lines and lines of code, making it almost like chapter in the DevNet OCG for example, once I know what a chapter contains I can refer back to it quickly and intuitively for information I need
  • Low Coupling / High Cohesion – Modular code will generally not be dependent on other blocks of code (or as few as possible), so that it is self contain to allow for easy re-usability in other scripts, this is called “Low Coupling / High Cohesion Modular Coding”
  • Code Re-usability – Modules make code easily re-usable in other scripts
  • Collaboration – Many people in a team can collaborate on a single coding project by breaking up the coding into different responsibilities to integrate back into one script

There are also 3 main ways that Modules can be used in Python to know for exam day:

  • Built-in and “pip” (PyPI) libraries contain pre-written module for about everything
  • Building your own Modules and saving the file as .py, this takes a bit longer at runtime to execute as Python may need to serialize / de-serialize the code
  • Writing the code in C language, compiling it, then add it to your Python code – This is a very fast method at run time of the script but has a ton of overhead / work to it

Importing Modules and Reviewing their functions / methods / etc!

If its hard to see this, I did an “import math” then “dir(math)” which gave me all of its functions that is contained within that library which has some I know right away like “exp” (exponent) / “sqrt” (square root) / “floor” (round down to integer value) / etc.

You don’t need to go to Pythons website to look up what is in the modules (or any modules), you should be able to find it with “help(modulename)” as shown here:

If there is one specific function you’d like to hone in on, you can do “help(math.sqrt)” :

So you can see a list of function names with dir(), a verbose list of functions and descriptions in a module with help(module), and finally help(module.function) to get an example of a function that is contained within a module!

When importing your own modules into Python, it will look to the system paths for modules that are shown using the following command below:

They go on and on to the right and the last path seen just below the first line, so you will want to make sure your modules reside in this file path to be found, to do this I just back out to the Bash Shell prompt and “nano” and paste the script into a .py file:

Now to see if I can import the class into a new IDE Python session on the Ubuntu VM:

I wanted to demonstrate YOU CANNOT IMPORT CLASSES FROM MODULES WITH INHERITANCE, AS SHOWN ABOVE^^^ ! Lets see how them being separated works:

I could have chosen my file name better so that the Module is not named the same as the Class being imported, but as seen it did import successfully and that is modularity 🙂

Last but not least, Important Python Modules to know for Cisco Infrastructure!

This will likely be very important for exam day points being that it directly relates to Cisco, and I am about fried on Python (even though there is more coming), so I will bullet point style these different modules with short definitions to finish this off!

It will go module name, import syntax, and short definition

General Purpose Library Modules:

  • pprint – “from pprint import pprint” – Pretty Print module makes text much more readable output without needing a text editor
  • sys – “import sys” – Allows for direct access to Python editor to interact with and manipulate objects
  • os – “import os” – Allows to use files local to the hosts underlying Operating System
  • datetime – “import datetime” – Create / format / work with Calendar Dates and times to use in logging or timestamps
  • time – “import time” – Allows for use of time delays and clock capabilities in scripts

Modules for working with / Manipulating Data:

  • xmltodict – “pip install xmltodict” to install, “import xmltodict” to import module – This interprets XML KeyValue pairs into Python language the script understands (Parsing)
  • csv – “import csv” – This library is used to understand Comma Separated Value files such as Excel Spreadsheets or the various batch files you can export in CSV format
  • json – “import json” – Reading and converting JSON formatted file types (Parsing)
  • PyYAML – “pip install pyyaml” to install, “import yaml” to import module – This module is needed to convert YAML files into Python (Parsing)
  • pyyang – “pip install pyyang” – This is a utility and NOT a module that has function to import, its a Utility used to Create / Modify / Verify YANG Models

Tools for API Usage:

  • requests – “pip install requests” – Library with HTTP services to interact with REST APIs
  • ncclient – “pip install ncclient” to install, “from ncclient import manager” to import module, This is a Python Library is for client-side scripting / communication / application integration with NETCONF protocol
  • netmiko – “pip install netmiko” to install, “from netmiko import ConnectHandler” to import module, Connection-Handling library helps to initiate SSH connections to Network Devices, geared towards bridging the programmability gap between devices with and without API support that still rely on CLI input, it relies on Paramiko module and works with multiple vendor platforms (not just Cisco!)
  • pysnmp – “pip install pysnmp” to install, “import pysnmp” to import module, This is a Python implementation of an SNMP engine to allow interaction with older infrastructure that does not support APIs but does support SNMP for management

Automation Tools:

  • nornir – “pip install nornir” to install, “from nornir.core import InitNornir” to import module, This is an Extendable / Mulithread Framework to work with large number of network devices at scale

Testing Tools:

  • unittest – “import unittest” – This is used to test Python code functionality, generally used in Automated Code testing and part of “TDD” or “Test-Driven Development” Methodology
  • pyats – “pip install pyats” to install, many different ways to import modules (will cover in next post), Cisco’s “gift to the development community” which was originally named Genie, this is intended for and used by Cisco Developers on Cisco Platforms for testing and configuration of “Infrastructure as Code”

Wow that was quite the end blitz, my brain is nearly smoking!

Holy cow, that is a bulk of Python Fundamentals if you can believe that, I will end this here as next I will be looking at “Working with Python Data Formatting” which I will gloss over for important parts for exam day, however this page is now so long the text input is slooow.

With that, Happy Halloween, and Happy Trick or Treating! 🙂

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s