Learning Python – Part 4

Forewords

A while back I decid­ed to try and learn Python for the hell of it as it seems like an inter­est­ing lan­guage and has some of the most con­cise and user-friend­ly syn­tax. Hav­ing spent some time going through a num­ber of dif­fer­ent learn­ing sources and mate­ri­als (like the offi­cial site python.org which has a very help­ful tuto­r­i­al sec­tion) I have put togeth­er a set of notes I made as I was learn­ing and hope­ful­ly they can be use­ful to you as a quick list of how-to code snip­pets.

All the code snap­shots I’m show­ing here are tak­en from the IDLE Python shell.

Classes

Some notable dif­fer­ences from the class mech­a­nism in C#:

  • The class inher­i­tance mech­a­nism allows mul­ti­ple base class­es
  • Class­es are cre­at­ed at run­time and can be mod­i­fied fur­ther after cre­ation
  • All class mem­bers are pub­lic by default
  • All class mem­bers are vir­tu­al
  • Class­es them­selves are objects

Like many oth­er lan­guages, most built-in oper­a­tors with spe­cial syn­tax (arith­metic oper­a­tors, sub­script­ing etc.) can be rede­fined for class instances.

 

Sim­ple class def­i­n­i­tion:

clip_image001

 

To instan­ti­ate a new class:

clip_image002

 

To define a con­struc­tor which takes in some para­me­ters:

clip_image003

to instan­ti­ate this class:

clip_image004

 

When a class defines an __init__() method, class instan­ti­a­tion auto­mat­i­cal­ly invokes this method for the new­ly-cre­at­ed class instance.

 

To access its attrib­ut­es:

clip_image005

 

To access its doc­string:

clip_image006

 

There are only two kinds of valid attribute names, data attrib­ut­es and meth­ods.

 

Data attrib­ut­es don’t need to be declared, they sim­ply spring into exis­tence when they are first assigned to. For instance, for the Per­son class defined above, we can add a new data attribute to an exist­ing instance:

clip_image007

note that the new data attribute belongs to the instance ref­er­enced by x, and doesn’t exist on any oth­er instance of the Per­son class (think Javascript)

 

You can delete the data attribute after you’re done with it:

clip_image008

 

Data attrib­ut­es can be over­ride by users of an object, so to avoid acci­den­tal name con­flicts it’s best prac­tice to use some kind of con­ven­tion that min­i­mize the chance of con­flicts. E.g. cap­i­tal­iz­ing method names, pre­fix­ing data attribute names with a small unique string, or using verbs for meth­ods and nouns for data attrib­ut­es.

 

It’s impor­tant to note that noth­ing in Python makes it pos­si­ble to enforce data hid­ing, it’s all based on con­ven­tion.

 

NOTES: Dif­fer­ence between a func­tion and a method is that a method is a func­tion bound to a class.

 

To define a method on the class:

clip_image009

 

To call the method:

clip_image010

You may have noticed that in the method def­i­n­i­tion say_greeting takes a sin­gle para­me­ter self but it was called with none. This is a spe­cial rule which applies to meth­ods where the object is passed as the first argu­ment. The con­ven­tion is to call the first argu­ment of a method self.

 

It’s not nec­es­sary that the func­tion def­i­n­i­tion is tex­tu­al­ly enclosed in the class def­i­n­i­tion:

clip_image011

and you can still use it like before:

clip_image012

 

To call oth­er meth­ods inside the class:

clip_image013

 

To find the class of an instance:

clip_image014

 

To cre­at­ed a derived class:

clip_image015

because we haven’t defined any new attrib­ut­es, every­thing will be inher­it­ed from Per­son includ­ing the __init__ method:

clip_image016

 

If a request­ed attribute is not found in the class, the search pro­ceeds to look in the base class and if still not found it pro­ceeds to look in the base class of that class, and so on.

You can also extend a base method instead of replac­ing it, to call a base class method:

