Categories


Archives


Recent Posts


Categories


Python Imports for PHP Developers

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

The biggest problem for developers who start with PHP is unlearning their hyper-defensive programming stance. Post PHP Shock Syndrome. There’s a lot of CS-101 concepts that get short shrift in PHP, and while that doesn’t stop you from building applications and systems, it does mean you’re not always ready to comprehend the context a language like python exists in.

This article is the start of a semi-regular series on python development, aimed at PHP developers. Today we’re going to look at a core python concept, importing code. This article assumes you know how to run both PHP and python scripts from the command line and understand the basics of their syntax.

Hello PHP

Alright, let’s consider a simple PHP hello world script. Create a file named

hello_earth.php

with the following contents

<?php
#File: hello_earth.php
function hello()
{
    echo "Hello Earth! \n";
}
hello();

and run it from the command line

$ php hello_earth.php

You should see output something like

Hello Earth!

Simple enough, right? Next, create a file named

hello_mars.php

with the following contents

<?php
#File: hello_mars.php
function hello()
{
    echo "Hello Mars! Mind the germs.  \n";
}
hello();

and run it with the following

$ php hello_mars.php

You’ll see output something like this

Hello Mars! Mind the germs. 

All very basic stuff. Now, let’s say we wanted to write a program that used code from both these files. We’ll start by creating a file named

solar_system.php

and placing the following code inside of it to include our two files

<?php
    include 'hello_earth.php';
    include 'hello_mars.php';

    echo "Starting up a solar system \n";

However, when we run solar_system.php

$ php solar_system.php

We see the following error (or one like it)

PHP Fatal error:  Cannot redeclare hello() (previously declared in hello_earth.php:2) in hello_mars.php on line 5
PHP Stack trace:
PHP   1. {main}() solar_system.php:0

PHP is telling us it can’t continue because both files declare a function named hello. Because PHP’s include (as well as require) statement works by merging every file into one “global namespace”, that means the system can’t use both these files without the author manually modifying them.

//ex.
function mars_hello()
{
    echo "Hello Mars! Mind the germs.  \n";
}
hello();

This was a big problem for early PHP framework developers: How could they ensure a multiple file project contained no name collisions such as the ones above. Developers tortured PHP4’s rudimentary object system into the now common web-MVC code pattern. Instead of the main entry point being a single PHP file, developers were encouraged to create a single controller class file for each URL. This controller file would contain any PHP code the developer needed, and more importantly is would start the developer off in method scope. By making is slightly more difficult to create functions in the global namespace, the possibilities of name collision were reduced. Developing standard patterns for including helper-classes/library functions reduced name collisions further.

This sort of code management is a key feature of any PHP framework.

When you start looking at python frameworks, you’ll notice this sort of code management is a much less important feature. That’s because python has a another tool for managing its code and these so called “namespace collisions”. That tool is python itself.

The Python Way

Let’s consider the same example in python. Create two files with the following content (placing them in the same directory)

#File: hello_earth.py
def hello():
    print "Hello Earth!"

hello()        

and

#File: hello_mars.py
def hello():
    print "Hello Mars! Mind the germs."

hello()            

and give them a test run from the command line

$ python hello_earth.py
Hello Earth!

$ python hello_mars.py
Hello Mars! Mind the germs.    

Next, let’s create our solar system file

#File: solar_system.py
print "Here comes the sun."

and run it

$ python solar_system.py
Here comes the sun.

All very straight forward and similar to PHP so far. Next, we want to include the code from our two hello files. Modify solar_system.py such that it look like this

#File: solar_system.py
import hello_earth
import hello_mars
print "Here comes the sun."    

The word import is python’s version of PHP’s include and require, with a few key differences. The first difference involves naming semantics. When you import a python file, you’ll want to leave off the .py. This is why we have import hello_earth instead of import hello_earth.py. At this point, you might expect python to complain about two functions named hello. Let’s try running our file.

$ python solar_system.py
Hello Earth!
Hello Mars! Mind the germs. 
Here comes the sun.

Look at that! Somehow python managed to import both the files despite each file defining a function with the same name! When you use the word import in python, you’re telling it you want to use code for another file as a module. This is another important difference between import and PHP’s include/require. PHP’s include/require statements don’t do much in the way of infering any special context on the files they load.

