Tags

Consider following program: –

for i in range(2,3):
    if all(i%k !=0 for k in range(2,i)):
        print i

The program outputs 2. But how? In line 1 of code, the only value i can take is 2. Now, the second line of code will be if all(2%k !=0 for k in range(2,2)):. In this line, range(2,2) is meaningless as it returns an empty list. You can check this –

>>> print range(2,2)
[]
>>>

Due to this the whole line 2 is effectively turned into all([]). Run following: –

>>> all([])
True

This happens because of the way all() is implemented. If you pass an empty list in it, it will return True. The implementation logic is roughly as follows –

def all(iterable):
    for item in iterable:
        if item:
            pass
        else:
            return False
    return True

Essentially, an empty list never really gets to the return False part, so it goes straight to return True. And that is why line 2 in above code evaluates to True and thereby producing the output 2.


Packages

Create a following directory tree in your c\Python27 directory –

dir1
.      __init__.py
.      /dir2
.                __init__.py
.                mod.py

That is, create a directory named ‘dir1’. Inside this directory, create another directory ‘dir2’. In both of these directories, put __init__.py files. The __init__.py file can be an empty file. mod.py is the module from which you will want to import some of its attributes. In mod.py, put following code –

a, _b = 1, 2
def test():
    print 'Test done'

In above code, we simply assigned values 1 & 2 to variables a & _b. Second variable looks peculiar but there is an explanation behind this which we’ll see later. We also defined a simple function test which we’ll want to use in another script.

Now open your interactive Python Shell and run following commands –

>>> import dir1.dir2.mod as f
>>> f.test()
Test done
>>> f._b
2
>>> f.a
1
>>>

What I am trying to explain here is how package import works in Python(2.7). We basically created a package dir1 , a subpackage dir2 and in this subpackage a module mod. From this module we imported its attributes (function and variables). If not for __init__.py file, our package is merely a directory tree. This __init__.py is necessary as without it, the import command will fail. Try deleting this __init__.py in either dir1 or in dir2 and then run again above commands. You’ll be greeted with Error Message. One more thing, we actually used import dir1.dir2.mod as f instead of import dir1.dir2.mod. This way, we have been able to save some typing efforts.

Now let’s turn our attention toward variable _b. Why did we put an underscore before it? To understand this, run following commands –

>>> from dir1.dir2.mod import *
>>> test()
Test done
>>> print _b
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    print _b
NameError: name '_b' is not defined
>>> print a
1
>>> _b = 5
>>> print _b
5

As you can see, by putting an underscore before a variable name, we have been able to avoid some potentially serious trouble. In our module mod, we have set the value of _b as 2. What if we also define a variable with the same name but with different value in our script? As we have used from dir1.dir2.mod import * above, this allowed us to access attributes of mod directly (that is without referencing to package path dir1.dir2.mod). Our program wouldn’t know which _b variable we are referring to. By putting an underscore before a variable name, we achieve some sort of protection. For this and other similar reasons, the import method using * is discouraged. As we saw earlier, when we used direct import method (import dir1.dir2.mod as f), we were able to access the value of _b from module. Even if we had defined another variable with same name in our script, there wouldn’t have been any conflict because we would have accessed them differently (print f._b vs print _b).


What would be the output? And why?

>>> x = 5
>>> y = x
>>> print x, y
5 5
>>> x = x + 1
>>> print x, y

Now, while running above program again, use function id() and is command like this: –

>>> x = 5
>>> y = x
>>> x is y
True
>>> id(x)
19715832
>>> id(y)
19715832
>>> x = x + 1
>>> x is y
False
>>> id(x)
19715820
>>> id(y)
19715832

Can you see now what is going on? Both ‘x’ and ‘y’ refers to the same object ‘5’ of ‘int’ type. This object is immutable which means it can’t be changed. So when we assign ‘x’ a new value, a new object is created which is now referenced by ‘x’ but ‘y’ still refers to original object. If object ‘x’ were list, both ‘x’ and ‘y’ would still refer to same object because list is mutable.

Advertisements