clip_image017

 

Use isin­stance() func­tion to check if an object is an instance of a class or some class derived from it:

clip_image018

 

Use issub­class() func­tion to check if a class derives from anoth­er:

clip_image019

 

Every class keeps these built-in attrib­ut­es:

  • __dict__ : Dic­tio­nary con­tain­ing the class’s name­space.
  • __doc__ : Class doc­u­men­ta­tion string, or None if unde­fined.
  • __name__: Class name.
  • __module__: Mod­ule name in which the class is defined. This attribute is “__main__” in inter­ac­tive mode.
  • __bases__ : A pos­si­bly emp­ty tuple con­tain­ing the base class­es, in the order of their occur­rence in the base class list.

clip_image020

 

Python’s garbage col­lec­tor runs dur­ing pro­gram exe­cu­tion and is trig­gered when an object’s ref­er­ence count reach­es zero.

You can imple­ment a destruc­tor, __del__() method, that is invoked when the instance is about to be destroyed.

 

Here’s a list of some of the meth­ods you can over­ride in your own class:

clip_image021

 

Python sup­ports mul­ti­ple inher­i­tance:

clip_image022

 

Whilst you can’t hide an object’s attrib­ut­es you can still make them not direct­ly vis­i­ble to out­siders by adding a dou­ble under­score pre­fix:

clip_image023

What’s hap­pened is that Python changed the name of these attrib­ut­es to include the class name:

clip_image024

So you can still access them like this:

clip_image025

This is called name man­gling it is most­ly designed to avoid acci­den­tal name con­flicts as opposed to pro­vide data hid­ing.

 

Most con­tain­er objects can be looped over using a for state­ment, under­neath, the for state­ment calls iter() on the con­tain­er object and gets back an object that defines the method next(). When there are no more ele­ments, next() rais­es a Sto­pIt­er­a­tion excep­tion which tells the for loop to ter­mi­nate.

 

Here’s how you might cre­ate a cus­tom iter­a­tor which loops through a con­tain­er object in reverse:

clip_image026

clip_image027

 

Modules

A mod­ule allows you to log­i­cal­ly orga­nize your Python code, a mod­ule is a file con­tain­ing Python def­i­n­i­tions and state­ments.

The file name is the mod­ule name with the .py exten­sion.

With­in a mod­ule, the mod­ule name is avail­able as the val­ue of the glob­al vari­able __name__.

You can import an entire mod­ule in your code, or just spe­cif­ic sub­set of the func­tions defined in the mod­ule. For exam­ple, to import the def­i­n­i­tions in a file called ‘fib.py’ in the cur­rent direc­to­ry:

clip_image001[4]

 

A mod­ule can con­tain exe­cutable state­ments as well as func­tions, these state­ments are intend­ed to ini­tial­ize the mod­ule and are exe­cut­ed only the first time the mod­ule is import­ed some­where.

 

Each mod­ule has its own pri­vate sym­bol table which is used as the glob­al sym­bol table by all the func­tions defined in the mod­ule. This way, you won’t have to wor­ry about acci­den­tal clash­es with glob­al vari­able names. How­ev­er, if required, you could still access a mod­ules glob­al vari­ables with modulename.itemname.

 

You can import a sub­set of the items from a mod­ule using a vari­ant of the import state­ment. To import spe­cif­ic func­tions or vari­ables:

clip_image002[4]

 

You can import all names from a mod­ule except those begin­ning with an under­score:

clip_image003[4]

 

You can use the python exe­cutable to run a python script:

clip_image004[4]

When you do this, the __name__ glob­al vari­able of the script is changed to __main__, and by adding the fol­low­ing lines to your script you can make the file usable as a script as well as an importable mod­ule:

clip_image005[4]

clip_image006[9]

 

When you ask to import a mod­ule called fib, Python first looks for a file named fib.py in the cur­rent direc­to­ry, if not found it then looks in the list of direc­to­ries spec­i­fied by the envi­ron­ment vari­able PYTHONPATH.