The creator of python assumed people would want to create code with files from many different sources and many different people, and he made python smart enough to resolve these difference automatically without requiring developers to change the way they write their code in an individual file. Any python file can become a module in any other project without the original author having to structure their code in a specific way.

So if we have two hello functions defined, how do we call them? Give the following a try

#File: solar_system.py
import hello_earth
import hello_mars
print "Here comes the sun." 
hello_earth.hello()
hello_mars.hello()

You’ll get output something like this

$ python solar_system.py
Hello Earth!
Hello Mars! Mind the germs. 
Here comes the sun.
Hello Earth!
Hello Mars! Mind the germs.

When you import a python script as a module, behind the scenes python creates a module object (hello_earth), and all the functions from the script are available as functions on the module object (hello_earth.hello()). Javascript developers should feel right at home. This is how python manages to safely include the files that define the same function names without having a namespace collision. The language itself is designed to handle this.

Quiet Down

One potential problem with this approach is the output of our scripts turned modules. Because we called hello() at the end of each of our files, output is sent when we import them as a module. Try changing your files to match the following

#File: hello_earth.py
def hello():
    print "Hello Earth!"

if __name__ == "__main__":    
    hello()        

#File: hello_mars.py
def hello():
    print "Hello Mars! Mind the germs."

if __name__ == "__main__":        
    hello()            

We’ve added a conditional before each call to the hello function.

if __name__ == "__main__":    
    hello()        

If you run your script directly from the command line, you’ll see the same output

$ python hello_earth.py
Hello Earth!

However, if you re-run your solar_system.py file

$ python solar_system.py 
Here comes the sun.
Hello Earth!
Hello Mars! Mind the germs. 

you’ll see the extra lines of output before [Here comes the sun.] have been removed. The variable __name__ is a special python global variable. When you run python code as a command line script, its value is __main__. However, when you import your code as a module, its value will be the name of the module. Try adding

print __name__ 

to the end of your hello_earth.py file

#File: hello_earth.py
def hello():
    print "Hello Earth!"

if __name__ == "__main__":        
    hello()            

print __name__

When you run solar_system.py, you’ll see the following output

$ python solar_system.py 
hello_earth
Here comes the sun.
Hello Earth!
Hello Mars! Mind the germs.

Notice the first line of output, hello_earth. That’s the super global __name__ in module context. If you’re writing python scripts and want to be polite to future developers who may want to use your script in a module, wrap any top level function calls in a

if __name__ == "__main__":
    ...

Even if you choose not to be polite, people will still be able to use your scripts as modules, they’ll just need to work around any side effects (output) caused by your function calls.

Don’t forget to remove the print __name__ before continuing.

PHP vs. Python

When the anonymous shrieking hoards of internet programmers castigate PHP as a toy language, the lack of this automatic namespace resolution, or similar facility, is one of the key things they’re complaining about. It’s the difference between a simple tool that evolved into a programming language capable of developing software systems (PHP) and a programming language that was designed from the ground up to be used as, and in, software systems (python)

One of the biggest shifts you’ll need to make when moving between PHP and python is how little class based boilerplate you’ll be working with. Consider Django’s routing system. In PHP based MVC systems, most routing schemes rely on you naming a class in a specific way, and placing it in a specific place. In Django, you open a file and edit it. To your PHP trained brain this would seem like reckless global namespace suicide, but because python gives you the ability to safely include code from anywhere into your project, you don’t need to worry about it. The point isn’t that one is better than the other, it’s that best PHP practices are different from best python practices.

Interestingly, PHP 5.3 introduced a namespace feature that bears a strong resemblance to python’s built in module system. In practice, adoption has been slow as namespace code is incompatible with earlier versions of PHP. Framework authors struggle with the best way to use a namespace system in their class based abstractions which were built to work around the lack of a namespace system. The weird verbose syntax doesn’t help things either. It’s another important contrast of development methodologies: python as a designed system, PHP as a feature driven, lets see what sticks system.

More on Importing Modules

Once all of the above has sunk in, there’s a few other important module concepts to grasp. Some of this may take a few days, weeks, months, or even years to fully sink in, so don’t worry if you don’t understand everything all at once. Understanding will come as you write more and more python code.

The first is python’s module system equivalent of PHP’s include_path. The above python examples worked because python always searches the current directory (or the script you’re running) for module files. However, just like PHP, python also has a list of folders it searches for modules to import. Create and run the following python script

