Python @ DjangoSpin

50+ Tips & Tricks for Python Developers

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon
Reading Time: 31 minutes

Page #6


Handling CSV files using csv module

CSV (Comma-separated values) is a common data interchange format. Python's csv library makes it easy to work with files containing comma-separate values. Its reader() method can be used to read CSVs, while its writer() method helps to write to them.

>>> import csv

# The writerow() method of the writer object returned by writer() writes a sequence to the specified file with the given delimiter.
>>> with open('csvFileOne.csv', 'w', newline = '') as csvFile:
	csvWriter = csv.writer(csvFile, delimiter = ',')
	csvWriter.writerow(['New Delhi', 'India', 'Asia'])
	csvWriter.writerow(['New Jersey', 'U.S.A.', 'North America'])

# Contents of csvFileOne.csv
New Delhi,India,Asia
New Jersey,U.S.A.,North America



# reader() returns an iterator of records in the CSV file.
>>> with open('csvFileOne.csv') as csvFile:
	csvReader = csv.reader(csvFile, delimiter = ',')
	for record in csvReader:
		print(', '.join(record))
		
New Delhi, India, Asia
New Jersey, U.S.A., North America


>>> with open('csvFileOne.csv') as csvFile:
	csvReader = csv.reader(csvFile, delimiter = ',')
	for city, country, continent in csvReader:
		print('{}, {}, {}'.format(city, country, continent))
		
New Delhi, India, Asia
New Jersey, U.S.A., North America

The "_" identifier in Interactive Sessions

The _ identifier stores the most recently printed expression. This identifier can be used to quickly access the last computed result while working interactively. However, this identifier is only available in interactive sessions, and not in modules.

>>> a = 5
>>> _
5


>>> sum(range(10))
45
>>> _
45
>>> _ + _
90

Generating random elements with the random module

The standard library called random helps the user to inject simulation into your Python programs. Its randint(x, y) function gives a random integer in the range marked by its arguments, inclusive of both end points. If you do not want the upper bound to be inclusive in the range, you can use the randrange(x, b) function, which even offers a step argument just like range().

>>> import random
>>> random.randint(0, 5)			# random number from 1-5
3
>>> random.randint(0, 5)
5
>>> random.randint(0, 5)
3


>>> random.randrange(0, 5)			# random number from 1-4
1
>>> random.randrange(0, 5)
4
>>> random.randrange(0, 5)
0


>>> random.randrange(0, 5, 2)		# random number from 1-4 & step = 2
2
>>> random.randrange(0, 5, 2)
0
>>> random.randrange(0, 5, 2)
2
>>> random.randrange(0, 5, 2)
4

To generate random elements other than integers, we have a method called choice(), which accepts an iterable and returns a random element from it.

>>> random.choice( ['a', 'b'] )
'a'
>>> random.choice( ['a', 'b'] )
'a'
>>> random.choice( ['a', 'b'] )
'b'
>>> random.choice( ['a', 'b'] )
'a'
>>> random.choice( ['a', 'b'] )
'b'

Getting current date

The date class of Python's builtin datetime module helps us to retrieve current date.

>>> import datetime

>>> thisDay = datetime.date.today()
>>> type(thisDay)
<class 'datetime.date'>

>>> thisDay
datetime.date(2016, 12, 24)
>>> print(thisDay)
2016-12-24
>>> thisDay.__repr__()
'datetime.date(2016, 12, 24)'
>>> thisDay.__str__()
'2016-12-24'


# In order to convert the date object generated by today() function into string, use the strftime() function. You will need to specify the format of your date with the help of directives. The following format uses three standard directives, namely %d, %m and %y for day, month and year without century, respectively.
>>> thisDay.strftime("%d/%m/%y")
'24/12/16'

# The following format uses
# %A: full name of the weekday e.g Sunday, Monday etc. Use %a for the abbreviated versions like Sun, Mon etc.
# %d: the date number e.g 01, 02, 22 etc.
# %B: full name of month e.g. January, February. Use %b for abbreviated versions like Jan, Feb etc.
# %Y: year with century e.g. 2015, 2016. Use %y as in the earlier format, for year without century.
>>> thisDay.strftime("%A %d %B %Y")
'Saturday 24 December 2016'

You can find the full list of directives on the official Python datetime documentation.


Maintaining order in dictionaries

The regular behaviour of Python dictionaries is such that, the order in which the key-value pairs are declared is not necessarily the order in which they are stored in memory. For example:

>>> normalDictionary = {2: 'two', 3: 'three', 1: 'one'}
>>> normalDictionary
{1: 'one', 2: 'two', 3: 'three'}

If you wish to have a dictionary which maintains this order, you can use the OrderedDict class of standard library collections.

 
>>> import collections
>>> orderedDictionary = collections.OrderedDict({2: 'two', 3: 'three', 1: 'one'})
>>> orderedDictionary
OrderedDict([(1, 'one'), (2, 'two'), (3, 'three')])
>>> dict(orderedDictionary)
{1: 'one', 2: 'two', 3: 'three'}

This class was introduced in version 2.7. If you want similar functionality in older versions, I suggest you look at this page.


Making a particular version of IDLE as default on clicking 'Edit with IDLE'