You mustn’t name your script the same as a stan­dard mod­ule, or Python will attempt to load the script as a mod­ule when that mod­ule is import­ed..

 

To improve the start-up time of short pro­grams that use a lot of stan­dard mod­ules, python gen­er­ates a com­piled ver­sion of the mod­ule. For instance, for the mod­ule fib, the fib.pyc con­tains a byte-com­piled ver­sion of the fib.py file. The mod­i­fi­ca­tion time of the ver­sion of fib.py used to cre­ate fib.pyc is record­ed in fib.pyc file and is used to deter­mine whether fib.pyc is up to date and there­fore if it could be used.

How­ev­er, it’s worth not­ing that a pro­gram 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 com­mand line the byte-code for the script is nev­er writ­ten to a .pyc file. You can improve the load time for these scripts by mov­ing most of its code to a mod­ule and hav­ing a small boot­strap script that imports that mod­ule.

It’s pos­si­ble to have a .pyc file with­out the cor­re­spond­ing .py file, this can be used to dis­trib­ute a library of Python code in a form that is mod­er­ate­ly hard to reverse engi­neer.

 

You can use the com­pileall mod­ule to cre­ate .pyc files for ALL .py files in a direc­to­ry:

clip_image007[4]

sys.py is one of the stan­dard mod­ules, and use sys.path you can see all the paths which Python will look when it’s try­ing to find a mod­ule to import:

clip_image008[4]

Of course, you can add to that path:

clip_image009[8]

But remem­ber, this change is only valid in the cur­rent ses­sion, when you restart the inter­preter this new path will be lost.

 

To find out what names are defined in a mod­ule, use the built-in dir() func­tion:

clip_image010[4]

 

With­out argu­ment, dir() lists all the names you have defined cur­rent­ly:

clip_image011[4]

Anoth­er handy thing you can do with the dir() func­tion is to use to list all the built-in func­tions:

clip_image012[4]

 

You can put a col­lec­tion of relat­ed mod­ules in a pack­age. Here’s a pos­si­ble struc­ture for a sound pack­age in terms of file hier­ar­chies:

clip_image013[4]

The __init__.py files are required to make Python treat the direc­to­ries as con­tain­ing pack­ages. It can be an emp­ty file, but it can also exe­cute ini­tial­iza­tion code for the pack­age.

 

To import an indi­vid­ual mod­ule of the pack­age:

clip_image014[4]

But it must be ref­er­enced with its full name:

clip_image015[8]

 

Alter­na­tive­ly you could also:

clip_image016[8]

The ben­e­fit of this approach is so that you don’t need the full name to ref­er­ence it:

clip_image017[4]

 

Or, if all you want is the echofil­ter func­tion, you could also:

clip_image018[4]

And to ref­er­ence it:

clip_image019[4]

 

When the users write from sound.effects import * the import state­ment uses the fol­low­ing con­ven­tion:

  • If a package’s __init__.py code defines a list named __all__, it is tak­en to be the list of mod­ules names that should be import­ed when from pack­age import * is encoun­tered:

clip_image020[4]

  • If __all__ is not defined, from sound.effects import * only ensures that the pack­age sound.effects is import­ed and run any ini­tial­iza­tion code in __init__.py.

 

The rec­om­mend­ed approach is to import spe­cif­ic mod­ules using from Pack­age import mod­ule.

 

You can also rel­a­tive paths from the cur­rent mod­ule, so from the sur­round mod­ule you might:

clip_image021[4]

Where . refers to the cur­rent pack­age, .. refers to the par­ent pack­age, ..fil­ters refers to the fil­ters pack­age at the same lev­el as the par­ent pack­age

Because the name of the main mod­ule is always “__main__”, there­fore mod­ules intend­ed for use as the main mod­ule of a Python appli­ca­tion should always use absolute imports.