I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types Part - 2/5.2 Advance Data Types.ipynb".
The first task is:
Default dict
The standard dictionary includes the method setdefault() for retrieving a value and establishing a default if the value does not exist. By contrast, defaultdict lets the caller specify the default up front when the container is initialized.
Can you write Python code for it?

d = {"a": 1, "b": 2}
print(d)
print(d['a'])
print(d['d'])

from collections import defaultdict

dd  = defaultdict(object)
print(dd)
print(dd['one'])
print(dd)
dd['Two'] = 2
print(dd)
for d in dd:
    print(d)
    print(dd[d])

help(defaultdict)

# Initializing with default value

dd = defaultdict(1)
print(dd)
print(dd['one'])
print(dd)
dd['Two'] = 2
print(dd)

for d in dd:
    print(d)
    print(dd[d])

# Using factory function
import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, india='new delhi')
print('d:', d)
print('india =>', d['india'])
print('bar =>', d['bar'])
print(d)

# Using factory function
import collections

def default_factory():
    return 'Bhopal'

d = collections.defaultdict(default_factory,
                            {"india": 'new delhi',
                             "karnataka":"Bangaluru"})
print('d:', d)
print('india =>', d['india'])
print('MP =>', d['MP'])
print(d)

# Using factory function

# ---------------------------------------------------
# TODO:  How can i pass value to the default function
# ---------------------------------------------------
import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, foo='bar')
print('d:', d)
print('foo =>', d['foo'])
print('bar =>', d['bar'])

# Using list as the default_factory, it is easy to group a sequence of key-value pairs into a dictionary of lists:

from collections import defaultdict

countryList = [("India", "New Delhi"), ("Iceland", "Reykjavik"),
               ("Indonesia", "Jakarta"), ("Ireland", "Dublin"),
               ("Israel", "Jerusalem"), ("Italy", "Rome")]
d = defaultdict(list)
for country, capital in countryList:
    d[country].append(capital)

print(d.items())

# Setting the default_factory to int makes the defaultdict useful for counting
quote = 'Vande Mataram'
dd = defaultdict(int)
print(dd)
for chars in quote:
    dd[chars] += 1

print(dd.items())
print(dd['T'])


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types Part - 2/5.2 Advance Data Types.ipynb".
The first task is:
For situations where the new context is known or built in advance, it is also possible to pass a mapping to new_child().
Can you write Python code for it?

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}
c = {'c': '333'}

m1 = collections.ChainMap(a, b)
m2 = m1.new_child(c)

print('m1["c"] = {}'.format(m1['c']))
print('m2["c"] = {}'.format(m2['c']))
print(m2)

#This is the equivalent of
m2_1 = collections.ChainMap(c, *m1.maps)
print(m2_1)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types Part - 2/5.2 Advance Data Types.ipynb".
The first task is:
The child mappings are searched in the order they are passed to the constructor, so the value reported for the key 'c' comes from the a dictionary.
Reordering
The ChainMap stores the list of mappings over which it searches in a list in its maps attribute. This list is mutable, so it is possible to add new mappings directly or to change the order of the elements to control lookup and update behavior.
Can you write Python code for it?

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

cm = collections.ChainMap(a, b)

print(cm.maps)
print('c = {}\n'.format(cm['c']))
# reverse the list
cm.maps = list(reversed(cm.maps)) # m = collections.ChainMap(b, a)

print(cm.maps)
print('c = {}'.format(cm['c']))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types Part - 2/5.2 Advance Data Types.ipynb".
The first task is:
When the list of mappings is reversed, the value associated with 'c' changes.
Updating Values
A ChainMap does not cache the values in the child mappings. Thus, if their contents are modified, the results are reflected when the ChainMap is accessed.
Can you write Python code for it?

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

m = collections.ChainMap(a, b)
print('Before: {}'.format(m['c']))
a['c'] = '3.3'
print('After : {}'.format(m['c']))

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

cm = collections.ChainMap(b, a)
print(cm.maps)
print('Before: {}'.format(cm['c']))
a['c'] = '3.3'
print('After : {}'.format(cm['c']))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types Part - 2/5.2 Advance Data Types.ipynb".
The first task is:
deque — Double-Ended Queue

A double-ended queue, or deque, supports adding and removing elements from either end of the queue. The more commonly used stacks and queues are degenerate forms of deques, where the inputs and outputs are restricted to a single end.
Can you write Python code for it?

import collections

d = collections.deque('Vande Mataram')
print('Deque:', d)
print('Length:', len(d))
print('Left end:', d[0])
print('Right end:', d[-1])