#File: find_path.py
import sys
print sys.path

This script will output a python list (sort of like a PHP array), something like this

$ python find_path.py
['/to/current/folder', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python26.zip', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-darwin', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac/lib-scriptpackages', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-old', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload', 
'/Library/Python/2.6/site-packages', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC', 
'/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/wx-2.8-mac-unicode']

The sys.path variable contains all the paths where python will search, in order, for a module. These paths vary from system to system, from OS to OS. It’s also possible for module and framework developers to alter this information. You can read more in the python documentation.

Build In Modules

If you were paying attention to the find_path.py file, you probably were wondering about this line

import sys

Here it appears we’re trying to import a file/module named sys.py, but we never created any such file. What gives?

This bring us to the next big difference between python and PHP. In PHP, all 6000+ PHP functions are available for you to call at any given time. This is super convient for the programmer, but it’s also very inefficient from a system memory/resources standpoint. Making those 6000+ functions available comes with a performance cost.

Python, (like most other programming systems), is different. By default, python offers a limited number of simple expressions and statements for the user to write their program with. When the python maintainers want to give users additional functionality, say, for accessing system information, or for working with zip files, they implement a python module to provide the functionality.

On my system (with the include paths mentioned above), there’s a file at

/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/zipfile.py

Inside that file is python code which implements functionality for the ziplib module. Python client programmers may load this modules by using

import zipfile

in their own programs. The key takeaway here is that much of what people think of as python is implemented in python. If you wanted to, (and were insane), you could go edit the python source files and change the behavior of the system. This is radically different than PHP, where much of what you think of as PHP is actually implemented in C++, which means to change the behavior of PHP you need to write C code and recompile the binary.

Of course, sometimes you want to use some C code in python. For these situations, the language offers you the ability to create modules using C code. The end-user-programmer never needs to know that behind the scenes there’s C magic happening. A python module is a python module.

Alternate Import Syntax

The last thing you’ll need to know about, at least for today, is the alternate module import syntax. Replace your solar_system.py file with the following

#File: solar_system.py
from hello_earth import hello
import hello_mars
print "Here comes the sun." 

and run it

$ python solar_system.py
Here comes the sun.

The new syntax we’re interested in is this

from hello_earth import hello

Instead of importing the entire hello_earth module, this syntax imports only specific functions. Let’s try using using the function we just imported from hello_earth

#File: solar_system.py
from hello_earth import hello
import hello_mars
hello_earth.hello()

When we run the file

$ python solar_system.py
Here comes the sun.

we’ll end up with an error something like this

Traceback (most recent call last):
  File "solar_system.py", line 4, in <module>
    print hello_earth.hello()
NameError: name 'hello_earth' is not defined

That’s because when you load a python module using from X import Y, you’re pulling the function directly into your file’s global namespace. You are asking python to look at one module and import a specific function. Try the following instead

#File: solar_system.py
from hello_earth import hello
import hello_mars
hello()

Here we’ve removed the [hello_earth.] prefix. Running this file gives us the result we want

$ python solar_system.py
Hello Earth!   

Generally, unless a third party module recommends you use this syntax, it’s best to stay away from it when you’re starting out. Because you’re loading files into the global namespace, you’re back in the position of having to carefully manage what is, and isn’t, imported. Consider the following

#File: solar_system.py
from hello_earth import hello
from hello_mars import hello
hello()

Here we’re importing two hello functions, but only the last one in wins. It’s easy to see this in a small program, but in larger programs and systems it’s easy to lose track in a long list of import statements and never be sure about which hello() you’re calling.

Wrap Up

When PHP is the first language you learn, you’ll often feel lost trying to make the jump to python, or any new programming system. Techniques that were tried and true will fail you, and the techniques your peers are using seem opaque and full of magic. While these feelings are natural, remember one thing: It’s all just code that runs with a certain set of rules. You’re still the same, smart, capable developer you thought you were a week ago, you’re just lacking certain key pieces of information everyone else takes as a given.

Once the initial barriers fall away, having PHP in your arsenal becomes a weird sort of blessing. Rather than relying on superstition and ritual (this is just how it’s done), you’ll quickly seen the actual benefits of language and system features you had to do without in PHP.

Originally published October 2, 2011

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 2nd October 2011