diff --git a/README.md b/README.md
index 9cb22ef..b4205fe 100644
--- a/README.md
+++ b/README.md
@@ -25,31 +25,39 @@ Moreover, if you want to add more content to this book then kindly submit a pull
Table of Contents:
------------------
-- [\*args and \*\*kwargs](args_and_kwargs.rst)
-- [Debugging](debugging.rst)
-- [Generators](generators.rst)
-- [Map & Filter](map_filter.rst)
-- [``set`` Data Structure](set_-_data_structure.rst)
-- [Ternary Operators](ternary_operators.rst)
-- [Decorators](decorators.rst)
-- [Global & Return](global_&_return.rst)
-- [Mutation](mutation.rst)
-- [\_\_slots\_\_ Magic](__slots__magic.rst)
-- [Virtual Environment](virtual_environment.rst)
-- [Collections](collections.rst)
-- [Enumerate](enumerate.rst)
-- [Object introspection](object_introspection.rst)
-- [Comprehensions](comprehensions.rst)
-- [Exceptions](exceptions.rst)
-- [Lambdas](lambdas.rst)
-- [One Liners](one_liners.rst)
-- [For - Else](for_-_else.rst)
-- [Python C extensions](python_c_extension.rst)
-- [Open function](open_function.rst)
-- [Targeting Python 2+3](targeting_python_2_3.rst)
-- [Coroutines](coroutines.rst)
-- [Function caching](function_caching.rst)
-- [Context managers](context_managers.rst)
+1) Programmer tools
+ - [Virtual Environment](virtual_environment.rst)
+ - [Debugging](debugging.rst)
+ - [Object introspection](object_introspection.rst)
+2) Syntax
+ - [Exceptions](exceptions.rst)
+ - [For - Else](for_-_else.rst)
+ - [Ternary Operators](ternary_operators.rst)
+ - [Global & Return](global_&_return.rst)
+ - [Open function](open_function.rst)
+ - [\*args and \*\*kwargs](args_and_kwargs.rst)
+ - [Context managers](context_managers.rst)
+3) Functional programming
+ - [Enumerate](enumerate.rst)
+ - [Lambdas](lambdas.rst)
+ - [``set`` Data Structure](set_-_data_structure.rst)
+ - [Map & Filter](map_filter.rst)
+ - [Comprehensions](comprehensions.rst)
+4) Data structures
+ - [Generators](generators.rst)
+ - [Coroutines](coroutines.rst)
+ - [Classes](classes.rst)
+5) Data types
+ - [Collections](collections.rst)
+ - [Mutation](mutation.rst)
+ - [\_\_slots\_\_ Magic](__slots__magic.rst)
+6) Decorators
+ - [What is a decorator?](decorators.rst)
+ - [Function caching](function_caching.rst)
+7) Extras
+ - [One Liners](one_liners.rst)
+ - [Targeting Python 2+3](targeting_python_2_3.rst)
+ - [Python C extensions](python_c_extension.rst)
Author:
------
@@ -61,9 +69,23 @@ Acknowledgement:
- [Philipp Hagemeister](https://github.com/phihag):
-He wrote the chapter on Open function and has helped me a lot along the way. Thanks Philipp! :+1:
+He wrote the chapter on Open function. Thanks Philipp! :+1:
+
+Translation:
+------------
+If you want to translate this book in any other language then kindly let [me know](mailto:yasoob.khld@gmail.com). I would love your contribution. The currently translated versions are listed below:
+
+- [Chinese](https://github.com/eastlakeside/interpy-zh)
+- [Russian](https://github.com/lancelote/interpy-ru)
+- [Korean](https://github.com/DDanggle/interpy-kr)
+- [Portuguese](https://github.com/joanasouza/intermediatePython)
+- [Spanish](https://github.com/cursospython/LibroPython)
License:
-------
This book is released under the [following](http://creativecommons.org/licenses/by-nc-sa/4.0/) CC license (CC BY-NC-SA 4.0).
+
+If you end up using/recommending this book to someone then kindly [let me know](mailto:yasoob.khld@gmail.com). :smile:
+
+
diff --git a/_static/custom.css b/_static/custom.css
new file mode 100644
index 0000000..bcdeef2
--- /dev/null
+++ b/_static/custom.css
@@ -0,0 +1,6 @@
+
+@media screen and (min-width: 1000px){
+ iframe{
+ margin-left: 330px;
+ }
+}
\ No newline at end of file
diff --git a/_templates/breadcrumbs.html b/_templates/breadcrumbs.html
new file mode 100644
index 0000000..f3f3b94
--- /dev/null
+++ b/_templates/breadcrumbs.html
@@ -0,0 +1,89 @@
+{# Support for Sphinx 1.3+ page_source_suffix, but don't break old builds. #}
+
+{% if page_source_suffix %}
+{% set suffix = page_source_suffix %}
+{% else %}
+{% set suffix = source_suffix %}
+{% endif %}
+
+{% if meta is defined and meta is not none %}
+{% set check_meta = True %}
+{% else %}
+{% set check_meta = False %}
+{% endif %}
+
+{% if check_meta and 'github_url' in meta %}
+{% set display_github = True %}
+{% endif %}
+
+{% if check_meta and 'bitbucket_url' in meta %}
+{% set display_bitbucket = True %}
+{% endif %}
+
+{% if check_meta and 'gitlab_url' in meta %}
+{% set display_gitlab = True %}
+{% endif %}
+
+
+
+
New book released!
+
Hi! I just released the alpha version of my new book; Practical Python Projects.
+ Learn more about it
+ on my blog. In 325+ pages, I will teach you how to implement 12 end-to-end projects.
+ You can buy it from Feldroy.com.
+
+
+
+
+ {% if (theme_prev_next_buttons_location == 'top' or theme_prev_next_buttons_location == 'both') and (next or prev) %}
+
+ {% endif %}
+
+
diff --git a/args_and_kwargs.rst b/args_and_kwargs.rst
index 332ff2f..85ed174 100644
--- a/args_and_kwargs.rst
+++ b/args_and_kwargs.rst
@@ -3,21 +3,20 @@
I have come to see that most new python programmers have a hard time
figuring out the \*args and \*\*kwargs magic variables. So what are they
-? First of all let me tell you that it is not necessary to write \*args
+? First of all, let me tell you that it is not necessary to write \*args
or \*\*kwargs. Only the ``*`` (asterisk) is necessary. You could have
also written \*var and \*\*vars. Writing \*args and \*\*kwargs is just a
-convention. So now lets take a look at \*args first.
+convention. So now let's take a look at \*args first.
Usage of \*args
^^^^^^^^^^^^^^^
\*args and \*\*kwargs are mostly used in function definitions. \*args
-and \*\*kwargs allow you to pass a variable number of arguments to a
-function. What variable means here is that you do not know beforehand
-how many arguments can be passed to your function by the user
-so in this case you use these two keywords. \*args is used to send a
-**non-keyworded** variable length argument list to the function. Here's
-an example to help you get a clear idea:
+and \*\*kwargs allow you to pass an unspecified number of arguments to a
+function, so when writing the function definition, you do not need to
+know how many arguments will be passed to your function. \*args is used to
+send a **non-keyworded** variable length argument list to the function.
+Here's an example to help you get a clear idea:
.. code:: python
@@ -51,10 +50,10 @@ arguments** in a function. Here is an example to get you going with it:
def greet_me(**kwargs):
for key, value in kwargs.items():
- print("{0} == {1}".format(key, value))
+ print("{0} = {1}".format(key, value))
>>> greet_me(name="yasoob")
- name == yasoob
+ name = yasoob
So you can see how we handled a keyworded argument list in our function.
This is just the basics of \*\*kwargs and you can see how useful it is.
diff --git a/classes.rst b/classes.rst
index 3e09bb2..9485307 100644
--- a/classes.rst
+++ b/classes.rst
@@ -53,7 +53,7 @@ Let's take a look at an example:
b.pi
# Output: 50
-There are not many issues while using mutable class variables. This is
+There are not many issues while using immutable class variables. This is
the major reason due to which beginners do not try to learn more about
this subject because everything works! If you also believe that instance
and class variables can not cause any problem if used incorrectly then
@@ -159,7 +159,7 @@ its ``__init__`` method is called. For example:
# called
You can see that ``__init__`` is called immediately after an instance is
-created. You can also pass arguments to the class during it's
+created. You can also pass arguments to the class during its
initialization. Like this:
.. code:: python
@@ -200,23 +200,24 @@ Implementing **getitem** in a class allows its instances to use the []
def __getitem__(self,i):
return self.info[i]
- foo = OldClass()
- foo['title']
+ foo = GetTest()
+
+ foo['name']
# Output: 'Yasoob'
foo['number']
- # Output: 36845124
+ # Output: 12345812
Without the ``__getitem__`` method we would have got this error:
.. code:: python
- >>> foo['title']
+ >>> foo['name']
Traceback (most recent call last):
File "", line 1, in
TypeError: 'GetTest' object has no attribute '__getitem__'
-Static, Class & Abstract methods
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+.. Static, Class & Abstract methods
+.. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/collections.rst b/collections.rst
index c251b62..f668ca6 100644
--- a/collections.rst
+++ b/collections.rst
@@ -8,7 +8,8 @@ their usefulness.
The ones which we will talk about are:
- ``defaultdict``
-- ``counter``
+- ``OrderedDict``
+- ``Counter``
- ``deque``
- ``namedtuple``
- ``enum.Enum`` (outside of the module; Python 3.4+)
@@ -67,8 +68,8 @@ share a solution using ``defaultdict``.
.. code:: python
- import collections
- tree = lambda: collections.defaultdict(tree)
+ from collections import defaultdict
+ tree = lambda: defaultdict(tree)
some_dict = tree()
some_dict['colours']['favourite'] = "yellow"
# Works fine
@@ -82,7 +83,43 @@ sample code:
print(json.dumps(some_dict))
# Output: {"colours": {"favourite": "yellow"}}
-``counter``
+``OrderedDict``
+^^^^^^^^^^^^^^^^^^^
+
+``OrderedDict`` keeps its entries sorted as they are initially inserted.
+Overwriting a value of an existing key doesn't change the position of
+that key. However, deleting and reinserting an entry moves the key to
+the end of the dictionary.
+
+**Problem:**
+
+.. code:: python
+
+ colours = {"Red" : 198, "Green" : 170, "Blue" : 160}
+ for key, value in colours.items():
+ print(key, value)
+ # Output:
+ # Green 170
+ # Blue 160
+ # Red 198
+ # Entries are retrieved in an unpredictable order
+
+**Solution:**
+
+.. code:: python
+
+ from collections import OrderedDict
+
+ colours = OrderedDict([("Red", 198), ("Green", 170), ("Blue", 160)])
+ for key, value in colours.items():
+ print(key, value)
+ # Output:
+ # Red 198
+ # Green 170
+ # Blue 160
+ # Insertion order is preserved
+
+``Counter``
^^^^^^^^^^^^^^^
Counter allows us to count the occurrences of a particular item. For
@@ -179,9 +216,15 @@ example so here you go:
.. code:: python
- d = deque(maxlen=30)
+ d = deque([0, 1, 2, 3, 5], maxlen=5)
+ print(d)
+ # Output: deque([0, 1, 2, 3, 5], maxlen=5)
+
+ d.extend([6])
+ print(d)
+ #Output: deque([1, 2, 3, 5, 6], maxlen=5)
-Now whenever you insert values after 30, the leftmost value will be
+Now whenever you insert values after 5, the leftmost value will be
popped from the list. You can also expand the list in any direction with
new values:
@@ -193,9 +236,6 @@ new values:
print(d)
# Output: deque([0, 1, 2, 3, 4, 5, 6, 7, 8])
-This was just a quick drive through the ``collections`` module. Make
-sure you read the official documentation after reading this.
-
``namedtuple``
^^^^^^^^^^^^^^^^^^
@@ -235,7 +275,7 @@ You can now see that we can access members of a tuple just by their
name using a ``.``. Let's dissect it a little more. A named tuple has two
required arguments. They are the tuple name and the tuple field\_names.
In the above example our tuple name was 'Animal' and the tuple
-field\_names were 'name', 'age' and 'cat'. Namedtuple makes your tuples
+field\_names were 'name', 'age' and 'type'. Namedtuple makes your tuples
**self-document**. You can easily understand what is going on by having
a quick glance at your code. And as you are not bound to use integer
indexes to access members of a tuple, it makes it more easy to maintain
@@ -343,3 +383,6 @@ methods will get you the value for ``cat``:
Species(1)
Species['cat']
Species.cat
+
+This was just a quick drive through the ``collections`` module. Make
+sure you read the official documentation after reading this.
diff --git a/comprehensions.rst b/comprehensions.rst
index d4061e9..1db2a61 100644
--- a/comprehensions.rst
+++ b/comprehensions.rst
@@ -3,12 +3,13 @@ Comprehensions
Comprehensions are a feature of Python which I would really miss if I
ever have to leave it. Comprehensions are constructs that allow
-sequences to be built from other sequences. Three types of
+sequences to be built from other sequences. Several types of
comprehensions are supported in both Python 2 and Python 3:
- list comprehensions
- dictionary comprehensions
- set comprehensions
+- generator comprehensions
We will discuss them one by one. Once you get the hang of using ``list``
comprehensions then you can use any of them easily.
@@ -33,12 +34,12 @@ Here is a short example:
.. code:: python
- multiples = [i for i in range(30) if i % 3 is 0]
+ multiples = [i for i in range(30) if i % 3 == 0]
print(multiples)
# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
This can be really useful to make lists quickly. It is even preferred by
-some instead of the ``filter`` function. list comprehensions really
+some instead of the ``filter`` function. List comprehensions really
shine when you want to supply a list to a method or function to make a
new list by appending to it in each iteration of the ``for`` loop. For
instance you would usually do something like this:
@@ -91,3 +92,17 @@ that they use braces ``{}``. Here is an example:
squared = {x**2 for x in [1, 1, 2]}
print(squared)
# Output: {1, 4}
+
+``generator`` comprehensions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+They are also similar to list comprehensions. The only difference is that they don't allocate memory for the whole list but generate one item at a time, thus more memory efficient.
+
+.. code:: python
+
+ multiples_gen = (i for i in range(30) if i % 3 == 0)
+ print(multiples_gen)
+ # Output: at 0x7fdaa8e407d8>
+ for x in multiples_gen:
+ print(x)
+ # Outputs numbers
diff --git a/conf.py b/conf.py
index 7184904..1de0eaa 100644
--- a/conf.py
+++ b/conf.py
@@ -42,7 +42,7 @@
# General information about the project.
project = u'Python Tips'
-copyright = u'2015, Muhammad Yasoob Ullah Khalid'
+copyright = u'2017, Muhammad Yasoob Ullah Khalid'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -217,7 +217,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'pythontips', u'Python Tips Documentation',
+ ('index', 'pythontips', u'Python Tips',
[u'Muhammad Yasoob Ullah Khalid'], 1)
]
@@ -231,7 +231,7 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'PythonTips', u'Python Tips Documentation',
+ ('index', 'PythonTips', u'Python Tips',
u'Muhammad Yasoob Ullah Khalid', 'PythonTips', 'One line description of project.',
'Miscellaneous'),
]
@@ -255,7 +255,7 @@
epub_title = u'Python Tips'
epub_author = u'Muhammad Yasoob Ullah Khalid'
epub_publisher = u'Muhammad Yasoob Ullah Khalid'
-epub_copyright = u'2015, Muhammad Yasoob Ullah Khalid'
+epub_copyright = u'2017, Muhammad Yasoob Ullah Khalid'
#epub_theme = 'epub'
@@ -313,3 +313,4 @@
#html_theme_path = ['_themes']
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme = 'sphinx_rtd_theme'
+
diff --git a/context_managers.rst b/context_managers.rst
index 45b2b8d..261ebd6 100644
--- a/context_managers.rst
+++ b/context_managers.rst
@@ -1,4 +1,4 @@
-Context managers
+Context Managers
----------------
Context managers allow you to allocate and release resources precisely
@@ -30,16 +30,16 @@ advantage of using a ``with`` statement is that it makes sure our file
is closed without paying attention to how the nested block exits.
A common use case of context managers is locking and unlocking resources
-and closing opened files (as I have already showed you).
+and closing opened files (as I have already shown you).
-Let's see how we can implement our own Context Manager. This would allow
+Let's see how we can implement our own Context Manager. This should allow
us to understand exactly what's going on behind the scenes.
-Implementing Context Manager as a Class:
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Implementing a Context Manager as a Class:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At the very least a context manager has an ``__enter__`` and
-``__exit__`` methods defined. Let's make our own file opening Context
+``__exit__`` method defined. Let's make our own file-opening Context
Manager and learn the basics.
.. code:: python
@@ -52,7 +52,7 @@ Manager and learn the basics.
def __exit__(self, type, value, traceback):
self.file_obj.close()
-Just by defining ``__enter__`` and ``__exit__`` methods we can use it in
+Just by defining ``__enter__`` and ``__exit__`` methods we can use our new class in
a ``with`` statement. Let's try:
.. code:: python
@@ -60,20 +60,20 @@ a ``with`` statement. Let's try:
with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')
-Our ``__exit__`` function accepts three arguments. They are required by
+Our ``__exit__`` method accepts three arguments. They are required by
every ``__exit__`` method which is a part of a Context Manager class.
Let's talk about what happens under-the-hood.
-1. The ``with`` statement stores the ``__exit__`` method of ``File``
+1. The ``with`` statement stores the ``__exit__`` method of the ``File``
class.
-2. It calls the ``__enter__`` method of ``File`` class.
-3. ``__enter__`` method opens the file and returns it.
-4. the opened file handle is passed to ``opened_file``.
-5. we write to the file using ``.write()``
-6. ``with`` statement calls the stored ``__exit__`` method.
-7. the ``__exit__`` method closes the file.
-
-Handling exceptions
+2. It calls the ``__enter__`` method of the ``File`` class.
+3. The ``__enter__`` method opens the file and returns it.
+4. The opened file handle is passed to ``opened_file``.
+5. We write to the file using ``.write()``.
+6. The ``with`` statement calls the stored ``__exit__`` method.
+7. The ``__exit__`` method closes the file.
+
+Handling Exceptions
^^^^^^^^^^^^^^^^^^^
We did not talk about the ``type``, ``value`` and ``traceback``
@@ -92,20 +92,20 @@ instance:
with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function('Hola!')
-Let's list down the steps which are taken by the ``with`` statement when
-an error is encountered.
+Let's list the steps which are taken by the ``with`` statement when
+an error is encountered:
1. It passes the type, value and traceback of the error to the
``__exit__`` method.
2. It allows the ``__exit__`` method to handle the exception.
-3. If ``__exit__`` returns True then the exception was gracefully
+3. If ``__exit__`` returns ``True`` then the exception was gracefully
handled.
-4. If anything else than True is returned by ``__exit__`` method then
- the exception is raised by ``with`` statement.
+4. If anything other than ``True`` is returned by the ``__exit__`` method then
+ the exception is raised by the ``with`` statement.
In our case the ``__exit__`` method returns ``None`` (when no return
statement is encountered then the method returns ``None``). Therefore,
-``with`` statement raises the exception.
+the ``with`` statement raises the exception:
.. code:: python
@@ -132,11 +132,11 @@ Let's try handling the exception in the ``__exit__`` method:
# Output: Exception has been handled
-Our ``__exit__`` method returned True, therefore no exception was raised
+Our ``__exit__`` method returned ``True``, therefore no exception was raised
by the ``with`` statement.
-This is not the only way to implement context managers. There is another
-way and we will be looking at it in this next section.
+This is not the only way to implement Context Managers. There is another
+way and we will be looking at it in the next section.
Implementing a Context Manager as a Generator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -153,8 +153,10 @@ Let's see a basic, useless example:
@contextmanager
def open_file(name):
f = open(name, 'w')
- yield f
- f.close()
+ try:
+ yield f
+ finally:
+ f.close()
Okay! This way of implementing Context Managers appear to be more
intuitive and easy. However, this method requires some knowledge about
@@ -167,11 +169,11 @@ Let's dissect this method a little.
1. Python encounters the ``yield`` keyword. Due to this it creates a
generator instead of a normal function.
2. Due to the decoration, contextmanager is called with the function
- name (open\_file) as it's argument.
-3. The ``contextmanager`` function returns the generator wrapped by the
+ name (``open_file``) as its argument.
+3. The ``contextmanager`` decorator returns the generator wrapped by the
``GeneratorContextManager`` object.
4. The ``GeneratorContextManager`` is assigned to the ``open_file``
- function. Therefore, when we later call ``open_file`` function, we
+ function. Therefore, when we later call the ``open_file`` function, we
are actually calling the ``GeneratorContextManager`` object.
So now that we know all this, we can use the newly generated Context
@@ -181,4 +183,3 @@ Manager like this:
with open_file('some_file') as f:
f.write('hola!')
-
diff --git a/coroutines.rst b/coroutines.rst
index 21ad8b5..113cb30 100644
--- a/coroutines.rst
+++ b/coroutines.rst
@@ -26,8 +26,8 @@ We then commonly use it in a ``for`` loop like this:
print(i)
It is fast and does not put a lot of pressure on memory because it
-**generates** the values on the fly rather then storing them in a list.
-Now if we use ``yield`` in the above example more generally we get a
+**generates** the values on the fly rather than storing them in a list.
+Now, if we use ``yield`` in the above example, more generally, we get a
coroutine. Coroutines consume values which are sent to it. A very basic
example would be a ``grep`` alternative in Python:
@@ -41,7 +41,7 @@ example would be a ``grep`` alternative in Python:
print(line)
Wait! What does ``yield`` return? Well we have turned it into a
-coroutine. It does not contain any value initially instead we supply it
+coroutine. It does not contain any value initially, instead we supply it
values externally. We supply values by using the ``.send()`` method.
Here is an example:
@@ -55,13 +55,13 @@ Here is an example:
search.send("I love coroutines instead!")
# Output: I love coroutines instead!
-The sent values are accessed by yield. Why did we run ``next()``? It is
-done to start the coroutine. Just like ``generators`` coroutines do not
-start the function immediately. Instead they run it in response to
-``__next__()`` and ``.send()`` methods. Therefore you have to run
+The sent values are accessed by ``yield``. Why did we run ``next()``? It is
+required in order to start the coroutine. Just like ``generators``, coroutines do not
+start the function immediately. Instead they run it in response to the
+``__next__()`` and ``.send()`` methods. Therefore, you have to run
``next()`` so that the execution advances to the ``yield`` expression.
-We can close a coroutine by calling the ``.close()`` method. Like:
+We can close a coroutine by calling the ``.close()`` method:
.. code:: python
diff --git a/debugging.rst b/debugging.rst
index 58af25a..5e591f8 100644
--- a/debugging.rst
+++ b/debugging.rst
@@ -2,14 +2,14 @@ Debugging
---------
Debugging is also something which once mastered can greatly enhance your
-bug hunting skills. Most of the newcomers neglect the importance of the
+bug hunting skills. Most newcomers neglect the importance of the
Python debugger (``pdb``). In this section I am going to tell you only a
few important commands. You can learn more about it from the official
documentation.
-**Running from commandline**
+**Running from the command line**
-You can run a script from the commandline using the Python debugger.
+You can run a script from the command line using the Python debugger.
Here is an example:
.. code:: python
@@ -58,3 +58,7 @@ function.
These are just a few commands. ``pdb`` also supports post mortem. It is
also a really handy function. I would highly suggest you to look at the
official documentation and learn more about it.
+
+**Note:**
+
+It might seem unintuitive to use `pdb.set_trace()` if you are new to this. Fortunately, if you are using Python 3.7+ then you can simply use the `breakpoint()` [built-in function](https://docs.python.org/3/library/functions.html#breakpoint). It automatically imports `pdb` and calls `pdb.set_trace()`.
diff --git a/decorators.rst b/decorators.rst
index 9d4c14e..30509fe 100644
--- a/decorators.rst
+++ b/decorators.rst
@@ -2,8 +2,8 @@ Decorators
----------
Decorators are a significant part of Python. In simple words: they are
-functions which modify the functionality of another function. They help
-to make our code shorter and more Pythonic. Most of the beginners do not
+functions which modify the functionality of other functions. They help
+to make our code shorter and more Pythonic. Most beginners do not
know where to use them so I am going to share some areas where
decorators can make your code more concise.
@@ -12,10 +12,10 @@ First, let's discuss how to write your own decorator.
It is perhaps one of the most difficult concepts to grasp. We will take
it one step at a time so that you can fully understand it.
-Everything in python is an object:
+Everything in Python is an object:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-First of all let's understand functions in python:
+First of all let's understand functions in Python:
.. code:: python
@@ -170,14 +170,14 @@ previous decorator and make a little bit more usable program:
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
a_function_requiring_decoration()
- #outputs:I am doing some boring work before executing a_function_requiring_decoration()
+ #outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
- # I am doing some boring work after executing a_function_requiring_decoration()
+ # I am doing some boring work after executing a_func()
Did you get it? We just applied the previously learned principles. This
-is exactly what the decorators do in python! They wrap a function and
-modify its behaviour in one way or the another. Now you might be
-wondering that we did not use the @ anywhere in our code? That is just a
+is exactly what the decorators do in Python! They wrap a function and
+modify its behaviour in one way or another. Now you might be
+wondering why we did not use the @ anywhere in our code? That is just a
short way of making up a decorated function. Here is how we could have
run the previous code sample using @.
@@ -206,9 +206,9 @@ Python. Now there is one problem with our code. If we run:
# Output: wrapTheFunction
That's not what we expected! Its name is
-"a\_function\_requiring\_decoration". Well our function was replaced by
+"a\_function\_requiring\_decoration". Well, our function was replaced by
wrapTheFunction. It overrode the name and docstring of our function.
-Luckily Python provides us a simple function to solve this problem and
+Luckily, Python provides us a simple function to solve this problem and
that is ``functools.wraps``. Let's modify our previous example to use
``functools.wraps``:
@@ -263,7 +263,7 @@ decorators.
Note: ``@wraps`` takes a function to be decorated and adds the
functionality of copying over the function name, docstring, arguments
-list, etc. This allows to access the pre-decorated function's properties
+list, etc. This allows us to access the pre-decorated function's properties
in the decorator.
Use-cases:
@@ -273,7 +273,7 @@ Now let's take a look at the areas where decorators really shine and
their usage makes something really easy to manage.
Authorization
-~~~~~~~~~~~~
+~~~~~~~~~~~~~
Decorators can help to check whether someone is authorized to use an
endpoint in a web application. They are extensively used in Flask web
@@ -296,7 +296,7 @@ authentication:
return decorated
Logging
-~~~~~~~~~~~~
+~~~~~~~
Logging is another area where the decorators shine. Here is an example:
@@ -321,3 +321,133 @@ Logging is another area where the decorators shine. Here is an example:
# Output: addition_func was called
I am sure you are already thinking about some clever uses of decorators.
+
+Decorators with Arguments
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Come to think of it, isn't ``@wraps`` also a decorator? But, it takes an
+argument like any normal function can do. So, why can't we do that too?
+
+This is because when you use the ``@my_decorator`` syntax, you are
+applying a wrapper function with a single function as a parameter.
+Remember, everything in Python is an object, and this includes
+functions! With that in mind, we can write a function that returns
+a wrapper function.
+
+Nesting a Decorator Within a Function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's go back to our logging example, and create a wrapper which lets
+us specify a logfile to output to.
+
+.. code:: python
+
+ from functools import wraps
+
+ def logit(logfile='out.log'):
+ def logging_decorator(func):
+ @wraps(func)
+ def wrapped_function(*args, **kwargs):
+ log_string = func.__name__ + " was called"
+ print(log_string)
+ # Open the logfile and append
+ with open(logfile, 'a') as opened_file:
+ # Now we log to the specified logfile
+ opened_file.write(log_string + '\n')
+ return func(*args, **kwargs)
+ return wrapped_function
+ return logging_decorator
+
+ @logit()
+ def myfunc1():
+ pass
+
+ myfunc1()
+ # Output: myfunc1 was called
+ # A file called out.log now exists, with the above string
+
+ @logit(logfile='func2.log')
+ def myfunc2():
+ pass
+
+ myfunc2()
+ # Output: myfunc2 was called
+ # A file called func2.log now exists, with the above string
+
+Decorator Classes
+~~~~~~~~~~~~~~~~~
+
+Now we have our logit decorator in production, but when some parts
+of our application are considered critical, failure might be
+something that needs more immediate attention. Let's say sometimes
+you want to just log to a file. Other times you want an email sent,
+so the problem is brought to your attention, and still keep a log
+for your own records. This is a case for using inheritence, but
+so far we've only seen functions being used to build decorators.
+
+Luckily, classes can also be used to build decorators. So, let's
+rebuild logit as a class instead of a function.
+
+.. code:: python
+
+ class logit(object):
+
+ _logfile = 'out.log'
+
+ def __init__(self, func):
+ self.func = func
+
+ def __call__(self, *args):
+ log_string = self.func.__name__ + " was called"
+ print(log_string)
+ # Open the logfile and append
+ with open(self._logfile, 'a') as opened_file:
+ # Now we log to the specified logfile
+ opened_file.write(log_string + '\n')
+ # Now, send a notification
+ self.notify()
+
+ # return base func
+ return self.func(*args)
+
+
+
+ def notify(self):
+ # logit only logs, no more
+ pass
+
+This implementation has an additional advantage of being much cleaner than
+the nested function approach, and wrapping a function still will use
+the same syntax as before:
+
+.. code:: python
+
+ logit._logfile = 'out2.log' # if change log file
+ @logit
+ def myfunc1():
+ pass
+
+ myfunc1()
+ # Output: myfunc1 was called
+
+Now, let's subclass logit to add email functionality (though this topic
+will not be covered here).
+
+.. code:: python
+
+ class email_logit(logit):
+ '''
+ A logit implementation for sending emails to admins
+ when the function is called.
+ '''
+ def __init__(self, email='admin@myproject.com', *args, **kwargs):
+ self.email = email
+ super(email_logit, self).__init__(*args, **kwargs)
+
+ def notify(self):
+ # Send an email to self.email
+ # Will not be implemented here
+ pass
+
+From here, ``@email_logit`` works just like ``@logit`` but sends an email
+to the admin in addition to logging.
diff --git a/enumerate.rst b/enumerate.rst
index a087c94..3bcd2f6 100644
--- a/enumerate.rst
+++ b/enumerate.rst
@@ -1,18 +1,25 @@
Enumerate
---------
-Enumerate is a built-in function of Python. It's usefulness can not be
+Enumerate is a built-in function of Python. Its usefulness can not be
summarized in a single line. Yet most of the newcomers and even some
advanced programmers are unaware of it. It allows us to loop over
something and have an automatic counter. Here is an example:
.. code:: python
+
+ my_list = ['apple', 'banana', 'grapes', 'pear']
+ for counter, value in enumerate(my_list):
+ print counter, value
- for counter, value in enumerate(some_list):
- print(counter, value)
+ # Output:
+ # 0 apple
+ # 1 banana
+ # 2 grapes
+ # 3 pear
-This is not it. ``enumerate`` also accepts some optional arguments which
-make it even more useful.
+And there is more! ``enumerate`` also accepts an optional argument that
+allows us to specify the starting index of the counter.
.. code:: python
@@ -26,9 +33,9 @@ make it even more useful.
# 3 grapes
# 4 pear
-The optional argument allows us to tell ``enumerate`` from where to
-start the index. You can also create tuples containing the index and
-list item using a list. Here is an example:
+An example of where the optional argument of ``enumerate`` comes in handy
+is creating tuples containing the index and list item using a list. Here
+is an example:
.. code:: python
diff --git a/exceptions.rst b/exceptions.rst
index f55f1b4..ebeb0d5 100644
--- a/exceptions.rst
+++ b/exceptions.rst
@@ -5,9 +5,11 @@ Exception handling is an art which once you master grants you immense
powers. I am going to show you some of the ways in which we can handle
exceptions.
-In basic terminology we are aware of ``try/except`` clause. The code
-which can cause an exception to occur is put in the ``try`` block and
+In basic terminology we are aware of the ``try/except`` structure. The code
+that can cause an exception to occur is put in the ``try`` block and
the handling of the exception is implemented in the ``except`` block.
+The code in the ``except`` block will only execute if the ``try`` block
+runs into an exception.
Here is a simple example:
.. code:: python
@@ -56,12 +58,20 @@ trapping ALL exceptions:
try:
file = open('test.txt', 'rb')
- except Exception:
+ except Exception as e:
# Some logging if you want
- raise
+ raise e
+
+This can be helpful when you have no idea about the exceptions that may
+be thrown by your program. If you are just looking to catch all execptions,
+but don't actually care about what they are, you can even exclude the
+``Exception as e`` part.
-This can be helpful when you have no idea about the exceptions which may
-be thrown by your program.
+Note:: catching all exceptions may have unintended consequences because catching
+all exceptions may also catch the ones you want to occur; for example, in
+many command-line based programs, pressing control+c will terminate the program,
+but if you catch all excepts, the ``KeyboardInterrupt`` will be caught as an
+exception, so pressing control+c will NOT terminate the program.
``finally`` clause
~~~~~~~~~~~~~~~~~~
@@ -89,8 +99,11 @@ to perform clean-up after a script. Here is a simple example:
~~~~~~~~~~~~~~~~~~~
Often times we might want some code to run if **no** exception occurs. This
-can easily be achieved by using an ``else`` clause. Most people don't
-use it and honestly I have myself not used it widely. Here is an
+can easily be achieved by using an ``else`` clause. One might ask: why, if
+you only want some code to run if no exception occurs, wouldn't you simply
+put that code inside the ``try``? The answer is that then any exceptions in
+that code will be caught by the ``try``, and you might not want that. Most
+people don't use it and honestly I have myself not used it widely. Here is an
example:
.. code:: python
@@ -100,12 +113,15 @@ example:
except Exception:
print('exception')
else:
- print('This would only run if no exception occurs.')
+ # any code that should only run if no exception occurs in the try,
+ # but for which exceptions should NOT be caught
+ print('This would only run if no exception occurs. And an error here '
+ 'would NOT be caught.')
finally:
print('This would be printed in every case.')
# Output: I am sure no exception is going to occur!
- # This would only run if no exception occurs.
+ # This would only run if no exception occurs. And an error here would NOT be caught
# This would be printed in every case.
The ``else`` clause would only run if no exception occurs and it would run
diff --git a/for_-_else.rst b/for_-_else.rst
index da01bf6..f7fbe82 100644
--- a/for_-_else.rst
+++ b/for_-_else.rst
@@ -1,12 +1,11 @@
-For - Else
-----------
+``for/else``
+------------
Loops are an integral part of any language. Likewise ``for`` loops are
an important part of Python. However there are a few things which most
-beginners do not know about them. We will discuss a few of them one by
-one.
+beginners do not know about them. We will discuss a few of them one-by-one.
-Let's first start of by what we know. We know that we can use for loops
+Let's first start off with what we know. We know that we can use ``for`` loops
like this:
.. code:: python
@@ -19,24 +18,24 @@ like this:
# Banana
# Mango
-That is the very basic structure of a for loop. Now let's move on to
+That is the very basic structure of a ``for`` loop. Now let's move on to
some of the lesser known features of ``for`` loops in Python.
-``else`` clause:
-^^^^^^^^^^^^^^^^^^^^
+``else`` Clause
+^^^^^^^^^^^^^^^
-For loops also have an ``else`` clause which most of us are unfamiliar
-with. The ``else`` clause executes when the loop completes normally.
-This means that the loop did not encounter any ``break``. They are
-really useful once you understand where to use them. I myself came to
+``for`` loops also have an ``else`` clause which most of us are unfamiliar
+with. The ``else`` clause executes after the loop completes normally.
+This means that the loop did not encounter a ``break`` statement. They are
+really useful once you understand where to use them. I, myself, came to
know about them a lot later.
The common construct is to run a loop and search for an item. If the
-item is found, we break the loop using ``break``. There are two
+item is found, we break out of the loop using the ``break`` statement. There are two
scenarios in which the loop may end. The first one is when the item is
found and ``break`` is encountered. The second scenario is that the loop
-ends. Now we may want to know which one of these is the reason for a
-loops completion. One method is to set a flag and then check it once the
+ends without encountering a ``break`` statement. Now we may want to know which one of these is the reason for a
+loop's completion. One method is to set a flag and then check it once the
loop ends. Another is to use the ``else`` clause.
This is the basic structure of a ``for/else`` loop:
@@ -64,15 +63,14 @@ documentation:
break
It finds factors for numbers between 2 to 10. Now for the fun part. We
-can add an additional ``else`` block which catches the numbers which are
-prime and tells us so:
+can add an additional ``else`` block which catches the numbers which have no factors and are therefore prime numbers:
.. code:: python
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
- prin( n, 'equals', x, '*', n/x)
+ print( n, 'equals', x, '*', n/x)
break
else:
# loop fell through without finding a factor
diff --git a/function_caching.rst b/function_caching.rst
index 865a866..7100aad 100644
--- a/function_caching.rst
+++ b/function_caching.rst
@@ -52,9 +52,9 @@ is a generic cache:
memo = {}
@wraps(function)
def wrapper(*args):
- if args in memo:
+ try:
return memo[args]
- else:
+ except KeyError:
rv = function(*args)
memo[args] = rv
return rv
@@ -67,6 +67,8 @@ is a generic cache:
fibonacci(25)
+**Note:** memoize won't cache unhashable types (dict, lists, etc...) but only the immutable types. Keep that in mind when using it.
+
`Here `__
is a fine article by Caktus Group in which they caught a bug in Django
which occurred due to ``lru_cache``. It's an interesting read. Do check it out.
diff --git a/generators.rst b/generators.rst
index 70da1a7..23cd1b0 100644
--- a/generators.rst
+++ b/generators.rst
@@ -20,8 +20,8 @@ Iterable
An ``iterable`` is any object in Python which has an ``__iter__`` or a
``__getitem__`` method defined which returns an **iterator** or can take
-indexes (Both of these dunder methods are fully explained in a previous
-chapter). In short an ``iterable`` is any object which can provide us
+indexes (You can read more about them `here `_).
+In short an ``iterable`` is any object which can provide us
with an **iterator**. So what is an **iterator**?
Iterator
@@ -134,10 +134,10 @@ element of a sequence. So let's test out our understanding:
As we can see that after yielding all the values ``next()`` caused a
``StopIteration`` error. Basically this error informs us that all the
-values have been yielded. You might be wondering that why don't we get
-this error while using a ``for`` loop? Well the answer is simple. The
+values have been yielded. You might be wondering why we don't get
+this error when using a ``for`` loop? Well the answer is simple. The
``for`` loop automatically catches this error and stops calling
-``next``. Do you know that a few built-in data types in Python also
+``next``. Did you know that a few built-in data types in Python also
support iteration? Let's check it out:
.. code:: python
@@ -149,17 +149,24 @@ support iteration? Let's check it out:
# TypeError: str object is not an iterator
Well that's not what we expected. The error says that ``str`` is not an
-iterator. Well it is right! It is an iterable but not an iterator. This
-means that it supports iteration but we can not directly iterate over
-it. How can we then iterate over it? It's time to learn about one more
+iterator. Well it's right! It's an iterable but not an iterator. This
+means that it supports iteration but we can't iterate over
+it directly. So how would we iterate over it? It's time to learn about one more
built-in function, ``iter``. It returns an ``iterator`` object from an
-iterable. Here is how we can use it:
+iterable. While an ``int`` isn't an iterable, we can use it on string!
.. code:: python
+ int_var = 1779
+ iter(int_var)
+ # Output: Traceback (most recent call last):
+ # File "", line 1, in
+ # TypeError: 'int' object is not iterable
+ # This is because int is not iterable
+
my_string = "Yasoob"
my_iter = iter(my_string)
- next(my_iter)
+ print(next(my_iter))
# Output: 'Y'
Now that is much better. I am sure that you loved learning about
diff --git a/global_&_return.rst b/global_&_return.rst
index e1b1f22..0661752 100644
--- a/global_&_return.rst
+++ b/global_&_return.rst
@@ -1,9 +1,9 @@
-Global & Return
+Global and Return
---------------
-You might have encountered some functions written in python which have a
+You might have encountered some functions written in Python which have a
``return`` keyword in the end of the function. Do you know what it does? It
-is similar to return in other languages. Lets examine this little
+is similar to return in other languages. Let's examine this little
function:
.. code:: python
@@ -15,7 +15,7 @@ function:
print(result)
# Output: 8
-The function above takes two values as input and then output their
+The function above takes two values as input and then outputs their
addition. We could have also done:
.. code:: python
@@ -28,15 +28,15 @@ addition. We could have also done:
print(result)
# Output: 8
-So first lets talk about the first bit of code which involves the
+So first let's talk about the first bit of code which involves the
``return`` keyword. What that function is doing is that it is assigning
the value to the variable which is calling that function which in our
-case is ``result``. In most cases and you won't need to use the
-``global`` keyword. However lets examine the other bit of code as well
+case is ``result``. In most cases you won't need to use the
+``global`` keyword. However, let's examine the other bit of code as well
which includes the ``global`` keyword. So what that function is doing is
that it is making a global variable ``result``. What does global mean
here? Global variable means that we can access that variable outside the
-scope of the function as well. Let me demonstrate it with an example :
+scope of the function as well. Let me demonstrate it with an example:
.. code:: python
@@ -47,7 +47,7 @@ scope of the function as well. Let me demonstrate it with an example :
add(2, 4)
print(result)
- # Oh crap we encountered an exception. Why is it so ?
+ # Oh crap, we encountered an exception. Why is it so?
# the python interpreter is telling us that we do not
# have any variable with the name of result. It is so
# because the result variable is only accessible inside
@@ -119,4 +119,42 @@ Or by more common convention:
age = 30
return name, age
-This is a better way to do it along with returning ``lists`` and ``dicts``. Don't use ``global`` keyword unless you know what you are doing. ``global`` might be a better option in a few cases but is not in most of them.
+ profile_name, profile_age = profile()
+ print(profile_name)
+ # Output: Danny
+ print(profile_age)
+ # Output: 30
+
+Keep in mind that even in the above example we are returning a tuple (despite the lack of parenthesis) and not separate multiple values. If you want to take it one step further, you can also make use of `namedtuple `_. Here is an example:
+
+.. code:: python
+
+ from collections import namedtuple
+ def profile():
+ Person = namedtuple('Person', 'name age')
+ return Person(name="Danny", age=31)
+
+ # Use as namedtuple
+ p = profile()
+ print(p, type(p))
+ # Person(name='Danny', age=31)
+ print(p.name)
+ # Danny
+ print(p.age)
+ #31
+
+ # Use as plain tuple
+ p = profile()
+ print(p[0])
+ # Danny
+ print(p[1])
+ #31
+
+ # Unpack it immediatly
+ name, age = profile()
+ print(name)
+ # Danny
+ print(age)
+ #31
+
+This is a better way to do it, along with returning ``list`` and ``dict``. Don't use ``global`` keyword unless you know what you are doing. ``global`` might be a better option in a few cases but is not in most of them.
diff --git a/index.rst b/index.rst
index 9895a2d..b45de64 100644
--- a/index.rst
+++ b/index.rst
@@ -2,9 +2,7 @@
.. note::
- You can donate me for my hardwork if you want to by buying the donation version of Intermediate Python from `Gumroad `__. Your help would be greatly appreciated!
-
- You can also sign up to my `mailing list `__ so that you remain in sync with any major updates to this book or my future projects!
+ You can sign up to my `mailing list `__ so that you remain in sync with any major updates to this book or my future projects!
Intermediate Python
===================
@@ -52,9 +50,11 @@ Table of Contents
virtual_environment
collections
enumerate
+ zip
object_introspection
comprehensions
exceptions
+ classes
lambdas
one_liners
for_-_else
diff --git a/lambdas.rst b/lambdas.rst
index 1223dca..756e260 100644
--- a/lambdas.rst
+++ b/lambdas.rst
@@ -21,7 +21,7 @@ normal functions and even behave like them.
print(add(3, 5))
# Output: 8
-Here are a few useful use cases for lambdas and just a few way in which
+Here are a few useful use cases for lambdas and just a few ways in which
they are used in the wild:
**List sorting**
@@ -39,5 +39,5 @@ they are used in the wild:
.. code:: python
data = zip(list1, list2)
- data.sort()
+ data = sorted(data)
list1, list2 = map(lambda t: list(t), zip(*data))
diff --git a/map_filter.rst b/map_filter.rst
index 0bc4187..9552cf8 100644
--- a/map_filter.rst
+++ b/map_filter.rst
@@ -1,7 +1,7 @@
-Map & Filter
+Map, Filter and Reduce
------------
-These are two functions which facilitate a functional approach to
+These are three functions which facilitate a functional approach to
programming. We will discuss them one by one and understand their use
cases.
@@ -41,9 +41,9 @@ of a list of inputs we can even have a list of functions!
.. code:: python
def multiply(x):
- return (x*x)
+ return (x*x)
def add(x):
- return (x+x)
+ return (x+x)
funcs = [multiply, add]
for i in range(5):
@@ -60,7 +60,7 @@ of a list of inputs we can even have a list of functions!
Filter
^^^^^^^^^
-As the name suggests, filter creates a list of elements for which a
+As the name suggests, ``filter`` creates a list of elements for which a
function returns true. Here is a short and concise example:
.. code:: python
@@ -75,3 +75,33 @@ The filter resembles a for loop but it is a builtin function and faster.
**Note:** If map & filter do not appear beautiful to you then you can
read about ``list/dict/tuple`` comprehensions.
+
+Reduce
+^^^^^^^^^
+
+``Reduce`` is a really useful function for performing some computation on
+a list and returning the result. It applies a rolling computation to sequential
+pairs of values in a list. For example, if you wanted to compute the product
+of a list of integers.
+
+So the normal way you might go about doing this task in python is using
+a basic for loop:
+
+.. code:: python
+
+ product = 1
+ list = [1, 2, 3, 4]
+ for num in list:
+ product = product * num
+
+ # product = 24
+
+
+Now let's try it with reduce:
+
+.. code:: python
+
+ from functools import reduce
+ product = reduce((lambda x, y: x * y), [1, 2, 3, 4])
+
+ # Output: 24
diff --git a/mutation.rst b/mutation.rst
index 440222a..493579c 100644
--- a/mutation.rst
+++ b/mutation.rst
@@ -30,7 +30,8 @@ something like this:
bar += ['bye']
print(foo)
- # Output: ['hi']
+ # Expected Output: ['hi']
+ # Output: ['hi', 'bye']
print(bar)
# Output: ['hi', 'bye']
diff --git a/one_liners.rst b/one_liners.rst
index e384bcb..17dc776 100644
--- a/one_liners.rst
+++ b/one_liners.rst
@@ -29,9 +29,59 @@ repl. Here is the relevant code:
from pprint import pprint
my_dict = {'name': 'Yasoob', 'age': 'undefined', 'personality': 'awesome'}
- pprint(my_dict)
-
-This is more effective on ``dict``s. Moreover, if you want to pretty print
+ print(dir(my_dict))
+ # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
+
+ pprint(dir(my_dict))
+ # ['__add__',
+ # '__class__',
+ # '__contains__',
+ # '__delattr__',
+ # '__delitem__',
+ # '__dir__',
+ # '__doc__',
+ # '__eq__',
+ # '__format__',
+ # '__ge__',
+ # '__getattribute__',
+ # '__getitem__',
+ # '__gt__',
+ # '__hash__',
+ # '__iadd__',
+ # '__imul__',
+ # '__init__',
+ # '__init_subclass__',
+ # '__iter__',
+ # '__le__',
+ # '__len__',
+ # '__lt__',
+ # '__mul__',
+ # '__ne__',
+ # '__new__',
+ # '__reduce__',
+ # '__reduce_ex__',
+ # '__repr__',
+ # '__reversed__',
+ # '__rmul__',
+ # '__setattr__',
+ # '__setitem__',
+ # '__sizeof__',
+ # '__str__',
+ # '__subclasshook__',
+ # 'append',
+ # 'clear',
+ # 'copy',
+ # 'count',
+ # 'extend',
+ # 'index',
+ # 'insert',
+ # 'pop',
+ # 'remove',
+ # 'reverse',
+ # 'sort']
+
+
+This is more effective on nested ``dict`` s. Moreover, if you want to pretty print
json quickly from a file then you can simply do:
.. code:: python
diff --git a/open_function.rst b/open_function.rst
index f197fc2..3683bb3 100644
--- a/open_function.rst
+++ b/open_function.rst
@@ -1,4 +1,4 @@
-Open function
+``open`` Function
-------------
`open `__ opens
@@ -17,10 +17,10 @@ you spot them all? If not, read on. By the end of this article, you'll
know what's wrong in the above code, and, more importantly, be able to
avoid these mistakes in your own code. Let's start with the basics:
-The return of open is a file handle, given out from the operating system
+The return value from ``open`` is a file handle, given out from the operating system
to your Python application. You will want to return this file handle
once you're finished with the file, if only so that your application
-won't reach the limit of the number of open file handle it can have at
+won't reach the limit of the number of open file handles it can have at
once.
Explicitly calling ``close`` closes the file handle, but only if the
@@ -53,11 +53,11 @@ or text mode (a string of characters).
In general, if the format is written by humans, it tends to be text
mode. ``jpg`` image files are not generally written by humans (and are
-indeed not readable to humans), and you should therefore open them in
-binary mode by adding a ``b`` to the text string (if you're following
+indeed not readable by humans), and you should therefore open them in
+binary mode by adding a ``b`` to the mode string (if you're following
the opening example, the correct mode would be ``rb``). If you open
something in text mode (i.e. add a ``t``, or nothing apart from
-``r/r+/w/a``), you must also know which encoding to use - for a
+``r/r+/w/a``), you must also know which encoding to use. For a
computer, all files are just bytes, not characters.
Unfortunately, ``open`` does not allow explicit encoding specification
@@ -65,16 +65,16 @@ in Python 2.x. However, the function
`io.open `__ is
available in both Python 2.x and 3.x (where it is an alias of ``open``),
and does the right thing. You can pass in the encoding with the
-``encoding`` keyword. If you don't pass in any encoding, a system- (and
-Python-) specific default will be picked. You may be tempted to rely on
+``encoding`` keyword. If you don't pass in any encoding, a system -- and
+Python -- specific default will be picked. You may be tempted to rely on
these defaults, but the defaults are often wrong, or the default
-encoding cannot actually express all characters (this will happen on
-Python 2.x and/or Windows). So go ahead and pick an encoding. ``utf-8``
-is a terrific one. When you write a file, you can just pick the encoding
+encoding cannot actually express all characters in the file (this will happen often on
+Python 2.x and/or Windows). So go ahead and pick an encoding. Encoding is the way to instruct computers about how the numbers should be stored as bytes in memory. ``utf-8``
+is a terrific one and is supported by major browsers and programming languages. When you write a file, you can just pick the encoding
to your liking (or the liking of the program that will eventually read
your file).
-How do you find out which encoding a file you read has? Well,
+How do you find out which encoding a file you're reading was written in? Well,
unfortunately, there is no foolproof way to detect the encoding - the
same bytes can represent different, but equally valid characters in
different encodings. Therefore, you must rely on metadata (for example,
@@ -93,11 +93,11 @@ determines whether it's JPG (hint: These files start with the bytes
jpgdata = inf.read()
if jpgdata.startswith(b'\xff\xd8'):
- text = u'This is a jpeg file (%d bytes long)\n'
+ text = u'This is a JPEG file (%d bytes long)\n'
else:
text = u'This is a random file (%d bytes long)\n'
with io.open('summary.txt', 'w', encoding='utf-8') as outf:
outf.write(text % len(jpgdata))
-I am sure that now you would use ``open`` correctly!
+I am sure that now you will use ``open`` correctly!
diff --git a/python_c_extension.rst b/python_c_extension.rst
index f54ba73..f4cf5d4 100644
--- a/python_c_extension.rst
+++ b/python_c_extension.rst
@@ -6,7 +6,7 @@ implementation is the ease of interfacing C code to Python.
There are three key methods developers use to call C functions from
their python code - ``ctypes``, ``SWIG`` and ``Python/C API``. Each
-method comes with it's own merits and demerits.
+method comes with its own merits and demerits.
Firstly, why would you want to interface C with Python?
@@ -22,7 +22,7 @@ CTypes
The Python `ctypes
module `__ is probably
-the most easiest way to call C functions from Python. The ctypes module
+the easiest way to call C functions from Python. The ctypes module
provides C compatible data types and functions to load DLLs so that
calls can be made to C shared libraries without having to modify them.
The fact that the C side needn't be touched adds to the simplicity of
@@ -36,8 +36,6 @@ Simple C code to add two numbers, save it as ``add.c``
//sample C file to add 2 numbers - int and floats
- #include
-
int add_int(int, int);
float add_float(float, float);
@@ -50,7 +48,7 @@ Simple C code to add two numbers, save it as ``add.c``
}
Next compile the C file to a ``.so`` file (DLL in windows) This will
-generate a adder.so file.
+generate an adder.so file.
.. code:: bash
@@ -93,7 +91,7 @@ functions, one to add two integers and another to add two floats.
In the python file, first the ctypes module is imported. Then the CDLL
function of the ctypes module is used to load the shared lib file we
-created. The functions defined in the C lib is now available to us via
+created. The functions defined in the C lib are now available to us via
the ``adder`` variable. When ``adder.add_int()`` is called, internally a
call is made to the ``add_int`` C function. The ctypes interface allows
us to use native python integers and strings by default while calling
@@ -194,7 +192,7 @@ Python/C API
---------------
The `C/Python API `__ is probably the
-most widely used method - not for it's simplicity but for the fact that
+most widely used method - not for its simplicity but for the fact that
you can manipulate python objects in your C code.
This method requires your C code to be specifically written for
@@ -229,7 +227,7 @@ the addList module is not written in Python at all, but rather in C.
Next we'll have a look at the C code that get's built into the
``addList`` Python module. This may seem a bit daunting at first, but
once you understand the various components that go into writing the C
-file, it's pretty straight forward.
+file, it's pretty straightforward.
*adder.c*
@@ -252,7 +250,7 @@ file, it's pretty straight forward.
long length = PyList_Size(listObj);
//iterate over all the elements
- int i, sum =0;
+ long i, sum =0;
for(i = 0; i < length; i++){
//get an element out of the list - the element is also a python objects
PyObject* temp = PyList_GetItem(listObj, i);
@@ -288,17 +286,12 @@ file, it's pretty straight forward.
"Add all ze lists");
}
-A step by step explanation - \* The ```` file consists of all
-the required types (to represent Python object types) and function
-definitions (to operate on the python objects). \* Next we write the
-function which we plan to call from python. Conventionally the function
-names are {module-name}\_{function-name}, which in this case is
-``addList_add``. More about the function later. \* Then fill in the info
-table - which contains all the relevant info of the functions we desire
-to have in the module. Every row corresponds to a function, with the
-last one being a sentinel value (row of null elements). \* Finally the
-module initialization block which is of the signature
-``PyMODINIT_FUNC init{module-name}``.
+A step by step explanation :
+
+- The ```` file consists of all the required types (to represent Python object types) and function definitions (to operate on the python objects).
+- Next we write the function which we plan to call from python. Conventionally the function names are {module-name}\_{function-name}, which in this case is ``addList_add``. More about the function later.
+- Then fill in the info table - which contains all the relevant info of the functions we desire to have in the module. Every row corresponds to a function, with the last one being a sentinel value (row of null elements).
+- Finally the module initialization block which is of the signature ``PyMODINIT_FUNC init{module-name}``.
The function ``addList_add`` accepts arguments as a PyObject type struct
(args is also a tuple type - but since everything in python is an
@@ -321,7 +314,7 @@ and a python list in that order, the function signature would be
int n;
char *s;
PyObject* list;
- PyArg_ParseTuple(args, "siO", &n, &s, &list);
+ PyArg_ParseTuple(args, "siO", &s, &n, &list);
In this case we only have to extract a list object, and store it in the
variable ``listObj``. We then use the ``PyList_Size()`` function on our
diff --git a/targeting_python_2_3.rst b/targeting_python_2_3.rst
index 40f9bb1..caa0c1e 100644
--- a/targeting_python_2_3.rst
+++ b/targeting_python_2_3.rst
@@ -4,8 +4,8 @@ Targeting Python 2+3
In a lot of cases you might want to develop programs which can be run in
both Python 2+ and 3+.
-Just imagine that you have a very popular Python module which is use by
-hundreds of people but not all of them have Python 2 or 3. In that case
+Just imagine that you have a very popular Python module which is used by
+hundreds of people but not all of them have the same version of Python (2 or 3). In that case
you have two choices. The first one is to distribute 2 modules, one for
Python 2 and the other for Python 3. The other choice is to modify your
current code and make it compatible with both Python 2 and 3.
diff --git a/ternary_operators.rst b/ternary_operators.rst
index d4a4e00..6f9cbbc 100644
--- a/ternary_operators.rst
+++ b/ternary_operators.rst
@@ -12,14 +12,14 @@ expressions.
.. code:: python
- condition_is_true if condition else condition_is_false
+ value_if_true if condition else value_if_false
**Example:**
.. code:: python
- is_fat = True
- state = "fat" if is_fat else "not fat"
+ is_nice = True
+ state = "nice" if is_nice else "not nice"
It allows to quickly test a condition instead of a multiline if
statement. Often times it can be immensely helpful and can make your
@@ -38,10 +38,10 @@ is some sample code:
.. code:: python
- fat = True
- fitness = ("skinny", "fat")[fat]
- print("Ali is ", fitness)
- # Output: Ali is fat
+ nice = True
+ personality = ("mean", "nice")[nice]
+ print("The cat is ", personality)
+ # Output: The cat is nice
This works simply because True == 1 and False == 0, and so can be done
with lists in addition to tuples.
@@ -70,3 +70,44 @@ first built, then an index is found. For the if-else ternary operator,
it follows the normal if-else logic tree. Thus, if one case could
raise an exception based on the condition, or if either case is a
computation-heavy method, using tuples is best avoided.
+
+
+**ShortHand Ternary**
+
+In python there is also the shorthand ternary tag which is a shorter version of the
+normal ternary operator you have seen above.
+
+Syntax was introduced in Python 2.5 and can be used in python 2.5 or greater.
+
+**Example**
+
+.. code:: python
+
+ >>> True or "Some"
+ True
+ >>>
+ >>> False or "Some"
+ 'Some'
+
+The first statement (`True or "Some"`) will return `True` and the second statement (`False or "Some"`) will return `Some`.
+
+This is helpful in case where you quickly want to check for the output of a function and give a useful message if the output is empty:
+
+.. code:: python
+
+ >>> output = None
+ >>> msg = output or "No data returned"
+ >>> print(msg)
+ No data returned
+
+Or as a simple way to define function parameters with dynamic default values:
+
+.. code:: python
+
+ >>> def my_function(real_name, optional_display_name=None):
+ >>> optional_display_name = optional_display_name or real_name
+ >>> print(optional_display_name)
+ >>> my_function("John")
+ John
+ >>> my_function("Mike", "anonymous123")
+ anonymous123
diff --git a/virtual_environment.rst b/virtual_environment.rst
index 58746ce..28b119f 100644
--- a/virtual_environment.rst
+++ b/virtual_environment.rst
@@ -32,7 +32,7 @@ To install it, just type this command in the shell:
The most important commands are:
- ``$ virtualenv myproject``
-- ``$ source bin/activate``
+- ``$ source myproject/bin/activate``
This first one makes an isolated virtualenv environment in the
``myproject`` folder and the second command activates that isolated
diff --git a/zip.rst b/zip.rst
new file mode 100644
index 0000000..947fe2a
--- /dev/null
+++ b/zip.rst
@@ -0,0 +1,71 @@
+Zip and unzip
+-------------
+
+**Zip**
+
+Zip is a useful function that allows you to combine two lists easily.
+
+After calling zip, an iterator is returned. In order to see the content wrapped inside, we need to first convert it to a list.
+
+Example:
+
+.. code:: python
+
+ first_name = ['Joe','Earnst','Thomas','Martin','Charles']
+
+ last_name = ['Schmoe','Ehlmann','Fischer','Walter','Rogan','Green']
+
+ age = [23, 65, 11, 36, 83]
+
+ print(list(zip(first_name,last_name, age)))
+
+ # Output
+ #
+ # [('Joe', 'Schmoe', 23), ('Earnst', 'Ehlmann', 65), ('Thomas', 'Fischer', 11), ('Martin', 'Walter', 36), ('Charles', 'Rogan', 83)]
+
+One advantage of zip is that it improves readability of for loops.
+
+For example, instead of needing multiple inputs, you only need one zipped list for the following for loop:
+
+.. code:: python
+
+ first_name = ['Joe','Earnst','Thomas','Martin','Charles']
+ last_name = ['Schmoe','Ehlmann','Fischer','Walter','Rogan','Green']
+ age = [23, 65, 11, 36, 83]
+
+ for first_name, last_name, age in zip(first_name, last_name, age):
+ print(f"{first_name} {last_name} is {age} years old")
+
+ # Output
+ #
+ # Joe Schmoe is 23 years old
+ # Earnst Ehlmann is 65 years old
+ # Thomas Fischer is 11 years old
+ # Martin Walter is 36 years old
+ # Charles Rogan is 83 years old
+
+**Unzip**
+
+We can use the `zip` function to unzip a list as well. This time, we need an input of a list with an asterisk before it.
+
+The outputs are the separated lists.
+
+Example:
+
+.. code:: python
+
+ full_name_list = [('Joe', 'Schmoe', 23),
+ ('Earnst', 'Ehlmann', 65),
+ ('Thomas', 'Fischer', 11),
+ ('Martin', 'Walter', 36),
+ ('Charles', 'Rogan', 83)]
+
+ first_name, last_name, age = list(zip(*full_name_list))
+ print(f"first name: {first_name}\nlast name: {last_name} \nage: {age}")
+
+ # Output
+
+ # first name: ('Joe', 'Earnst', 'Thomas', 'Martin', 'Charles')
+ # last name: ('Schmoe', 'Ehlmann', 'Fischer', 'Walter', 'Rogan')
+ # age: (23, 65, 11, 36, 83)
+