d.remove('e')
print('remove(e):', d)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.0.1 Answers - Data_Type.ipynb".
The first task is:
Q: Write a Python program to iterate over dictionaries using for loops.
ans: please look above examples
Q: Write a Python script to generate and print a dictionary that contains number (between 1 and n) in the form (x, x*x).
Sample Dictionary ( n = 5) : Expected Output : {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
and
Write a Python script to print a dictionary where the keys are numbers between 1 and 15 (both included) and the values are square of keys. Sample Dictionary {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100, 11: 121, 12: 144, 13: 169, 14: 196, 15: 225}
Can you write Python code for it?

a = { b : b*b for b in range(1,10) }
print(a)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.0.1 Answers - Data_Type.ipynb".
The first task is:
Q: Write a Python script to add key to a dictionary.
e.g. Sample Dictionary : {0: 10, 1: 20} Expected Result : {0: 10, 1: 20, 2: 30}
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.0.1 Answers - Data_Type.ipynb".
The first task is:
Q: Write a Python script to concatenate following dictionaries to create a new one.
e.g:
Sample Dictionary : dic1={1:10, 2:20} dic2={3:30, 4:40} dic3={5:50,6:60}
Expected Result : {1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60}
Can you write Python code for it?

names1={1:10, 2:20}
names2={3:30, 4:40}
names3={5:50,6:60}
# names1.update(names2)
new_dict = {}
for ls in (names1, names2, names3):
    new_dict.update(ls)
print(new_dict)

d1={1:2,3:4}
d2={5:6,7:9}
d3={10:8,13:22}
d4 = dict(d1)
d4.update(d2)
d4.update(d3)
print(d4)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.0.1 Answers - Data_Type.ipynb".
The first task is:
Q: Write a Python script to merge two Python dictionaries.

TIP: use update

Q: Write a Python program to sum all the items in a dictionary.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.0.1 Answers - Data_Type.ipynb".
The first task is:
Q: Write a Python script to sort (ascending and descending) a dictionary by value.
Can you write Python code for it?

# Ans
e = {1:39,8:110, 4:34, 3:87, 7:110, 2:87}
sortE = sorted(e.items(), key=lambda value: value[1])
print(sortE)

# Ans
e = {1:39,8:110, 4:34, 3:87, 7:110, 2:87}
sortE = sorted(e.items(), key=lambda value: value[1], reverse=True)
print(sortE)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.1 Uses of collection and datatype.ipynb".
The first task is:
Clone Or Copy A List in Python
here are a lot of ways of cloning or copying a list:
You can slice your original list and store it into a new variable: newList = oldList[:]
You can use the built-in list() function: newList = list(oldList)
You can use the copy library:
    With the copy() method: newList = copy.copy(oldList)
    If your list contains objects and you want to copy those as well, you can use copy.deepcopy(): copy.deepcopy(oldList)
Can you write Python code for it?

# Copy the grocery list by slicing and store it in the `newGroceries` variable
groceries = [1, 2, 3, 4, 5, 6]
newGroceries = groceries[:]
# Copy the grocery list with the `list()` function and store it in a `groceriesForFriends` variable
groceriesForFriends = list(groceries)
# Import the copy library
import copy as c
# Create a `groceriesForFamily` variable and assign the copied grocery list to it
groceriesForFamily = c.copy(groceries)
# Use `deepcopy()` and assign the copied list to a `groceriesForKids` variable
groceriesForKids = c.deepcopy(groceries)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.1 Uses of collection and datatype.ipynb".
The first task is:
Convert A List To A Tuple
You can change a list to a tuple in Python by using the tuple() function. Pass your list to this function and you will get a tuple back!
NOTE: tuples are immutable thus can’t change them afterwards :(
Convert Your List To A Set In Python
a set is an unordered collection of unique items. That means not only means that any duplicates that you might have had in your original list will be lost once you convert it to a set, but also the order of the list elements.
You can change a list into a set with the set() function. Just pass your list to it!
Convert Lists To A Dictionaries
A dictionary works with keys and values, so the conversion from a list to a dictionary might be less straightforward.
helloWorld = ['hello','world','1','2']
You will need to make sure that hello and world and ‘1’ and ‘2’ are interpreted as key-value pairs. The way to do this is to select them with the slice notation and pass them to zip().
zip() actually works like expected: it zips elements together. In this case, when you zip the helloWorld elements helloWorld[0::2] and helloWorld[1::2]
Can you write Python code for it?

helloWorld = ['hello','world','1','2']
# print(list(zip(helloWorld)))

helloWorldDictionary = dict(zip(helloWorld[0::2], helloWorld[1::2]))

# Print out the result
print(helloWorldDictionary)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.1 Uses of collection and datatype.ipynb".
The first task is:
Note that the second element that is passed to the zip() function makes use of the step value to make sure that only the world and 2 elements are selected. Likewise, the first element uses the step value to select hello and 1.
If your list is large, you will probably want to do something like this:
Can you write Python code for it?

a = [1, 2, 3, 4, 5]

# Create a list iterator object
i = iter(a)
for k in i:
    print(k)

# Zip and create a dictionary
print(dict(zip(i, i)))

## Difference Between The Python append() and extend() Methods?
# Append [4,5] to `shortList`
# This is your list
shortList  = [1, 2, 3]
longerList = [1, 2, 3]
# Check whether it's iterable
list.__iter__

shortList.append([4, 5])

# Use the `print()` method to show `shortList`
print(shortList)

# Extend `longerList` with [4,5]
longerList.extend([4, 5])

# Use the `print()` method to see `longerList`
print(longerList)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Adding
Can you write Python code for it?

import collections

# Add to the right
d1 = collections.deque()
d1.extend('Vande')
print('extend    :', d1)

for a in " Mataram":
    d1.append(a)

d1.extend(" !!!")
print('append    :', d1)
d1.extendleft(" #!* ")
print('append    :', d1)

# Add to the left
d2 = collections.deque()
d2.extendleft(range(6))
print('extendleft:', d2)
d2.appendleft(6)
print('appendleft:', d2)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Changing the values associated with existing keys and adding new elements works the same way.
It is also possible to set values through the ChainMap directly, although only the first mapping in the chain is actually modified.
Can you write Python code for it?

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

cm = collections.ChainMap(a, b)
print('Before: {}'.format(cm['c']))
cm['c'] = '3.3'
print('After : {}'.format(cm['c']))
print(a['c'])
print(b['c'])

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

cm = collections.ChainMap(b, a)
print('Before: {}'.format(cm['c']))
cm['c'] = '3.3'
print('After : {}'.format(cm['c']))
print(a['c'])
print(b['c'])

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

cm = collections.ChainMap(a, b)
print('Before: {}'.format(cm['c']))
cm['d'] = '3.3'
print('After : {}'.format(cm['c']))
print(cm.maps)
print(a)
print(b)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Consuming
OrderedDict
It is a dictionary subclass that remembers the order in which its contents are added.
Lets start with a normal dictionary:
Can you write Python code for it?

fruitsCount = {}
fruitsCount["apple"] = 10
fruitsCount["grapes"] = 120
fruitsCount["mango"] = 200
fruitsCount["kiwi"] = 2000
fruitsCount["leeche"] = 20
print(fruitsCount)
for fruit in fruitsCount:
    print(fruit)

# Now lets try this with OrderedDict

from collections import OrderedDict as OD

fruitsCount = OD()
fruitsCount["apple"] = 10
fruitsCount["grapes"] = 120
fruitsCount["mango"] = 200
fruitsCount["kiwi"] = 2000
fruitsCount["leeche"] = 20
print(fruitsCount)
for fruit in fruitsCount:
    print(fruit)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Counter
Counter is a dict subclass which helps count the hashable objects. It stores elements as dictionary keys and the counts of the objects as value. In other words , It is a container that keeps track of how many times equivalent values are present.
For example:
Can you write Python code for it?

# Tally occurrences of words in a list
from collections import Counter

cnt = Counter()
for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
    cnt[word] += 1

Counter({'blue': 3, 'red': 2, 'green': 1})

# Find the ten most common words in Hamlet
import re
words = re.findall(r'\w+', open('hamlet.txt').read().lower())
Counter(words).most_common(10)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Counter with Strings
Can you write Python code for it?

sentance = "The collections module is a tresure trove of a built-in module that implements " + \
           "specialized container datatypes providing alternatives to Python’s general purpose " + \
           "built-in containers."

wordList = sentance.split(" ")
Counter(wordList).most_common(3)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Default dict
The standard dictionary includes the method setdefault() for retrieving a value and establishing a default if the value does not exist. By contrast, defaultdict lets the caller specify the default up front when the container is initialized.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
For situations where the new context is known or built in advance, it is also possible to pass a mapping to new_child().
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
The child mappings are searched in the order they are passed to the constructor, so the value reported for the key 'c' comes from the a dictionary.
Reordering
The ChainMap stores the list of mappings over which it searches in a list in its maps attribute. This list is mutable, so it is possible to add new mappings directly or to change the order of the elements to control lookup and update behavior.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
When the list of mappings is reversed, the value associated with 'c' changes.
Updating Values
A ChainMap does not cache the values in the child mappings. Thus, if their contents are modified, the results are reflected when the ChainMap is accessed.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
When the new value is stored using m, the a mapping is updated.
ChainMap provides a convenience method for creating a new instance with one extra mapping at the front of the maps list to make it easy to avoid modifying the existing underlying data structures.
This stacking behavior is what makes it convenient to use ChainMap instances as template or application contexts. Specifically, it is easy to add or update values in one iteration, then discard the changes for the next iteration.
Can you write Python code for it?

import collections

a = {'a': '1', 'c': '3'}
b = {'b': '2', 'c': '33'}

m1 = collections.ChainMap(a, b)
m2 = m1.new_child()

print('m1 before:', m1)
print('m2 before:', m2)

m2['c'] = '3.3'

print('m1 after:', m1)
print('m2 after:', m2)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
Where as Counter can be used:
Counter() with lists
Can you write Python code for it?

l = [1 ,23 , 23, 44, 4, 44, 55, 555, 44, 32, 23, 44, 56, 64, 2, 1]

lstCounter = Counter(l)
print(lstCounter)
print(lstCounter.most_common(4))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
deque — Double-Ended Queue

A double-ended queue, or deque, supports adding and removing elements from either end of the queue. The more commonly used stacks and queues are degenerate forms of deques, where the inputs and outputs are restricted to a single end.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/5.2 Advance Data Types.ipynb".
The first task is:
namedtuple
Named tuples helps to have meaning of each position in a tuple and allow us to code with better readability and self-documenting code. You can use them in any place where you are using tuples. In the example we will create a namedtuple to show hold information for points.
Can you write Python code for it?

from collections import namedtuple

Point = namedtuple("India", ['x', 'y', "z"])  # Defining the namedtuple
p = Point(10, y=20, z = 30)  # Creating an object
print(p)
print(p.x + p.y + p.z)
p[0] + p[1]  # Accessing the values in normal way
x, y, z  = p     # Unpacking the tuple
print(x)

print(y)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Dictionary.ipynb".
The first task is:
Lets check below two examples and see what is happening
Can you write Python code for it?

d = dict()
d[10.1] = "TEST"
d[10] = "test"
d[10.5] = "really testing"
d[20] = "Testing completed"
print(d)

d = dict()
d[10.0] = "TEST"
d[10] = "test"
d[10.5] = "really testing"
d[20] = "Testing completed"
print(d)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Dictionary.ipynb".
The first task is:
NOTE: Dictionaries are implemented with a hash table and hash of 10.0 and 10 are same, thus  10.0 and 10 keys of dict are same and only one key/value pair is shown for them. Please check the url for details.
- https://stackoverflow.com/questions/32209155/why-can-a-floating-point-dictionary-key-overwrite-an-integer-key-with-the-same-v/32211042#32211042
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Dictionary.ipynb".
The first task is:
NOTE: Last key will override the previous one
Can you write Python code for it?

print(dic.items())

# Progs and their albums
progs = {'Yes': ['Close To The Edge', 'Fragile'],
    'Genesis': ['Foxtrot', 'The Nursery Crime'],
    'ELP': ['Brain Salad Surgery']}

# More progs
progs['King Crimson'] = ['Red', 'Discipline']

# items() returns a list of
# tuples with key and value
for singer, album  in progs.items():
    print(singer, ":=>", album)

for albums in progs.values():
    print(albums)

for prog in progs:
    print(prog, "=>", progs[prog])

# If there is 'ELP', removes
if 'ELP' in progs:
    del progs['ELP']

print(progs)

multid = {'school': 'DMS',
          'students_details': {
              1001: {
               "name": "Mayank",
                  "age": 41
              },
              1002: {
                "name" : "Vishal",
                "age": 42
              },
              1003: {
                  "name": "Rajeev Chaturvedi",
                  "age": 41
              }
          }
        }
print(multid)

print(multid['students_details'][1001])
print(multid['students_details'][1002]['name'])

print(multid['students_details'][1004]['name'])

multid = {'school': 'DMS',
          'students_details': {
              "students":
                  [
                      "Mayank",
                      "Vishal",
                      "Rajeev"
                  ]
          }}
print(multid)

dupli = {
    "meme" : "mjmj",
    "test" : "TESt value",
    "meme" : "wewe"
}

print(dupli)
for k in dupli:
    print(k)

# Matrix in form of string
matrix = '''0 0 0 0 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 4 0 0
0 0 0 0 0 0 0 3 0 0 0 0
0 0 0 0 0 0 5 0 0 0 0 0
0 0 0 0 6 0 0 0 0 0 0 0'''

mat = {}

# split the matrix in lines
for row, line in enumerate(matrix.splitlines()):

    # Splits the line int cols
    for col, column in enumerate(line.split()):

        column = int(column)
        # Places the column in the result,
        # if it is differente from zero
        if column:
            mat[row, col] = column

print (mat)
# The counting starts with...


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Dictionary.ipynb".
The first task is:
NOTE: One can create dictionary using the following methods as well
Can you write Python code for it?

names = dict(mayank="johri", ashwini="johri", Rahul="Johri")
print(names)

names = dict([("mayank","johri"), ("ashwini", "johri"), ("Rahul","Johri")])
print(names)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Lists.ipynb".
The first task is:
Appending
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Lists.ipynb".
The first task is:
Inverting
Can you write Python code for it?

fruits.reverse()
print(fruits)

# # # prints with number order
fruits = ['Apple', 'Mango', 'Grapes', 'Jackfruit',
          'Apple', 'Banana', 'Grapes']
for i, prog in enumerate(fruits):
    print( i + 1, '=>', prog)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Lists.ipynb".
The first task is:
Removing
Can you write Python code for it?

## Removing the second instance of Grapes
x = 0
y = 0
for fruit in fruits:
    if x == 1 and fruit == 'Grapes':
#          del (fruits[y])
        fruits.pop(y)
    elif fruit == 'Grapes':
        x = 1
    y +=1

print(fruits)

fruits.remove('Grapes')


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Lists.ipynb".
The first task is:
The function enumerate() returns a tuple of two elements in each iteration: a sequence number and an item from the corresponding sequence.
The list has a pop() method that helps the implementation of queues and stacks:
Can you write Python code for it?

my_list = ['A', 'B', 'C']
for a, b in enumerate(my_list):
    print(a, b)

my_list = ['A', 'B', 'C']
print ('list:', my_list)

# # The empty list is evaluated as false
while my_list:
    # In queues, the first item is the first to go out
    # pop(0) removes and returns the first item
    print ('Left', my_list.pop(0), ', remain', len(my_list), my_list)

my_list.append("G")
# # More items on the list
my_list += ['D', 'E', 'F']
print ('list:', my_list)

while my_list:
    # On stacks, the first item is the last to go out
    # pop() removes and retorns the last item
    print ('Left', my_list.pop(), ', remain', len(my_list), my_list)

l = ['D', 'E', 'F', "G", "H"]
print(l)
k = ('D', "E", "G", "H")
print(dir(l))
print("*"*8)
print(dir(k))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
1011
"""
OR
0 0 | 0
0 1 | 1
1 0 | 1
1 1 | 1
AND
0 0 | 0
0 1 | 0
1 0 | 0
1 1 | 1
"""
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
Equal To
Can you write Python code for it?

x = 4
y = 4
if(x == y):
    print("X & Y are equal")
else:
    print("X & Y are different")

x = 41
y = 4
if(x == y):
    print("X & Y are equal")
else:
    print("X & Y are different")

x = 2+1j
y = 3+1j
if(x == y):
    print("X & Y are equal")
else:
    print("X & Y are different")

x = 21+1j
y = 21+1j
if(x == y):
    print("X & Y are equal")
else:
    print("X & Y are different")

x = 21+1j
y = 21+1j
if(x == y):
    print("X & Y are equal")
else:
    print("X & Y are different")


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
Less than or equal to (<=)
Can you write Python code for it?

x = 2
y = 4
if(x <= y):
    print("X wins")
else:
    print("Y wins")

x = 2
y = 4
if(x <= y):
    print("X wins")
else:
    print("Y wins")

x = 21
y = 4
if(x <= y):
    print("X wins")
else:
    print("Y wins")

x = 4
y = 4
if(x <= y):
    print("X wins")
else:
    print("Y wins")


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
NOTE: The real numbers can also be represented in scientific notation, for example: 1.2e22.

Arithmetic Operations:
Python has a number of defined operators for handling numbers through arithmetic calculations, logic operations (that test whether a condition is true or false) or bitwise processing (where the numbers are processed in binary form).
Logical Operations:

Less than (<)
Greater than (>)
Less than or equal to (<=)
Greater than or equal to (>=)
Equal to (==)
Not equal to (!=)

Less than (<)
Can you write Python code for it?

x = 22
y = 4
if(x < y):
    print("X wins")
else:
    print("Y wins")

x = 2
y = 4
if(x < y):
    print("X wins")
else:
    print("Y wins")


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
Not Equal To
Can you write Python code for it?

x = 4
y = 4
if(x != y):
    print("X & Y are different")
else:
    print("X & Y are equal")

x = 41
y = 4
if(x != y):
    print("X & Y are different")
else:
    print("X & Y are equal")

x = 2+1j
y = 3+1j
if(x != y):
    print("X & Y are different")
else:
    print("X & Y are equal")

x = 21+1j
y = 21+1j
if(x != y):
    print("X & Y are different")
else:
    print("X & Y are equal")


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/Numbers.ipynb".
The first task is:
greater_than_or_equal_to
Can you write Python code for it?

x = 8
y = 4
if(x >= y):
    print("X wins")
else:
    print("Y wins")

x = 4
y = 14
if(x <= y):
    print("X wins")
else:
    print("Y wins")

x = 4
y = 4
if(x <= y):
    print("X wins")
else:
    print("Y wins")


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
%-formatting
Str.format()
Template Strings
Literal String
It is the new Interpolation method as it is implemented in Python 3.6.
Can you write Python code for it?

# name = 'World'
# program = 'Python'
# print(f'Hello {name}! This is {program}')

# String processed as a sequence
s = "Murthy "
for ch in s: print(ch , end=',') # This
# print(help(print))
print("\b.")
print("~"*79)

# Strings are objects
if s.startswith('M'): print(s.upper())

print(s.lower())
print("~"*79)

# what will happen?
print(3*s)

# print(dir(s))

s = "   Murthy "
age = 5
print(s + str(age))
print(s.strip(), age)
# print(s + age)

st = "    Mayank Johri    "
print(len(st))
s = st.strip()
print(len(s))
print(st.rstrip())
print(st.lstrip())

m = "Mohan Shah"
x = ["mon", "tues", "wed"]
y = ","
a = "On Leave"
print(y.join(x)) # -> mon,tues,wed
print(m.join(y))
print(a.join(y))
print(y.join(a))
print(a.join(m))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
>>> TODO !!!
Explain the below examples
Can you write Python code for it?

'{} {}'.format('सूर्य', 'नमस्कार')

'{1} {0}'.format('सूर्य', 'नमस्कार')

'{:>10}'.format('सूर्य नमस्कार')

'{:20}'.format('सूर्य नमस्कार')

'{:4}'.format('Bonjour')

'{:_<5}'.format('Ja')

'{:^7}'.format('こんにちは')

'{:.5}'.format('Bonjour')

'{:10.5}'.format('Bonjour')

'{:{align}{width}}'.format('Bonjour', align='^', width='9')

'{:.{prec}} = {:.{prec}f}'.format('Bonjour', 2.22, prec=4)

'{:d}'.format(1980)

'{:f}'.format(3.141592653589793)

'{:4f}'.format(3.141592653589793)

'{:04d}'.format(119)

'{:06.2f}'.format(3.141592653589793)

'{:+d}'.format(119)

'{:+d}'.format(-119)

### Need to find for complex & boolean numbers
## '{:+d+d}'.format(-3 + 2j)

'{:=5d}'.format((- 111))

'{: d}'.format(101)

'{name} {surname}'.format(name='Mayank', surname='Johri')

user = dict(name='Mayank', surname='Johri')
'{u[name]} {u[surname]}'.format(u=user)

lst = list(range(10))
'{l[2]} {l[7]}'.format(l=lst)

from datetime import datetime
'{:%Y-%m-%d %H:%M}'.format(datetime(2017, 12, 23, 14, 15))

class Yoga(object):

    def __repr__(self):
        return 'सूर्य नमस्कार'

'{0!r} <-> {0!a}'.format(Yoga())


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
Interpolation
string interpolation (or variable interpolation, variable substitution, or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values.
Can you write Python code for it?

print( 'Size of %s => %d' % (s, len(s)))
print(dir(s))
print( 'Size of %s => %d' % (s, s.__len__()))

def size(strdata):
    c = 0
    for a in strdata:
        c+=1
    return c

print(size("Anshu"))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
Since Python 3, strings are by default unicode string.
The standard string can be converted to unicode by using the function unicode().

String can be initialized using:

With single or double quotes ('', "").
On several consecutive lines, provided that it's between three single or double quotes (''' ''', """ """).
Without expansion characters (example: s = r '\ n', where s will contain the characters \ and n).
Can you write Python code for it?

a = r'Roshan\tMusheer'
print(a)

path = "C:\new_data\technical_jargons"
print(path)
path = R"C:\new_data\technical_jargons"
print(path)


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
String Module

Various functions for dealing with text are implemented in the module string.
Can you write Python code for it?

import string

# the alphabet
print(dir(string))
a = string.ascii_letters
print(a)
# Shifting left the alphabet
b = a[1:] + a[0]
print(b)
print(b.__doc__)
print(string.digits)
print(string.hexdigits)
print(help(string.printable))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
String Operations:
Creation
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
Template
The module also implements a type called Template, which is a model string that can be filled through a dictionary. Identifiers are initialized by a dollar sign ($) and may be surrounded by curly braces, to avoid confusion.
Example:
Can you write Python code for it?

import string

# Creates a template string
st = string.Template('$warning occurred in $when $$what')

# Fills the model with a dictionary
s = st.substitute({'warning': 'Lack of electricity',
    'when': 'April 3, 2002'})

# Shows:
# Lack of electricity occurred in April 3, 2002
print(s)

# Unicode String
u = u'Hüsker Dü'
# Convert to str
s = u.encode('latin1')
print (s, '=>', type(s))

# String str
s = 'Hüsker Dü'
# u = s.decode('latin1')

print (repr(u), '=>', type(u))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
The operator % is used for string interpolation. The interpolation is more efficient in use of memory than the conventional concatenation.
Symbols used in the interpolation:

%s: string.
%d: integer.
%o: octal.
%x: hexacimal.
%f: real.
%e: real exponential.
%%: percent sign.

Symbols can be used to display numbers in various formats.
Example:
Can you write Python code for it?

# Zeros left
print ('Now is %02d:%02d.' % (6, 30))

# Real (The number after the decimal point specifies how many decimal digits )
print ('Percent: %.1f%%, Exponencial:%.2e' % (5.333, 0.00314))

# Octal and hexadecimal
print ('Decimal: %d, Octal: %o, Hexadecimal: %x' % (10, 10, 10))


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
encoding and decoding
Standard String
Can you write Python code for it?

+ Raw String: `a = r'Roshan\tMusheer'` # Roshan\tMusheer
+ Unicode String: `u = u'Björk'`


I am working on the file "Section 1 - Core Python/Chapter 05 - Data Types/String.ipynb".
The first task is:
format
In addition to interpolation operator %, the string method and function format() is available.

The function format() can be used only to format one piece of data each time.

Examples:
Can you write Python code for it?

peoples = [('Mayank', 'friend', 'Manish'),
('Mayank', 'reportee', 'Roshan Musheer')]

# Parameters are identified by order
msg = '{0} is {1} of {2}'

for name, relationship, friend in peoples:
    print(msg.format(name, relationship, friend))

# Parameters are identified by name
msg = '{greeting}, it is {hour:02d}:{minute:02d}'

print(msg.format(greeting='Good Morning', hour=9, minute=30))
print(msg)
# Builtin function format()
print ('Pi =', format(3.14159, '.3e'))
print ('Pi =', format(3.14159, '.1e'))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Example (Fibonacci series with recursion):
Can you write Python code for it?

def fib(n):
    """Fibonacci:
    fib(n) = fib(n - 1) + fib(n - 2) se n > 1
    fib(n) = 1 se n <= 1
    """
    if n > 1:
        return fib(n - 1) + fib(n - 2)
    else:
        return 1

# Show Fibonacci from 1 to 5
for i in [1, 2, 3, 4, 5]:
    print (i, '=>', fib(i))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Example (Fibonacci series without recursion):
Can you write Python code for it?

def fib(n):
    # the first two values
    l = [1, 1]

    # Calculating the others
    for i in range(2, n + 1):
        l.append(l[i -1] + l[i - 2])

    return l[n]

# Show Fibonacci from 1 to 5
for i in [1, 2, 3, 4, 5]:
    print (i, '=>', fib(i))

def test(a, b):
    print(a, b)
    return a + b

print(test(1, 2))
test(b=1, a=2)

def test_abc(a, b, c):
    print(a, b, c)
    return a + b + c

try:
    test_abc(b=1, a=2, 3)
except SyntaxError as e:
    print("error", e)


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Example (RGB conversion):
Can you write Python code for it?

def rgb_html(r=0, g=0, b=0):
    """Converts R, G, B to #RRGGBB"""

    return '#%02x%02x%02x' % (r, g, b)

def html_rgb(color='#000000'):
    """Converts #RRGGBB em R, G, B"""

    if color.startswith('#'): color = color[1:]

    r = int(color[:2], 16)
    g = int(color[2:4], 16)
    b = int(color[4:], 16)

    return r, g, b

print (rgb_html(200, 200, 255))
print (rgb_html(b=200, g=200, r=255))
print (html_rgb('#c8c8ff'))


def rgb_html(r=0, g=0, b=0):
    """Converts R, G, B to #RRGGBB"""

    return '#%02x%02x%02x' % (r, g, b)

def html_rgb(color='#000000'):
    """Converts #RRGGBB em R, G, B"""

    if color.startswith('#'): color = color[1:]

    r = int(color[:2], 16)
    g = int(color[2:4], 16)
    b = int(color[4:], 16)

    return r, g, b # a sequence

print (rgb_html(200, 200, 255))
print (rgb_html(b=200, g=200, r=255)) # what's happened?
print (html_rgb('#c8c8ff'))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Example (factorial with recursion):
Can you write Python code for it?

def factorial(num):
    """Fatorial implemented with recursion."""
    if num <= 1:
        return 1
    else:
        return(num * factorial(num - 1))

# Testing factorial()
print (factorial(5))

# 5 * (4 * (3 * (2) * (1))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Functions can also not return anything like in the below example
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Functions can also return multiple values, usually in form of tuple.
Can you write Python code for it?

def test(a, b):
    print(a, b)
    return a*a, b*b

x, a = test(2, 5)

print(x)
print(type(x))
print(a)
print(type(a))

print(type(test(2, 5)))

def test(a, b):
    print(a, b)
    return a*a, b*b, a*b

x = test(2 , 5)
print(x)
print(type(x))

def test(a, b):
    print(a, b)
    return a*a, b*b, "asdf"

x = test(2, 5)
print(x)
print(type(x))

def test(a=100, b=1000):
    print(a, b)
    return a, b

x = test(2, 5)
print(x)
print(test(10))

def test(a=100, b=1000):
    print(a, b)
    return a, b

print(test(b=10))
print(test(101))

def test(d, c, a=100, b=1000):
    print(d, c, a, b)
    return d, c, a, b

x = test(c=2, d=10, b=5)
print(x)
x = test(1, 2, 3, 4)
print(x)
print(test(10, 2))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Functions can return any data type, next example returns a boolean value.
Can you write Python code for it?

def isValid(data):
    if 10 in data:
        return True
    return False

a = isValid([10, 200, 33, "asf"])
print(a)

a = isValid((10,))
print(a)

isValid((10,))

a = isValid((110,))
print(a)

def isValid_new(data):
    return 10 in data

print(isValid_new([10, 200, 33, "asf"]))

a = isValid_new((110,))
print(a)


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
In the above example, we have caps as function, which takes val as argument and returns val * 2.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
In the example, kargs will receive the named arguments and args will receive the others.
The interpreter has some builtin functions defined, including sorted(), which orders sequences, and cmp(), which makes comparisons between two arguments and returns -1 if the first element is greater, 0 (zero) if they are equal, or 1 if the latter is higher. This function is used by the routine of ordering, a behavior that can be modified.
Example:
Can you write Python code for it?

def func(* , **kargs):
    print (args)
    print (kargs)
a = {
    "name": "Mohan kumar Shah",
    "age": 24 + 1
}
func('weigh', 10, unit='k', val=a)

def func(*args):
    print(args)

func('weigh', 10, "test")

data = [(4, 3), (5, 1), (7, 2), (9, 0)]

# Comparing by the last element
def _cmp(x, y):
    return cmp(x[-1], y[-1])

print ('List:', data)


def func(*args, **kargs):
    print (args)
    print (kargs)
a = {
    "name": "Mohan kumar Shah",
    "age": 24 + 1
}
func('weigh', 10, unit='k', val=a)

def func(*args):
    print(args)

func('weigh', 10, "test")

data = [(4, 3), (5, 1), (7, 2), (9, 0)]

# Comparing by the last element
def _cmp(x, y):
    return cmp(x[-1], y[-1])

print ('List:', data)


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Note: non-default argument's should always follow default argument
Can you write Python code for it?

def test(d, a=100, c, b=1000):
    print(d, c, a, b)
    return d, c, a, b

x = test(c=2, d=10, b=5)
print(x)
x = test(1, 2, 3, 4)
print(x)
print(test(10, 2))

def test(c, d, a=100, b=1000):
    print(d, c, a, b)
    return d, c, a, b

x = test(c=2, d=10, b=5)
print(x)
x = test(1, 2, 3, 4)
print(x)
print(test(10, 2))


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Observations:

The  arguments with default value must come last, after the non-default arguments.
The default value for a parameter is calculated when the function is defined.
The arguments passed without an identifier are received by the function in the form of a list.
The arguments passed to the function with an identifier are received in the form of a dictionary.
The parameters passed to the function with an identifier should come at the end of the parameter list.

Example of how to get all parameters:
Can you write Python code for it?

# *args - arguments without name (list)
# **kargs - arguments with name (ditcionary)

def func(*args, **kargs):
    print (args)
    print (kargs)

func('weigh', 10, unit='k')


I am working on the file "Section 1 - Core Python/Chapter 06 - Functions/1. Functions.ipynb".
The first task is:
Python also has a builtin function eval(), which evaluates code (source or object) and returns the value.
Example:
Can you write Python code for it?

print (eval('12. / 2 + 3.3'))

def listing(lst):
    for l in lst:
        print(l)

d = {"Mayank Johri":40, "Janki Mohan Johri":68}
listing(d)

d = {
    "name": "Mohan",
    "age": 24
}

a = {
    "name": "Mohan kumar Shah",
    "age": 24 + 1
}

def process_dict(d=a):
    print(d)

process_dict(d)
process_dict()

def test(a=[]):
    a.append(1)
    print(a)

test()
test()
test()

def test(a=None):
    if a == None:
        a = []
    a.append(1)
    print(a)

test()
test()
test()


I am working on the file "Section 1 - Core Python/Chapter 07 - Scope/Scope_of_names.ipynb".
The first task is:
NOTE: - A special quirk of Python is that – if no global statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects.
Can you write Python code for it?

def outer():
    a = 0
    b = 1

    def inner():
        print(a)
        print(b)
        b = 4

    inner()
try:
    outer()
except Exception as e:
    print(e)

def outer():
    a = 0
    print("outer: ", a)

    def inner():
        global a
        print("inner: ", a)

    inner()

a = 200
b = 10
outer()
print("base")
print(a)
print(b)

def power(x):
    def inner(y):
        return y ** x
    return inner

square = power(2)
cube = power(3)

print(square)
print(cube)

square(10)

square(20)

cube(3)

power(2)(20)='[================================================]'

def List_fun(l, a=[]):
    """function takes 2 parameters list having values and empty list."""

    for i in l:
        #checking whether the values are list or not
        if isinstance(i, list):
            List_fun(i, a)
        else:
            a.append(i)
    return a

b=[]
l2 = List_fun([[1,2],[3,[4,5]],6,7], b)
print(l2)
print(b)
print(id(l2))
print(id(b))

def List_fun(l, a=[]):
    """function takes 2 parameters list having values and empty list."""

    for i in l:
        #checking whether the values are list or not
        if isinstance(i, list):
            List_fun(i, a)
        else:
            a.append(i)

b=[]
List_fun([[1,2],[3,[4,5]],6,7], b)
print(b)

def fun_numbers(a):
    print(a)
    print(id(a))
    a += 10
    print(a)
    print(id(a))

b = 10
print(id(b))
fun_numbers(b)
print(b)

def fun_numbers(a):
    print(a)
    print(id(a))
    a = [20]
    print(a)
    print(id(a))

b = [10]
print(id(b))
fun_numbers(b)
print(b)

def fun_numbers(a):
    print(a)
    print(id(a))
    a.append(20)
    print(a)
    print(id(a))

b = [10]
print(id(b))
fun_numbers(b)
print(b)

def fun_numbers(a):
    print(a)
    a.append(120)
    print(a)

b = [10]
fun_numbers(b)
print(b)

def func():
    a = 10
    for d in [10,20,30]:
        a = a+d
    print(a)

func()


I am working on the file "Section 1 - Core Python/Chapter 07 - Scope/Scope_of_names.ipynb".
The first task is:
Using global variables is not considered a good development practice, as they make the system harder to understand, so it is better to avoid their use. The same applies to overshadowing variables.
Can you write Python code for it?

add = 10

def addlist(lists):
    """
    Add lists of lists, recursively
    the result is global
    """
    global add

    for item in lists:
        if isinstance(item, list): # If item type is list
            addlist(item)
            x = 100
        else:
            add += item

        print(x)

try:
    addlist([[1, 2], [3, 4, 5], 6])
except Exception as e:
    print(e)
print(add)

def outer():
    a = 0
    b = 1

    def inner():
        print(a)
        print(b)

    inner()

outer()


#add = 10

def addlist(lists):
    """
    Add lists of lists, recursively
    the result is global
    """
    global add

    for item in lists:
        if isinstance(item, list): # If item type is list
            addlist(item)
            x = 100
        else:
            add += item

        print(x)

addlist([[1, 2], [3, 4, 5], 6])

print(add)

def outer():
    a = 0
    b = 1

    def inner():
        print(a)
        print(b)

    inner()

outer()


I am working on the file "Section 1 - Core Python/Chapter 07 - Scope/Scope_of_names.ipynb".
The first task is:
Wasted global usage
Can you write Python code for it?

global a
a = 10

def test():
    a = "Pune Rocks"
    print(a)
    print(locals())
    print("~"*20)
#     print(globals())

test()
print(a)

a = 10

def test(a):
    print(a)
    a = "Pune Rocks"
    print(locals())
    return a

a= test(a)
print(a)
# print(locals())
print(len(locals()))
print(len(globals()))

def addlist(lists):
    """
    Add lists of lists, recursively
    the result is global
    """
    global add
    for item in lists:
        if isinstance(item, list): # If item type is list
            addlist(item)
        else:
            add += item # add = add + item

add = 0
addlist([[1, 2], [3, 4, 5], 6])

print(add)

# add = 10

def addlist(lists):
    """
    Add lists of lists, recursively
    the result is global
    """
    global add2

    for item in lists:
        if isinstance(item, list): # If item type is list
            addlist(item)
        else:
            if 'add2' in globals():
                add2 += item
            else:
                print("Creating add")
                add2 = item

addlist([[1, 2], [3, 4, 5], 6])

print(add2)


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
Example module use:
Can you write Python code for it?

from os.path import getsize, getmtime
from time import localtime, asctime

import modutils

mods = modutils.find('os')
print("Valid attributes of module: ", dir(modutils))
for mod in mods:
    tm = asctime(localtime(getmtime(mod)))
    kb = getsize(mod) / 1024
    print ('{0}: ({1} kbytes, {2})'.format(mod, kb, tm))


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
Example of module usage:
Can you write Python code for it?

# Imports calc module
import calc

l = [23, 54, 31, 77, 12, 34]

# Calls the function defined in calc
print (calc.average(l))


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
Example of user defined module:
Can you write Python code for it?

# File calc.py

# Function defined in module
def average(list): return float(sum(list)) / len(list)


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
TIP: Splitting programs into modules makes it easy to reuse and locate faults in the code.

The Module Search Path
When a module is requested to be imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named <module_name>.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.
Packages

NOTE: To be discussed after classes

Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or the Python Imaging Library from having to worry about each other’s module names.
Suppose you want to design a collection of modules (a “package”) for the uniform handling of sound files and sound data. There are many different sound file formats (usually recognized by their extension, for example: .wav, .aiff, .au), so you may need to create and maintain a growing collection of modules for the conversion between the various file formats. There are also many different operations you might want to perform on sound data (such as mixing, adding echo, applying an equalizer function, creating an artificial stereo effect), so in addition you will be writing a never-ending stream of modules to perform these operations. Here’s a possible structure for your package (expressed in terms of a hierarchical filesystem):
Package maya_util structure
```
./maya_util:
init.py  db  json  misc.py
./maya_util/db:
init.py  read.py  write.py
./maya_util/json:
init.py  read.py ...
Can you write Python code for it?

import maya_util.db

## Magic of modules
### Hidden modules
# 1)
cond = false
if cond:
    from A import amp1 as a
else:
    from B import bmp1 as a

# 2)
if cond:
    from A import a
    print(a.test)
else:
    from AA import aa as a
    print(a.test)

# 3)
# Check how many times import statement is called
for i in range(1,10):
    if cond:
        import a
        print(a.test)
    else:
        import aa as a
        print(a.test)
###

## PLEASE ADD advance topics in it.


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
That way it is easy to turn a program into a module.
Another module example:
Can you write Python code for it?

"""
modutils => utility routines for modules
"""

import os.path
import sys
import glob

def find(txt):
    """find modules with name containing the parameter."""

    resp = []

    for path in sys.path:
        mods = glob.glob('%s/*.py' % path)

        for mod in mods:
            if txt in os.path.basename(mod):
                resp.append(mod)

    return resp


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
The from...import Statement
Python allows to import specific portions of the module instead of whole module by using from ... import statement.
The format of it is as follows
Can you write Python code for it?

from <module> import <attribute/submodule(s)>

from os import name
print (type(name))

from os import name as os_name
print (os_name)
print

from os import environ, linesep


I am working on the file "Section 1 - Core Python/Chapter 08 - Modules/Chapter8_Modules.ipynb".
The first task is:
The main module of a program has the variable __name__ equals to __main__, thus it is possible to test if the main module:
Can you write Python code for it?

if __name__ == "__main__":
    # Code here will only be run
    # if it is the main module
    # and not when it is imported by another program/module
    pass


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Every instance of this class will contain three attributes, namely title, author, and get_entry, in addition to the standard attributes provided by the object ancestor.
Can you write Python code for it?

b = Book(title="Akme", author="Mayank")

print(dir(b))

print(b.title)
b.title = "Lets Go"
print(b.title)
print(b.get_entry())

data = b.get_entry
print(data)
print(data())
print(type(b.__dict__))
print(b.__dict__)
#print(b.nonExistAttribute())

def testtest(func):
    print(func())

testtest(data)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Having a single implementation
attributes
In Python, attribute is everything, contained inside an object. In Python there is no real distinction between plain data and functions, being both objects.
The following example represents a book with a title and an author. It also provides a get_entry() method which returns a string representation of the book.
Can you write Python code for it?

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def get_entry(self):
        return f"{self.title} by {self.author}"


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Instead of using the normal statements to access attributes, you can use the following functions −
getattr
: to access the attribute of the object
The getattr(obj, name[, default])
: to access the attribute of object.
The hasattr(obj,name)
: to check if an attribute exists or not.
The setattr(obj,name,value)
    : to set an attribute. If attribute does not exist, then it would be created.
The delattr(obj, name)
    : to delete an attribute.
Properties
Sometimes you want to have an attribute whose value comes from other attributes or, in general, which value shall be computed at the moment. The standard way to deal with this situation is to create a method, called getter, just like I did with get_entry().
In Python you can "mask" the method, aliasing it with a data attribute, which in this case is called property.
Can you write Python code for it?

class Book(object):
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def get_entry(self):
        return "{0} by {1}".format(self.title, self.author)

    entry = property(get_entry)

b = Book(title="Pawn of Prophecy", author="David Eddings")
print(b.entry)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Lets look at another example, we have removed the __new__ method from the above class and created an object.
Can you write Python code for it?

class MyNewTest:
    def __init__(self, name):
        print("in init", name)

mnt = MyNewTest("Hari Hari")


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
NOTE: both Second and sec are same object as their id's are same
Can you write Python code for it?

class Second:
    def __init__(self, name = ""):
        self.fullname = name

    def set_name(self, name):
        print(id(self))
        self.fullname = name

    def get_name(self):
        return self.fullname

sec = Second("Vishal Saxena")
print(sec.get_name())

# first.py

class Second:
    def __init__(self, name, age=35):
        self.name(name)
        self.age = age

    def name(self, new_name):
        self.fullname = new_name

    def get_name(self):
        return self.fullname

sec = Second("Arya")
print(sec.get_name())
print(sec.age)

class Second:
    def __init__(self, name, age=55):
        self.name(name)
        self.age = age

    def name(self, name):
        self.name = name

    def get_name(self):
        return self.name

sec = Second("Rajneekanth")
print(sec.get_name())
print(sec.age)
print(dir(sec))
dir(sec.__dir__)

# first.py

class Second:
    fullname = "Mayank Johri"
    age = 33

    def name(self, name):
        self.fullname = name

    def get_name(self):
        return self.fullname

sec = Second()
print(dir(sec))
print(sec.get_name())
print(id(sec.fullname))
sec2 = Second()
print(id(sec2.fullname))

# first.py

class Second:
    fullname = "Ram Setu"
    age = 33

    def name(self, name):
        self.fullname = name

    def get_name(self):
        return self.fullname

rs_1 = Second()
rs_2 = Second()

print(rs_1.fullname == rs_2.fullname)
print(id(rs_1.fullname) == id(rs_2.fullname))

rs_1.name("ram setu")

print(rs_1.fullname == rs_2.fullname)
print(id(rs_1.fullname) == id(rs_2.fullname))

print(rs_1.fullname, " - ", rs_2.fullname)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Note, in the above example all three objects are pointing to same object ohm meaning all three objects are same.
Now, we might have situations where we need to raise exception, if creation of more than one instance is attempted. We can achieve it by raising an exception as shown in below example.
Can you write Python code for it?

class SingletonError(Exception):
    pass

class HeadMaster(object):

    def __new__(cls, name):
        it = cls.__dict__.get("__it__")
        if it is not None:
            raise SingletonError(f"Count not create new instance for value {name}")

        cls.__it__ = it = object.__new__(cls)
        it.__init__(name)
        return it

    def __init__(self, name):
        self.name = name

    def print_name(self):
        print(self.name)

try:
    print("Creating Anshu Mam as Primary School headmistress.")
    anshu_mam = HeadMaster("Anshu Shrivastava")
    print("Creating Rahim Sir as Primary School headmaster.")
    rahim_sir = HeadMaster("Rahim Khan")
except Exception as e:
    print(e)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Now lets check where its goog idea to use __init__ and where __new__.
One thumb rule is try to avoid using __new__ and let python handle it because almost all the things you wish to do in constructor can be done in __init__. Still if you wish to do so, below examples will show you how to do it currectly.
In the first example, we have __init__ function and are using it.
Can you write Python code for it?

class MyNewTest:
    def __init__(self, name):
        print("in init", name)
        self.name = name

    def print_name(self):
        print(self.name)

mnt = MyNewTest("Hari Hari")
mnt.print_name()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Now, since we have not returned any thing in __new__ thus mnt is null. We must have __new__ which returns the object itself. Now to over come this issue, we need to return an instance of our class. We can do that using instance = super(<class>, cls).__new__(cls) as shown in the below example
Can you write Python code for it?

class MyNewTest(object):
    def __new__(cls, name):
        print("in __new__:\n\t{0}".format(name))
        instance = super(MyNewTest, cls).__new__(cls)
        instance.name = name
        return instance

    def print_name(self):
        print("print_name:\n\t{0}".format(self.name))

mnt = MyNewTest("!!! Hari Om Hari Om !!!")
mnt.print_name()
ram_ram = MyNewTest("!!! Ram Ram !!!")
ram_ram.print_name()
mnt.print_name()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Properties allow to specify also a write method (a setter), that is automatically called when you try to change the value of the property itself.

NOTE:
Don't Worry to much about properties, we have entire chapter dedicated for it.
Can you write Python code for it?

class User():
    def __init__(self, name):
        self.name = name

    def getname(self):
        return "User's full name is: {0}".format(self.name)

    def setname(self, name):
        self.name = name

    fullname = property(getname, setname)

user = User("Roshan Musheer")
print(user.fullname)
user.fullname = "Shaeel Parez"
print(user.fullname)
# print(x)
# print(p.name)

class TestSetter():
    def setter(self, name):
        self.name = name
    myname = property(fset=setter)

ts = TestSetter()

ts.myname = "Mayank"
print(ts.name)

class A:
    def get_x(self, neg=False):
        return -5 if neg else 5
    x = property(get_x)

a = A()
print(a.x)

class Book(object):
    def __init__(self, title, author):
        self.__title = title
        self.__author = author

    def __get_entry(self):
#         print("_get_entry")
        return "{0} by {1}".format(self.__title, self.__author)

    def __set_entry(self, value):
        if " by " not in value:
            raise ValueError("Entries shall be formatted as '<title> by <author>'")
        self.__title, self.__author = value.split(" by ")

    entry = property(__get_entry, __set_entry)

    def __getattr__(self, attr):
        print("Sorry attribure do not exist")
        return None

b = Book(title="Step in C", author="Mayank Johri")
print(b.entry)
b.entry = "Lets learn C by Mayank Johri"
print("*"*20)
print(b.entry)
print("*"*20)
b.entry = "Explore Go by Mayank Johri"
print("*"*20)
print(b.entry)
b.nonExistAttribute


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Regulating number of object creation
we are going to tweak previous example and convert it to have a finite number of objects created for the class
Can you write Python code for it?

class HeadMaster(object):
    _instances = []  # Keep track of instance reference
    limit = 2

    def __new__(cls, *args, **kwargs):
        if len(cls._instances) >= cls.limit:
            raise RuntimeError("Creation Limit %s reached" % cls.limit)
        instance = object.__init__(cls)
        cls._instances.append(instance)
        return instance

    def __del__(self):
        self._instance.remove(self)

try:
    li1 = HeadMaster()
    li2 = HeadMaster()
    li3 = HeadMaster()
    li4 = HeadMaster()
except Exception as e:
    print(e)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
So far so good, val & id of val from both the instance and class seems to be same, thus they are pointing to same memory location which contains the value.
Now lets try to update it in class
Can you write Python code for it?

Static_Test.val = "राजीव चतुर्वेदी"

print(s.val,"\b,", id(s.val))
print(Static_Test.val, "\b,", id(Static_Test.val))
s_new = Static_Test()
print(s_new.val,"\b,", id(s.val))


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
So, if we update values at class level, than they are getting reflected in all the instances as well. Now lets try to update its value in an instance and check its effect
Can you write Python code for it?

s.val = "Sachin"
print(s.val,"\b,", id(s.val))
print(Static_Test.val, "\b,", id(Static_Test.val))
s_new = Static_Test()
print(s_new.val,"\b,", id(s.val))


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
We saw, that everything was working without any issue. Now lets try to replace __init__ with __new__.
Can you write Python code for it?

# -----------------#
# Very Bad Example #
# -----------------#
class MyNewTest:
    def __new__(cls, name):
        print("in init", name)
        cls.name = name

    def print_name(self):
        print(self.name)

try:
    mnt = MyNewTest("Hari Hari")
    mnt.print_name()
except Exception as e:
    print(e)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
Where can we use __new__
Creating singleton class
In singleton pattern, we create one instance of the class and all subsequent objects of that class points to the first instance.
Lets try to create a singleton class using __new__ constructor.
Can you write Python code for it?

class Godlike(object):

    def __new__(cls, name):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(name)
        return it

    def init(self, name):
        self.name = name

    def print_name(self):
        print(self.name)

ohm = Godlike("Ohm")
ram = Godlike("ram")
hari = Godlike("hari")

print(ohm is ram)
print(ohm is hari)
ohm.print_name()
ram.print_name()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
__new__
__new__ is called for new Class type,
Overriding the new method
As per "https://www.python.org/download/releases/2.2/descrintro/#new"
Here are some rules for __new__:

__new__ is a static method. When defining it, you don't need to (but may!) use the phrase "__new__ = staticmethod(__new__)", because this is implied by its name (it is special-cased by the class constructor).
The first argument to __new__ must be a class; the remaining arguments are the arguments as seen by the constructor call.
A __new__ method that overrides a base class's __new__ method may call that base class's __new__ method. The first argument to the base class's __new__ method call should be the class argument to the overriding __new__ method, not the base class; if you were to pass in the base class, you would get an instance of the base class.
Unless you want to play games like those described in the next two bullets, a __new__ method must call its base class's __new__ method; that's the only way to create an instance of your object. The subclass __new__ can do two things to affect the resulting object: pass different arguments to the base class __new__, and modify the resulting object after it's been created (for example to initialize essential instance variables).
__new__ must return an object. There's nothing that requires that it return a new object that is an instance of its class argument, although that is the convention. If you return an existing object, the constructor call will still call its __init__ method. If you return an object of a different class, its __init__ method will be called. If you forget to return something, Python will unhelpfully return None, and your caller will probably be very confused.
For immutable classes, your __new__ may return a cached reference to an existing object with the same value; this is what the int, str and tuple types do for small values. This is one of the reasons why their __init__ does nothing: cached objects would be re-initialized over and over. (The other reason is that...
Can you write Python code for it?

class MyTest:
    def __new__(self):
        print("in new")

    def __init__(self):
        print("in init")

mnt = MyTest()

class MyNewTest:
    def __new__(self):
        print("in new")

    def __new__(self, name):
        print("in new", name)

    def __init__(self, name):
        print("in init", name)

mnt = MyNewTest("Hari Hari")


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
```python
class Class_Name(base_classes_if_any):
    """optional documentation string"""
static_member_declarations = 1

def method_declarations(self):
    """
    documentation
    """
    pass

```
Can you write Python code for it?

# first.py

class First:
    pass

fr = First()
print (type(fr))
print (type(First))
print(type(int))

# first.py

class First(object):
    pass

fr = First()
print (type(fr))
print (type(First))
print(type(int))

# first.py
# Class with it's methods

class Second:
    def set_name(self, name):
        self.fullname = name

    def get_name(self):
        return self.fullname

try:
    sec = Second()
    print(sec.get_name())
except Exception as e:
    print(e)

# first.py

class Second:
    def set_name(self, name):
        print(id(self))
        self.fullname = name

    def get_name(self):
        return self.fullname

sec = Second()
print(id(sec))
sec.set_name("Manish Gupta")
print(sec.get_name())


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
both super(MyNewTest, cls).__new__(cls) and object.__new__(cls) produce the desired instance as shown in the above examples.
If we were to return anything other than instance of object, then __init__ function will never be called as shown in the below example.
Can you write Python code for it?

class Distance(float):
    def __new__(cls, dist):
        print("in __new__", dist)
        return dist*0.0254

    # __init__ is redundent in this example,
    # as it will never be called.
    def __init__(self, dist):
        print("in __init__", dist)

    def print_dist(self):
        print(self.__name__)

try:
    mnt = Distance(22)
    print(mnt, type(mnt))
    mnt.print_dist()
except Exception as e:
    print(e)

class Distance(float):
    def __new__(cls, dist):
        print("in __new__", dist)
        instance =  super(Distance, cls).__new__(cls)
        print(type(instance))
        instance.val = dist*0.0254
        return instance

    def __init__(self, dist):
        print("in __init__", dist)

    def print_dist(self):
        print(self.val)

if __name__ == "__main__":
    try:
        mnt = Distance(22)
        print(mnt, type(mnt))
        mnt.print_dist()
    except Exception as e:
        print(e)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/01_Classes_and_OOPS.ipynb".
The first task is:
static / class variables
 Reference:
https://stackoverflow.com/questions/68645/are-static-class-variables-possible.
Static variables are variables declared inside the class definition, and not inside a method are class or static variables.
But before you go all, Yahooooo... about understanding of static variables. Please note that the implementation of static variables in python are different from Java/C++, they are unique in many ways.
Lets understand them a little using the following code
Can you write Python code for it?

class Static_Test(object):
    val = "Rajeev Chaturvedi"

s = Static_Test()

print(s.val,"\b,", id(s.val))
print(Static_Test.val, "\b,", id(Static_Test.val))


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_02_Single_Inheritance.ipynb".
The first task is:
BallPen & InkPen both are initializing the parent class using super().__init(size, name) function. Now lets create few objects of both,
Can you write Python code for it?

pb = BallPen(10, "Renolds", "Green")
print(pb.name)
pb.set_name("cello")
print(pb.name)
print(pb.__dict__)

class grand_parent:
    def __init__(self, middle_name):
        print("grand_parent init")
        self.__middle_name = middle_name

    def middle_name(self, middle_name):
        self.__middle_name = middle_name
        return self.__middle_name


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_02_Single_Inheritance.ipynb".
The first task is:
Check the order of init's being called.
Can you write Python code for it?

print(mohan.middle_name)

mohan.middle_name = "KUMAR"
print(mohan.middle_name)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_02_Single_Inheritance.ipynb".
The first task is:
Lets create a parent class which inherits grand_parent class, note we have used super().__init_(middle_name) to set middle name using parents function middle_name.
Can you write Python code for it?

class parent(grand_parent):
    def __init__(self, middle_name, surname):
        print("parent init")
        self.__surname = surname
        super().__init__(middle_name)

    def middle_name(self):
        return self.middle_name


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_02_Single_Inheritance.ipynb".
The first task is:
Now lets create the same classes without init functions, and see what happens
Can you write Python code for it?

class grand_parent:
    def __init__(self, middle_name):
        print("grand_parent init")
        self.__middle_name = middle_name

    def middle_name(self, middle_name):
        self.__middle_name = middle_name
        return self.__middle_name

class parent(grand_parent):
    def __init__(self, middle_name, surname):
        print("parent init")
        self.__surname = surname

    def middle_name(self):
        return self.__middle_name

class student(parent):
    def __init__(self, name, middle_name, surname):
        print("student init")
        self.name = name

mohan = student("Venkat", "kumar", "Mohan")

try:
    print(mohan.middle_name())
except Exception as e:
    print(e)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_02_Single_Inheritance.ipynb".
The first task is:
We got the error because, init of none of the parent's were called, and only students init was called.
Can you write Python code for it?

# NOTE: python 2 has issues with Super , get it also documented here


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
C3 Linearization Algorithm
C3 super-class linearization follow the following rules:

Children precede their parents
If a class inherits from multiple classes, they are kept in the order specified in the tuple of the base class.
Consistent extended precedence graph, which in short means how base class is extended from the super class. Inheritance graph determines the structure of method resolution order.
Preserving local precedence ordering, i.e., visiting the super class only after the method of the local classes are visited.
Monotonicity
Can you write Python code for it?

class Type(type):
    def __repr__(cls):
        return cls.__name__

class O(object, metaclass=Type): pass

class A(O): pass

class B(O): pass

class C(O): pass

class D(O): pass

class E(O): pass

class K1(A, B, C): pass

class K2(D, B, E): pass

class K3(D, A): pass

class Z(K1, K2, K3): pass

Z.mro()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Few things of importance you will find are as follows
- By adding super().__init__(**kwds) in all the parents __init__ functions, and adding super().__init__(**kwds) in child __init__ function, we can make sure that init of every parent is called automatically.

We can use similar to def __init__(self, count_reportee, **kwds) as __init__ function signature, where count_reportee is the needed parameter for that class.
Can you write Python code for it?

class Base1:
    def test(self):
        print("in Base1 -> test")

class Base2:
    def test(self):
        print("in Base2 -> test")

class MultiDerived(Base1, Base2):
    def test2(self):
        super().test()
        Base2.test(Base2)

class MultiDerived2(Base2, Base1):
    def test2(self):
        super().test()
        Base2.test(Base2)

print("Please check the result of test()")

md = MultiDerived()
md.test2()
md.test()
print("*"*10)
md2 = MultiDerived2()
md2.test2()
md2.test()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Finding subclasses
Can you write Python code for it?

class O(object): pass

class A(O): pass

class B(O): pass

class C(O): pass

class D(O): pass

class E(O): pass

class K1(A, B, C): pass

class K2(D, B, E): pass

O.__subclasses__()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching same class twice
Can you write Python code for it?

class Base:
    def test(self):
        print("In Base test")

    def test_alone(self):
        print("In Base test: test_alone")

class Derived1(Base):
    def test(self):
        print("In Derived1 test")
        super().test()

    def test_alone(self, val):
        print("In Derived1 test: test_alone ", val)

    def test_alone(self):
        print("In Base test: test_alone")

class Derived2(Derived1):
    def test2(self):
        print("in Derived2 test2")

obj = Derived2()
obj.test()
obj.test2()
obj.test_alone()
Base.test(Base)

class Base:
    def test(self):
        print("In Base test")

    def test_base(self):
        print("test_base")

    def test_alone(self):
        print("In Base test: test_alone")

class Derived1(Base):
    def test(self):
        print("In Derived1 test")
        super().test()
        self.test_base()

    def test_alone(self, val):
        print("In Derived1 test: test_alone ", val)
        self.test()

obj = Derived1()
obj.test()
obj.test_alone("test")
Base.test(Base)

class Base:
    def test(self):
        print("In Base test")

    def test_base(self):
        print("test_base")

    def test_alone(self):
        print("In Base test: test_alone")

class Derived1(Base):
    def test(self):
        print("In Derived1 test")
        super().test()
        Base.test_base(Base)

    def test_alone(self, val):
        print("In Derived1 test: test_alone ", val)
        self.test()

obj = Derived1()
obj.test()
obj.test_alone("test")
Base.test(Base)

class Base:
    def test(self):
        print("In Base test")

    def test_alone(self):
        print("In Base test: test_alone")

class Derived1(Base):
    def test(self):
        print("In Derived1 test")
        super().test()

    def test_alone(self, val):
        print("In Derived1 test: test_alone ", val)

    def test_alone(self):
        print("In Base test: test_alone")

class Derived2(Derived1):
    def test2(self):
        print("in Derived2 test2")

obj...


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Lets create a small function based on the above code sample
Can you write Python code for it?

def get_subclasses(cls):
    lst = []
    for a in cls.__subclasses__():
        lst.append(a)
        lst.extend(get_subclasses(a))
    return set(lst)

get_subclasses(O)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Lets create the class MultiDerived child class using Base1 & Base2.
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Lets try the same using super function
Can you write Python code for it?

class Tech(object):
    def __init__(self, tech, **kwds):
        print("Inside Tech")
        self._tech_name = tech
        super().__init__(**kwds)

    @property
    def tech_name(self):
        return self._tech_name

    @tech_name.setter
    def tech_name(self, name):
        self._tech_name = name

class Lead(object):
    def __init__(self, count_reportee, **kwds):
        print("Inside Lead")
        self._reportee_count = count_reportee
        super().__init__(**kwds)

    @property
    def reportee(self):
        return self._reportee_count

    @reportee.setter
    def reportee(self, count):
        self._reportee_count = count

class TechLead(Tech, Lead):
    def __init__(self, name, **kwds):
        print("Inside TechLead")
        self._name = name
        super().__init__(**kwds)

abhi = TechLead(name="Abhishek Kumar", tech="macOS", count_reportee= 10)
print(abhi)
print(abhi.__dict__)

class Tech(object):
    def __init__(self, tech, **kwds):
        print("Inside Tech")
        self._tech_name = tech
#         super().__init__(**kwds)

    @property
    def tech_name(self):
        return self._tech_name

    @tech_name.setter
    def tech_name(self, name):
        self._tech_name = name

class Lead(object):
    def __init__(self, count_reportee, **kwds):
        print("Inside Lead")
        self._reportee_count = count_reportee
        super().__init__(**kwds)

    @property
    def reportee(self):
        return self._reportee_count

    @reportee.setter
    def reportee(self, count):
        self._reportee_count = count

class TechLead(Tech, Lead

              ):
    def __init__(self, name, **kwds):
        print("Inside TechLead")
        self._name = name
        super().__init__(**kwds)

abhi = TechLead(name="Abhishek Kumar", count_reportee= 10, tech="macOS")
print(abhi)
print(abhi.__dict__)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
Problem
```python
class First(object):
    def init(self):
        print("first")
class Second(First):
    def init(self):
        print("second")
class Third(First, Second):
    def init(self):
        print("third")
```
```python
TypeError                                 Traceback (most recent call last)
<ipython-input-37-a1afb9897f3d> in <module>()
      7         print("second")
      8
----> 9 class Third(First, Second):
     10     def init(self):
     11         print("third")
TypeError: Cannot create a consistent method resolution
order (MRO) for bases First, Second
```

Solution
Can you write Python code for it?

class First(object):
    def __init__(self):
        print("first")

class Second(First):
    def __init__(self):
        print("second")

class Third(Second):
    def __init__(self):
        print("third")

Third.mro()

class O(object): pass

class A(O): pass

class B(O): pass

class C(O): pass

class D(O): pass

class E(O): pass

class K1(A, B, C): pass

class K2(D, B, E): pass

class K3(D, A): pass

class Z(K1, K2, K3): pass

print(Z.mro())


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
The above object is a blank object with contains no custom defined attributes. Lets explore it in more details.
Can you write Python code for it?

class P1():
    def __init__(self):
        print("P-1 init")

class P2():
    def __init__(self):
        print("P_2 init")

class C1(P1, P2):
    pass

class C2(P2, P1):
    pass

c1 = C1()
print("*"*20)
c2 = C2()


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/02_03_Multiple_Inheritance.ipynb".
The first task is:
thus, we can create the following code the get the sub-classes name
Can you write Python code for it?

print([cls.__name__ for cls in O.__subclasses__()])


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/06_ABC.ipynb".
The first task is:
NOTE

issubclass: Return true if class is a subclass (direct, indirect or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects, in which case every entry in classinfo will be checked. In any other case, a TypeError exception is raised.

isinstance: Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. Also return true if classinfo is a type object (new-style class) and object is an object of that type or of a (direct, indirect or virtual) subclass thereof. If object is not a class instance or an object of the given type, the function always returns false. If classinfo is a tuple of class or type objects (or recursively, other such tuples), return true if object is an instance of any of the classes or types. If classinfo is not a class, type, or tuple of classes, types, and such tuples, a TypeError exception is raised.

Registering the child class
Can you write Python code for it?

from abc import ABCMeta, abstractmethod

class Mammal(metaclass=ABCMeta):

    @abstractmethod
    def eyes(self, val):
        raise NotImplementedError()

    def hair(self):
        print("hair")

    def neocortex(self):
        """a part of the cerebral cortex concerned with sight and hearing in mammals,
        regarded as the most recently evolved part of the cortex."""
        print("neocortex")

class Human:
    def eyes(self, val):
        print("human eyes: ", val)

Mammal.register(Human)
print ('Subclass:', issubclass(Human, Mammal))
c = Human()
print ('Instance:', isinstance(c, Mammal))
c.eyes("Hazel")


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/08_MetaProgramming.ipynb".
The first task is:
Classes are often referred to as “types,” so this reads fairly sensibly: you’re calling a function that creates a new type based on its arguments.
We can also add base classes, fields and methods:
Can you write Python code for it?

def howdy(self, you):
    print("Howdy, " + you)

MyList = type('MyList', (list,), dict(x=42, howdy=howdy))

ml = MyList()
ml.append("Camembert")
print(ml)
print(ml.x)
ml.howdy("John")


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/08_MetaProgramming.ipynb".
The first task is:
To modify a class, you perform operations on it like any other object. You can add and subtract fields and methods, for example. The difference is that any change you make to a class affects all the objects of that class, even the ones that have already been instantiated.
What creates these special “class” objects? Other special objects, called metaclasses.
The default metaclass is called type and in the vast majority of cases it does the right thing. In some situations, however, you can gain leverage by modifying the way that classes are produced – typically by performing extra actions or injecting code. When this is the case, you can use metaclass programming to modify the way that some of your class objects are created.
It’s worth re-emphasizing that in the vast majority of cases, you don’t need metaclasses, because it’s a fascinating toy and the temptation to use it everywhere can be overwhelming. Some of the examples in this chapter will show both metaclass and non-metaclass solutions to a problem, so you can see that there’s usually another (often simpler) approach.
Some of the functionality that was previously only available with metaclasses is now available in a simpler form using class decorators. It is still useful, however, to understand metaclasses, and certain results can still be achieved only through metaclass programming.
Basic Metaprogramming

So metaclasses create classes, and classes create instances. Normally when we write a class, the default metaclass type is automatically invoked to create that class, and we aren’t even aware that it’s happening.
It’s possible to explicitly code the metaclass’ creation of a class. type called with one argument produces the type information of an existing class; type called with three arguments creates a new class object. The arguments when invoking type are the name of the class, a list of base classes, and a dictionary giving the namespace for the class (all the fields and methods). So the equivalent of:
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/08_MetaProgramming.ipynb".
The first task is:
is
Can you write Python code for it?

C = type('C', (), {})

class Cpp(object):
    """This is a doc"""
    def me(self):
        self.meme="Me"

    def you(self):
        self.youyou = "You"

print(type(Cpp))
print(Cpp.__dict__)
cpp = Cpp()
cpp.me()
print(cpp.meme)

def me(self):
    self.meme="Me"

def you(self):
    self.youyou = "You"

Cpp = type('Cpp',(), dict(me=me, you=you) )
print(type(Cpp))
print(Cpp.__dict__)
cpp = Cpp()
cpp.me()
print(cpp.meme)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/Chapter 18 - Multiple Inheritance/Multiple Inheritance.ipynb".
The first task is:
Multilevel Inheritance

we can inherit to from a derived class also. This is called as multilevel inheritance. Multilevel inheritance can be of any depth in Python. An example with corresponding visualization is given below.
Can you write Python code for it?

class Base:
    pass

class Derived1(Base):
    pass

class Derived2(Derived1):
    pass


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/Chapter 21 - Metaclasses/MetaProgramming.ipynb".
The first task is:
Classes are often referred to as “types,” so this reads fairly sensibly: you’re calling a function that creates a new type based on its arguments.
We can also add base classes, fields and methods:
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/Chapter 21 - Metaclasses/MetaProgramming.ipynb".
The first task is:
To modify a class, you perform operations on it like any other object. You can add and subtract fields and methods, for example. The difference is that any change you make to a class affects all the objects of that class, even the ones that have already been instantiated.
What creates these special “class” objects? Other special objects, called metaclasses.
The default metaclass is called type and in the vast majority of cases it does the right thing. In some situations, however, you can gain leverage by modifying the way that classes are produced – typically by performing extra actions or injecting code. When this is the case, you can use metaclass programming to modify the way that some of your class objects are created.
It’s worth re-emphasizing that in the vast majority of cases, you don’t need metaclasses, because it’s a fascinating toy and the temptation to use it everywhere can be overwhelming. Some of the examples in this chapter will show both metaclass and non-metaclass solutions to a problem, so you can see that there’s usually another (often simpler) approach.
Some of the functionality that was previously only available with metaclasses is now available in a simpler form using class decorators. It is still useful, however, to understand metaclasses, and certain results can still be achieved only through metaclass programming.
Basic Metaprogramming

So metaclasses create classes, and classes create instances. Normally when we write a class, the default metaclass type is automatically invoked to create that class, and we aren’t even aware that it’s happening.
It’s possible to explicitly code the metaclass’ creation of a class. type called with one argument produces the type information of an existing class; type called with three arguments creates a new class object. The arguments when invoking type are the name of the class, a list of base classes, and a dictionary giving the namespace for the class (all the fields and methods). So the equivalent of:
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/Chapter 21 - Metaclasses/MetaProgramming.ipynb".
The first task is:
is
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/OOPs Fundamentals - ABC.ipynb".
The first task is:
Abstract Properties

If your API specification includes attributes in addition to methods, you can require the attributes in concrete classes by defining them with @abstractproperty
Can you write Python code for it?

from abc import ABCMeta, abstractmethod

class Base(metaclass=ABCMeta):

    @abc.abstractproperty
    def value(self):
        return 'Should never get here'

class Implementation(Base):
    @property
    def value(self):
        return 'concrete property'

if __name__ == '__main__':
#     try:
#         b = Base()
#         print ('Base.value:', b.value)
#     except Exception as err:
#         print ('ERROR:', str(err))

    i = Implementation()
    print ('Implementation.value:', i.value)

import abc

class Base(metaclass=abc.ABCMeta):

    @abc.abstractproperty
    def value(self):
        return 'Should never see this'

    @value.setter
    def value(self, newvalue):
        return

class Implementation(Base):

    _value = 'Default value'

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, newvalue):
        self._value = newvalue

i = Implementation()
print ('Implementation.value:', i.value)

i.value = 'New value'
print ('Changed value:', i.value)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/OOPs Fundamentals - ABC.ipynb".
The first task is:
Implementation Through Subclassing
Can you write Python code for it?

from abc import ABCMeta, abstractmethod

class Mammal(metaclass=ABCMeta):
## version 2.x ##     __metaclass__=ABCMeta

    @abstractmethod
    def eyes(self, val):
        raise NotImplementedError()

#     @abstractmethod
#     def hand(self):
#         raise NotImplementedError()

    def hair(self):
        print("hair")

    def neocortex(self):
        """a part of the cerebral cortex concerned with sight and hearing in mammals,
        regarded as the most recently evolved part of the cortex."""
        print("neocortex")

class Human(Mammal):
    def eyes(self, val):
        print("human eyes")

print ('Subclass:', issubclass(Human, Mammal))
print ('Instance:', isinstance(Human(), Mammal))
c = Human()
print ('Instance:', isinstance(c, Mammal))
c.eyes("Gray")


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/OOPs Fundamentals - ABC.ipynb".
The first task is:
NOTE

issubclass: Return true if class is a subclass (direct, indirect or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects, in which case every entry in classinfo will be checked. In any other case, a TypeError exception is raised.

isinstance: Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. Also return true if classinfo is a type object (new-style class) and object is an object of that type or of a (direct, indirect or virtual) subclass thereof. If object is not a class instance or an object of the given type, the function always returns false. If classinfo is a tuple of class or type objects (or recursively, other such tuples), return true if object is an instance of any of the classes or types. If classinfo is not a class, type, or tuple of classes, types, and such tuples, a TypeError exception is raised.

Registering the child class
Can you write Python code for it?


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/OOPs Fundamentals - Inheritance.ipynb".
The first task is:
NOTE: Check the init function calls, in the above example.
Can you write Python code for it?

class Parent:

    def override(self):
        print( "PARENT override()")

    def implicit(self):
        print ("PARENT implicit()")

    def altered(self):
        print ("PARENT altered()")

class Child(Parent):

    def override(self):
        print ("CHILD override()")

    def altered(self):
        p = super(Child, self)
        print(type(p))
        print ("CHILD, BEFORE PARENT altered()")
        p.altered()
        print ("CHILD, AFTER PARENT altered()")

dad = Parent()
child = Child()

dad.implicit()
child.implicit()

dad.override()
child.override()

dad.altered()
child.altered()

class Parent:
    x = 10
    def override(self):
        print( "PARENT override()")

    def implicit(self):
        print ("PARENT implicit()")

    def altered(self):
        print ("PARENT altered()")

    def update(self, val):
        self.x = val

class Child(Parent):

    def override(self):
        print ("CHILD override()")

    def altered(self):
        p = super(Child, self)
        print(type(p))
        print ("CHILD, BEFORE PARENT altered()")
        p.altered()
        print ("CHILD, AFTER PARENT altered()")

dad = Parent()
child1 = Child()
child2 = Child()

child1.update(100)
print(child1.x)
print(child2.x)

class Parent:
    x = 10

    def update(self, val):
        self.x = val

class Child(Parent):

    def altered(self, val):
        p = super(Child, self)
        p.update(val)

dad = Parent()
child1 = Child()
child2 = Child()

child1.altered(100)
print(child1.x)
print(child2.x)


I am working on the file "Section 1 - Core Python/Chapter 09 - Classes & OOPS/OOPs Fundamentals - Inheritance.ipynb".
The first task is:
The Reason for super()
This should seem like common sense, but then we get into trouble with a thing called multiple inheritance. Multiple inheritance is when you define a class that inherits from one or more classes, like this:
python
class SuperFun(Child, BadStuff):
    pass
This is like saying, "Make a class named SuperFun that inherits from the classes Child and BadStuff at the same time."
In this case, whenever you have implicit actions on any SuperFun instance, Python has to look-up the possible function in the class hierarchy for both Child and BadStuff, but it needs to do this in a consistent order. To do this Python uses "method resolution order" (MRO) and an algorithm called C3 to get it straight.
Because the MRO is complex and a well-defined algorithm is used, Python can't leave it to you to get the MRO right. Instead, Python gives you the super() function, which handles all of this for you in the places that you need the altering type of actions as I did in Child.altered. With super() you don't have to worry about getting this right, and Python will find the right function for you.
Using super() with init
The most common use of super() is actually in init functions in base classes. This is usually the only place where you need to do some things in a child, then complete the initialization in the parent. Here's a quick example of doing that in the Child:
```python
class Child(Parent):
def __init__(self, stuff):
    self.stuff = stuff
    super(Child, self).__init__()

```
This is pretty much the same as the Child.altered example above, except I'm setting some variables in the init before having the Parent initialize with its Parent.init.
Can you write Python code for it?