If you have two versions of Python installed on your computer, which is usually the case if you are developing in Pygame since the highest version of Python it supports as of now is 2.7, then you would have faced the problem of loading a particular version of Python in IDLE on right-clicking a python file and clicking 'Edit with IDLE'.

Say, that IDLE is loading up with Python 3.4(visible on the title bar of IDLE), and you would like to load it with 2.7 temporarily. Follow the below steps to accomplish this:

  • Click on Start > Run. Alternatively, hit Windows button + R.
  • Type in 'regedit' and hit enter.
  • In the left window pane, navigate as follows: Computer > HKEY_CLASSES_ROOT > Python File > shell > Edit with IDLE > command
  • In the right window pane, right-click (Default) > Modify. In the Value Data field, you'll find two strings, each  between a pair of double quotes. In each string, replace Python34 with the other version of Python you want IDLE to load up with, e.g. Python27

This should now load IDLE with the version you just specified. Make sure that the version you just typed in is installed for it to be loaded.

A word of caution: It's advised against tweaking the registry values with incomplete information. So be sure to follow the steps carefully.


Sending mail with Python

Python provides smtplib & email modules to facilitate sending emails.

# This script uses a gmail account to send the mail. If you
# have your account with another email provider, you will have
# to change the smtp_variable and smtp_port variable accordingly.
 
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
 
# fill in the variables
smtp_server = "smtp.gmail.com"
smtp_port = 587                                 # for smtp.gmail.com
from_address = "from_address_here"              # e.g. username@gmail.com
from_password = "from_address_password_here"    # required by script to login using your username
to_address = "to_address_here"                  # e.g. username2@gmail.com
subject = "Subject_here"               
mail_body = "Body content here"
 
msg = MIMEMultipart()
msg['Subject'] =  subject
msg['To'] = to_address
msg.attach(MIMEText(mail_body))
 
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.login(from_address, from_password)
server.sendmail(from_address, to_address, msg.as_string())
server.quit()

If you get a failure message on the interpreter and a mail from your email provider saying that it blocked an attempt to login from a third-party app, you might have to turn on access for less secure apps in the settings of your email account. Doing so makes your email account vulnerable to hackers, so turn this access off once you no longer need it.

To see how to send a mail with attachments, visit this link.


Working with binary files in Python

Not all files are text files. Others contain information in the form of bits and bytes, like images, formatted text files (Microsoft Word), audio-video files etc. These are called binary files and cannot be read in the same way as Python reads text files. Check this post out an elaborated discussion on working with binary files in Python.


Double Underscore Methods and Attributes

The builtin dir() method, when applied on a string, or a function, class, dict, tuple etc. reveals a host of double underscore methods(or functions), attributes (or variables), classes and other objects. These are called dunder objects, short for double underscore in their names. These are also known as magic methods and magic attributes. I have explained dunder methods and dunder attributes at length here, so check it out.


Generators: the yield keyword

A Generator is an object in Python which returns a sequence of elements, one at a time. A Generator function returns the Generator object. It is characterized by the keyword yield i.e. a function having the yield keyword in its body is a Generator Function. Basic usage example:

>>> def generateFamousDetectives():
	print("Famous Detective #1:", end = " ")
	yield "Sherlock Holmes"
	print("Famous Detective #2:", end = " ")
	yield "Hercule Poirot"
	print("Famous Detective #3:", end = " ")
	yield "Nancy Drew"

	
>>> generateFamousDetectives
<function generateFamousDetectives at 0x030303D8>

>>> generateFamousDetectives()
<generator object generateFamousDetectives at 0x030290F8>

>>> generatorObjectOne = generateFamousDetectives()
 
>>> generatorObjectOne.__next__()
Famous Detective #1: 'Sherlock Holmes'
>>> generatorObjectOne.__next__()
Famous Detective #2: 'Hercule Poirot'
>>> generatorObjectOne.__next__()
Famous Detective #3: 'Nancy Drew'
>>> generatorObjectOne.__next__()
Traceback (most recent call last):
    generatorObjectOne.__next__()
StopIteration

The generator function 'generateFamousDetectives' returns a generator object, which we can assign to a variable, such as 'generatorObjectOne'. Once we have this object, there are 3 methods in which we can fetch elements from it, just like iterators:

1. Using the __next__() magic function of the generator object, as done above. The __next__() is calling the builtin next() method and passing itself (i.e. the generator object) to it.
2. Using the builtin next() function explicitly, such as next(generatorObjectOne)
3. Using a for loop, such as for detective in generatorObjectOne: print(detective)

The word 'Generator' can be interpreted in two ways. It can be understood to mean the function that is generating the values one by one i.e. generateFamousDetectives(). And it can also be understood to mean the the generator object that the generator function is returning i.e. generatorObjectOne. The latter is the correct one. You can make the distinction by using the terms generator function and generator.

So, a generator is similar to iterators in the sense that both have __next__() method, both can be passed to the builtin next() function & both can be used in conjunction with a for loop. There is a major difference though. Generators evaluate the generator function till the point they encounter the next yield statement which returns an element, and as a result, they do not store the entire list of elements in memory. Iterators on the other hand, take an iterable as input, store the entire iterable in program memory, and return one element at a time.

For more examples & Generator Expressions, refer to this article.


See also: 50+ Know-How(s) Every Pythonista Must Know


Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon

Leave a Reply