Python Workout - Book Summary
Contents
The following post contains a summary of the book titled Python Workout by Reuven M. Lerner

Chapter 1
-
randintselects numbers inclusive of both the min and max range provided as arguments1 2 3import random x = [random.randint(1,10) for i in range(100)] print(max(x))10 -
In
Python 2it wasn’t an error to compare objects of different types. It would compare by type and then within type. InPython 3this has been removed. -
f stringsare superuseful in string formatting -
walrus operator:= -
splatoperator helps one to pass multiple arguments in to a function -
Never knew that
0.1+0.2in Python gives'0.30000000000000004' -
It is better to use
f stringsto display floating numbers - for examplef'{x:.2f}' -
“decimal” module in
Python 3that does human type arithmetic -
Learned how to convert a hexadecimal number to base 10 number
-
Learn that hexadecimal is called that way because it has hex - 6 + decimal - 10 , i.e. 16 numbers 0 - 15
Chapter 2
- There is no character type in Python 3
- In Python 3, the strings contain unicode characters
- “Pig translate”: Never knew such a thing existed
printfunction takes anendargument in which we can pass any character- There are 1114111 unicode characters
- Learned about
itemgetterandattrgetterfrom operator module - Decorate-Sort-Undecorate method for sorting elements in an array
- Unicode lecture at Pycon - https://nedbatchelder.com/text/unipain.html
Sortingmethod has akeyargument and areverseargument
Chapter 3
- Slice of a list/str/tuple gives back a list/str/tuple
- When retrieving via slice, Python allows you to go beyond the bounds
- Data is strongly typed but variables don’t have types
- Learned the importance of slicing
- Python lists are implemented as arrays of pointers. Python allocates some buffer space so that one can add a few elements to it on a dynamic basis. In its background, Python keeps a tab on the buffer space and allocates new memory as and when required
- Worked on replicating
zipfunction in python - Learned about
itemgetterin theoperatormodule - How does one verbalizing
lambdafunction? - How can
itemgetterreplacelambdafunction ? - Use
collectionslibrary that can give summary statistics on collections maxfunction also takes in akeyargument that can be used to specify the criterion for sorting the iterablefstringare format string literals are super useful in printing stuff
Chapter 4
- Much of the Python language is itself implemented using
dicts - When one creates a dictionary in Python using
{'a':12}, Python creates a hash function using the inputaand then stores the value12in that location. - Most important properties of Python
dicts- always store key-value pairs together
- guarantee very fast lookup for keys
- ensure key uniqueness
- don’t guarantee anything on the value of the lookups
setsaredictswithout valuesUTCis a compromise between Coordinated Universal Time (English) and French abbreviation- The actual timezones , for example,
GMTis defined as an offset toUTC - Python time representation as a continuum
- float -> tuples -> structs -> string
- human readable and machine operability go against each other
- Python time as a tuple with nine fields
- Fun Fact: If you’re a native English speaker, you might be wondering why the abbreviation for “Coordinated Universal Time” is UTC rather than the more obvious CUT. However, if you’re a native French speaker, you would call it “Temps Universel Coordonné,” which suggests a different abbreviation: TUC.
- To format a string, one uses
directives time.struct_timecan be converted to string usingasctimeandstrftime- the latter is more flexible
strptimeconverts a string to a time objecttime.perf_countercan be used to measure time between operationstime.ctimegives a string representation of timetime.timegives the number of seconds since epoch. It takes in to account fractional secondstime.gtimeandtime.localtimeare ways to convert time in seconds tostruct_timedict.keyshas several methods similar to sets
Chapter 5
withis not meant to be only used withfilesIt is calledcontext managerand can be used with any python class that has__enter__method and__exit__method- When the context method is used in the context of
file, then the__enter__method merely returns the file object. When all the operations are done, the__exit__method flushes the file and closes the object - It is better to use
globas you can retrieve a specific pattern in filenames.In the case ofos.listdir, you cannot specify patterns - it returns all the files in a given directory pathlibis another way to access objects in a directory- since paths are not strings, the functionality to handle paths are spread across several modules such as
glob,shutil,os - This chapter introduced me to
pathliband I was super impressed with the functionality that I am going to use it for all my tasks that deal with going through files and directories pathlibis powerful because- it directly represents the underlying object
- it has super useful functions that make it easy to perform many operations on files and directories
- it is consistent across several operating systems
json.loadcan be used on a file buffer to read all the content in to a list of dictswithcontext manager can work with more than one objects
Chapter 6
__code__attribute contains the core of the function, including the bytecodes in to which the function was compiled.__code__.co_argcountgives the number of arguments that the function consumes- Do not use mutable arguments in Python functions. It is better to assign the default value as
None - When passing a mutable value as a default argument in a function, the default argument is mutated anytime the value is mutated. One can come across a situation that the default value changes across multiple invocations of the function
- Python has four levels of scoping(LEGB):
- Local
- Enclosing function
- Global
- Built-ins
- If you are in a function all four scopes are searched for
- The way the python looks for a variable is
- It first looks for the variable in the local scope
- It then looks for the variable in the enclosing function scope
- It then looks for the variable in the global name space
- It then looks for the variable in the built-ins
nonlocaltells python that any update to the variable should be done to the variable in the immediate outside scope of the functionsplittakes amaxsplitargument that helps you unpack values according to predefined splits- one can use
operatormodule to get a unified interface for most of mathematical functions
Chapter 7
- the syntax for list comprehension and generator looks similar. Wherever possible, you generator
- if you want to read a binary file, you need to specify the encoding
- if you open the file in ‘rb’ mode, then Python opens the file and does not attempt to read the file in string
- if you have two sets
aandb, then if you have to check whether a is subset of b, you can easily do viaa<b
Chapter 8
Real Python course on Modules and Packages
importmodule necessitates the use of.notation to access the objects- individual objects can be imported via
from module import x - you can import specific modules within a function
dir()is a built in function that gives the defined names in the namespace- object names are store in the local symbol table
dir(modulename)gives the namespace for the modulename- when a
.pyfile is imported, the dunder variable__name__is set to the name of the module - when a
.pyfile is run as a standalone script,__name__is set to the string__main__ __name__dunder name- one can test a piece of code in a python program via two ways
- load the code as a module and write tests
- write the tests as a part of
__main__and execute the python code
- python imports the module only once. If there is need to reload the module, one can use
importliband then usereloadfunction packagesallow hierarchical structuring of namespace using dot notationpackage intializationentails creating__init__.pyfile- if you put
importstatements in__init__.pyall the imported modules are available for a program that imports the pkg folder from pkg import *works if there is__all__variable in__init__.py- packages can contain nested packages
..is used for relative imports- There are two ways to invoke a python program - either step in to the directory that contains the script and run the code under global name space or step in to any root folder that has visibility on the script and run it from the root folder. In the latter, one should specify
-moption - if you remove
__init__.pyfrom a folder, python will stop searching for modules under that folder - when we drop
.py, we are telling python to use the namespace and use the-moption. The code runs as a part of an imported module and hence the dunder variable name takes the name of the package
Learnings from the chapter
- Modules are useful for creating reusable code
- Modules are useful for creating namespaces
- curated list of Python packages - https://awesome-python.com/
builtinsis a namespace in python that contains all the standard modulesimportdefines a new variable that references a namespacefrom os import sep, pathcreatessepandpathnames in the namespaceflooralways rounds to the nearest zero for positive numbers whereas it rounds away from zero for negative numbersceilalways round to the nearest zero for negative numbers whereas it rounds away from zero from positive numbersroundimplements bankers rounding, i.e, it rounds towards the closest even significant numbers, i.e.round(2.5)gives2whereasround(3.5)rounds to4- It is always better to use
Decimalclass in python Pythonkeeps a track of its modules by searching the loaded modules insys.modules- I will stop using
floatand always useDecimalclass poetrypackage can be used to bundle your source code as a packagePyPIhas about 250,000 packages__name__is either defined as the current module name or__main__- variable lookup - LEGB - Local, Enclosing, Global, Builtins
Chapter 9
- The first parameter for every class method is
self. However it is not a reserved word in Python and it comes from Smalltalk language, whose object system influenced Python - When you call a
classto instantiate an object, it looks for the name in the global namespace and then invokes the__new__constructor. The__new__constructor is responsible for creating the object and invoking__init__method before returning the instance object to the caller. - The job of any
__init__method is to add attributes to the instance - One can always add instance attributes outside of init dunder method but it is considered good practice to add all the instance attributes at one place, i.e. in the init dunder method
- Fantastic article on type checking at Real Python
- when you pass a
splatoperator, it results in a tuple that can be accessed in the function - Python searches the attributes based on ICPO - instance, class, parent, object
- Today i have understood why the following code works
|
|
The above code works because python checks for upper method on s. Since it does not find that method, it goes and checks the type(s) has that method. In the above case, type(s) is string and string has an =upper=method. In the case when the class does not have an attribute, it looks for the parent class and checks to see if it has an attribute
- the ultimate parent for every object in python is
object - One can specify class attributes as well as instance attributes
- One can use
self.__class__.__name__to obtain the class name - This chapter has been superuseful to me as I had forgotten most of the oops concepts in Python. Stumbled on to OOPS Python path on real python. Need to work through the course
Chapter 10
strings,listsanddictsare iterables because they implement the iterator protocol- Three parts of a
forloop- It asks the object whether it is an iterable or not using
__iter__built-in function. This function invokes the__iter__method on the target object. Whatever the__iter__function returns ,it is an iterator - If the object was an iterator, then the
forloop invokes thenextbuilt-in function on the iterator that was returned. That function invokes__next__on the iterator - if =_next__ raises an exception, then the loop exits
- It asks the object whether it is an iterable or not using
- To make any class in to an iterable, it must adhere to the following protocol:
- it must implement
__iter__method that returns the object - it must implement
__next__method that returns the next value - it must raise
StopIterationif the index runs though the entire data
- it must implement
- There is a difference between
iterableanditerator. The former is an object that is put in a loop which returns an iterator object. The latter actually follows the iterator protocol. Most of the cases, the iterable and iterator are the same object. However there are cases such as strings and lists, that return a separate iterable object - Iterable is a category of data in Python
itertoolsis a module that implements many classes that follow the iterator protocol- Awesome explanation of the difference between
iterableanditeratorusingMyEnumerateclass andMyEnumerateIteratorclass. Most of the boiler plate code needed for iteration is present in the helper class - Iterator can return whatever data it wants until it hits a
StopIterationexception - ways to convert an iterator class to generator functions
- Learned a nice way of using boolean
orfunction to cut down redundant code - There are three different ways to create an iterator
- add the appropriate methods to the class
- write a generator function
- write a generator expression
Takeaway
After a very long time, I have actually completed a book from cover to cover. I have worked through all the exercises in the book and have learned a ton of Python in the process. Let me recollect all my learnings from this book
- difference between iterator and iterable
- how does one write a generator function ?
- how does one enable to class to be an iterable ?
- class can have attributes and so can the instances
- composition and inheritance in python OOP
- the way attributes are looked up instance-class-parent-object - IOCP
- the way variables are looked up local-enclosing-global-builtin - LEGB
- the way namespaces are structured
- importing means creating variables in the namespace
- pathlib module - a fantastic module to work with files in a file system
- argparse module - a fantastic module to create command line programs
- why should one use
__init__.pyin a package - poetry can help in automatically creating a package out of your source code
- mutable vs immutable datatypes
- list comprehensions, dict comprehensions, set comprehensions
- PyPI has about 250,000 packages
- Type hints introduced in Python 3
- Interesting methods on
dictssuch as update methods - one can iterate through strings, lists as they all implement iter functions
- it is always preferable to use generator expressions whereever possible as a substitute to list comprehensions
- way to use
keyin sorting methods - use of
keyinmaxmethod collectionsmodule that has a lot of useful classes such asdefaultdict,Counterimportlibmodule to reload module multiple times in a REPL
After going back to all the points that I had written while going through the book, here are some of the additional takeaways:
- splat operator
- itemgettr and attrgettr functions
timemodule ,datetimemodule - super useful modules- learnt about
Decimalclass - banker’s rounding
- way to incorporate timezones in to python datetime objects
withcan work with a lot more python objects than merely file instancesa<bfor sets- dunder name
__name__ - There are close to 1 billion unicode characters