Pages

Sunday, October 22, 2017

Python Beyond Basics

arithmetic operations
c = a + b   # c = a.__add__(b)
c = a - b   # c = a.__sub__(b)
c = a * b   # c = a.__mul__(b)
c = a / b   # c = a.__div__(b)
c = a % b   # c = a.__mod__(b)
c = a // b  # c = a.__floordiv__(b)
c = a ** b  # c = a.__pow__(b)

c = -a      # c = a.__neg__()

c = a | b   # c = a.__or__(b)
c = a & b   # c = a.__and__(b)
c = a ^ b   # c = a.__xor__(b)
c = ~a     

c = a << 1  # c = a.__lshift__(1)
c = a >> 1  # c = a.__rshift__(1)
===

assignments
Simple statements
---------------------
1. Simple assignment
   a = 10
   b = 20

2. Tuple packing
   a = 10, 20, 30

3. Tuple unpacking
   b, c, d = a

4. Parallel assignment
   a, b = 10, 20

5. Assignment chainloading
   a = b = c = 10
 
6. Augmented assignments
   a += 10
----------------------------
del variable_name

print variable, expression
====

boolean expression
 ==
 != <>
 <
 >
 <=
 >=

 is
 is not

 and
 or
 not

 a == b
a < b
a > b
a <= b
a >= b

a != b
a <> b   # only in python2

a is b
a is not b

element in collection
"with" in "this is a test string with some words"
10 in (33, 44, 10, 44, 33, 44)

not in

not
and
or

conditional expression

a == b
a != b, a <> b
a > b
a < b
a >= b
a <= b

a is b
a is not b

33 in (11, 22, 33, 44, 55, 66)

"test" not in "this is a dummy string"

and
or
not

if not a == b
if a != b
 ======

 boolean context

  0, 0.0, "", (), [], {}, False, None -> False
=====

1. Boolean comparison operators
-------------------------------
    ==
    != <>
    <
    >
    <=
    >=

    is
    is not

    in
    not in

    Examples
    --------
        >>> a = 10
        >>> b = 10
        >>> a == b
        True
        >>>
     
        >>> c = 20
        >>> a > c
        False
        >>> a < c
        True
        >>> a != c
        True

2. Logical evaluation operators
-------------------------------
    and
    or
    not

    Examples
    --------
    >>> a = 10
    >>> b = 20
    >>> c = 30
    >>> b > a and b < c
    True
    >>> a == 10 or b == 100
    True

3. Special boolean operators
----------------------------
    is
    is not

        Example
        -------
        >>> a = "Hello world"
        >>> b = "Hello world"
        >>> c = a
        >>> a is b
        False
        >>> a is c
        True
        >>> a is not b
        True

    in
    not in

        Example
        -------
        >>> a = 33, 44, 55, 32, 12, 56, 77
        >>> 44 in a
        True
        >>> 100 not in a
        True
        >>> 67 in a
        False

4. Boolean context
------------------
false_values = False, None, 0, 0.0, "", (), [], {}
====

built-in function types
1. Type constructors (type conversion functions)
2. Generalized functions (work on broader range of object types)
3. Introspection functions
====

 If you have a quote in the middle of the string? Python needs help to recognize quotes as part of the English language and not as part of the Python language.
>>> “I can’t do that”
“I can’t do that”
 >>> “He said \“no\” to me”
“He said “no” to me”


Conditional statements:
=======================
    ##############################################################
    if [boolean-expression]:
     
    elif [another-boolean-expression]:
           

    else:
       
       
>>> num = 21
>>> if num == 20:
... print 'the number is 20'
... elif num > 20:
... print 'the number is greater then 20'
... else:
... print 'the number is less then 20'
...
the number is greater then 20


    ###############################################################
    if [boolean-expression]:

>>> if num == 10:
...     print("The number is 10")
... 

The number is 10

    ###############################################################
    if [boolean-expression] else

>>> num = 20
>>> if num == 20:
... print 'the number is 20'
... else:
... print 'the number is not 20'
...
the number is 20


Loops:
======
    ###############################################################
    while [boolean-expression]:
       
       
        if [some-condition]: break
        if [some-other-condition]: continue
       
        ....
       
    else:
       
>>> num = 1
>>> while num <= 5:
...     print(num)
...     num += 1
... 
1
2
3
4

5

>>> i = 1
>>> while i < 6:
...     print(i)
...     if i == 3:
...             break
...     i += 1
... 
1
2
3


>>> i = 0
>>> while i < 6:
...     i += 1
...     if i == 3:
...             continue
...     print(i)
... 
1
2
4
5
6

    ###############################################################
    for element in iterable:
       
       
        if [some-condition]: break
        if [some-other-condition]: continue
       
        ...
       
    else:
       

>>> colors = ('red','blue','green')
>>> 
>>> colors
('red', 'blue', 'green')
>>> 
>>> for favorite in colors:
...     print("I love", favorite)
... 
I love red
I love blue

I love green

>>> fruits = ["apple", "banana", "cherry"]
>>> for x in fruits:
...     if x == "banana":
...             break
...     print(x)
... 

apple


>>> fruits = ["apple", "banana", "cherry"]
>>> for x in fruits:
...     if x == "banana":
...             continue
...     print(x)
... 
apple
cherry

Exception handling:
===================
    try:
       
       
    except SomeError as error_object:
       
    except SomeOtherError as error_object:
       
    except Exception as error_object:
       
    else:
       
    finally:
       

Context management:
===================
    with as object:
       
     
Function definition:
====================
    def function_name():
       
>>> def my_function():
...   print("Hello from a function")
... 
>>> my_function()
Hello from a function

>>> 

Class definition:
=================
    class ClassName:
       
       ....

Create a class named Person, use the __init__() function to assign values for name and age:

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

p1 = Person("John", 36)

print(p1.name)
print(p1.age)


=====

Data types

NoneType -> None

bool -> True, False

Numeric
   int, float, long, complex
   fractions.Decimal, fractions.Fraction

Iterable Objects
   Collection
      Sequences            -> Ordered collection of elements
         str (String)      -> "hello", 'hello', """hello""", r'hello'
         unicode           -> u'hello world'
         tuple             -> (10, "hello", 3.4)
-----------------------------------------------------------------------------------
         bytearray         -> bytearray('hello world')
         list              -> [10, "hello", 3.4]
         collections.deque -> deque([22, 33, 44, 55])

      Set                  -> Unordered collection of unique hashable objects
         set               -> {33, 44, 54, 12, 56}

      MappingType          -> Unordered collection of key:value pair
         dict              -> {
                               "name" : "Smith",
                               "city" : "New York",
                               "host" : "44.33.21.45",
                               "port" : 8080
                              }
         collections.OrderedDict

   xrange object
   File objects
   Generators
   Views
====

definitions
1. Assignment statement
-----------------------
a = 100
name = "john"

2. function definition
----------------------
def greet(): print "Hello world"

3. module definition / module loading
-------------------------------------
import sys
from sys import version

4. class definition
-------------------
class Person: pass

To remove variable:
   del variablename
=====

equality
==      a == 10, b == "john"
!=
<
>
<=
>=

in      "john" in names,
not in  10 not in numbers

is      -> check whether two variables refer to same object
is not
========

exception

Parse Errors (errors while parsing the python program):
   - SyntaxError
   - IndentationError

Runtime Errors
--------------
    NameError -> invalid variable name or access to undefined variable
    TypeError -> invalid type detected or object type is incompatible
    ValueError -> incompatible value detected.
    IndexError -> invalid index in a sequence
    KeyError   -> invalid key in a map
    AttributeError -> invalid attribute on an object

    IOError -> invalid file or socket I/O operation
    OSError -> error while executing underlying OS syscall
======

for loop
a = "Hello"
for c in a: print c,

H
e
l
l
o

it = iter(a)

try:
   while True:
      c = it.next()
      print c,
except StopIteration:
   pass
 
Traceback (most recent call last):
  File "", line 3, in
AttributeError: 'str_iterator' object has no attribute 'next' 
=====

Identifiers
In Python, identifiers can be:
  a. variable name
  b. function name
  c. class name
  d. module name
  e. constants

Naming conventions
------------------
1. Variables
   count
   name
 
   word_count
   program_name

2. Functions
   is_prime()
   generate_primes()

   gen_values()

3. Classes
   EmployeeInfo

4. Modules
   sys
   os
   flask

5. Constants
   ---------
   MAX = 100
   MAX_VALUE = 500
====

Immutable types
NoneType (None)
bool (True / False)
numbers (int, float, long, complex)
sequences -> str, unicode, tuple
set -> frozenset
=====

Introspection function
type(variable)
id(variable)
dir(variable)
help(variable)
help(variable.attribute)
====

naming conventions
All variable names should start with lower-case and contain alphabets and _
    max_count
    user_name

functions must start with a verb
    run_command
    start_process
    kill_process
    wait_for_thread

Module names must also start with lower-case and contain alphabets and _
    import time
    import sys

boolean functions start with is_
    is_prime()
    is_lower()

All classes follow CamelCase convention and start with Capital letter
    CustomerOrder
    InvoiceInfo

Constants are all capital letters
    MAX_COUNT
    PI_VALUE
====

1. All variables are references to Objects
2. All identifiers are variables
   a. Function name
   b. Class name
   c. Module name
   d. Other variable names

3. All assignments are "assign by references"
4. Every object is uniquely identified by the object-id.
=====

numeric operater
+
-
*
/
%

**   power
//   floordiv
====

Object types
Immutable objects
Hashable objects
Iterable objects
     Collection
        -> Sequence : An ordered collection of items
             str (string)
             unicode
             bytearray
             tuple           
=====

references
Variables
Attributes of an object
Index of a sequence
Key of a mapping
:
====

sequence operator
+    Concatenation
*    Repetition
%    String format operator
====

sequence function
sum()
max(), min(), all(), any()

sorted(), reversed()

enumerate(), zip(), itertools.izip()

iter()

len()
====

sequences
Sequences -> str, unicode, tuple, list, bytearray, buffer
Sequence operations
-------------------
  +  concatenation
     a = 10, 20, 30
     b = 40, 50, 60
     c = a + b
     print c
     10, 20, 30, 40, 50, 60

  * repetition
     a = 10, 20
     b = a * 5
     print b
     10, 20, 10, 20, 10, 20, 10, 20 ...

  [] indexing
     a[0]
     a[-1]
     a[2:3]
     a[::2]

Sequence methods
----------------
   a.index(element)
   a.count(element)

General sequence functions
--------------------------
len(s)
min(s)
max(s)
all(s)
any(s)

sum(s)

zip(s1, s2)
enumerate(s)

reversed(s)
sorted(s)
=====

statements
1. print tuple          # Prints each element of a tuple as a string
   print "Hello world", "another string", 10, 5.6

2. Assignments
   a = 10               # simple assignment
   a = 10, 20           # tuple packing
   b, c = a             # tuple unpacking
   a, b = 10, 20        # parallel assignment
   a += 5               # augmented assignment
   a = b = c = 10       # assignment chain-loading

3. del variable_or_ref  # Remove a variable or a reference 
   del a                # Remove the variable 'a' and
                        # decrement the reference count
                        # of the object referred by 'a'

4. import module        # Load a module and in the module namespace

   import sys           # Load a module called 'sys'
   sys.version          # Access members of 'sys' module using 'sys' namespace

   import sys as system # Load a module called 'sys' under 'system' namespace
   system.version       # Access member called 'version' from
                        # 'system' namespace

5. from module import member  # Load a module and make members of that module
                              # accessible in the current namespace

   from sys import getsizeof  # getsizeof() function can now be accessed
                              # directly without namespace resolution.


   from sys import getsizeof as sizeof    # import a member named 'getsizeof'
                                          # from module named 'sys' as a
                                            new name 'sizeof'

6. pass                 # Represent a do-nothing statement

7. raise SomeError      # Raise a runtime exception of type SomeError

8. assert expression    # Assert expression to be true
-------------------------------------------------------------------------------
Other simple statements
-----------------------
9.  return value        # Return a value from a function
10. break               # Break out of a while/for loop
11. continue            # Skip to next iteration of while/for loop
12. yield value         # Yield a value to the generator
13. global variable     # Declare a variable in a function to be global
===

special methods
+   __add__
-   __sub__
*   __mul__
/   __div__
**  __pow__
//  __floordiv__
%   __mod__

<<  __lshift__
>>  __rshift__
|   __or__
&   __and__
^   __xor__
~   __neg__
+= __iadd__    # a += 1 --> a.__iadd__(1)
-= __isub__
....
a == b
a.__cmp__(b) == 0

a > b
a.__cmp__(b) > 0

a < b
a.__cmp__(b) < 0
====

strings
a = "He said - I'll be back"
a = 'He said "I will be back" and we waited'
a = "He said - \"I'll be back\" and we waited"
a = 'He said - "I\'ll be back" and we waited'

a = """He said - "I'll be back" and we waited"""

install_path = r'C:\backup\remote\network\temp\assorted'
unicode_data = u'Hello world'
---------------------------------------------------------
String operators
----------------
  %  - String format operator
  +  - String concatenation (from sequence)
  *  - String repetition (from sequence)
  [] - String index operator (from sequence)
 ====

 oop test

 from __future__ import print_function

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

    @staticmethod
    def sell():
        print("Sold this car...")

    @classmethod
    def count(c):
        print("c =", c)

    def drive(self):
        print("Driving", self.name)

c = Car("Honda")
c.drive()
c.sell()
Car.sell()
c.count()
Car.count()

Driving Honda
Sold this car...
Sold this car...
c =
c =
=====

Performance check

"""
A simple python program to test CPU performance by
generating a series of prime numbers
"""
from __future__ import print_function
import sys
if sys.version_info[0] == 2:
    from __builtin__ import xrange as range

NUM_PRIMES = 100000

def is_prime(number):
    "Returns True if 'number' is prime"
    limit = int(number ** 0.5) + 1
    for i in range(2, limit):
        if number % i == 0:
            return False
    return True

def gen_prime(num):
    "Generates first 'num' series of prime numbers"
    i = 2
    while num:
        if is_prime(i):
            print(i)
            num -= 1
        i += 1

from time import time

print("Generating %d prime numbers..." % NUM_PRIMES)
start = time()
gen_prime(NUM_PRIMES)
duration = time() - start
print("gen_prime(%d) took %f seconds" % (NUM_PRIMES, duration))
           
gen_prime(100000) took 12.242300 seconds
=====

list vs tuple vs set 
from timeit import timeit
from sys import argv

sample_size = int(argv[1]) if len(argv) > 1 else 1000

num_iters = 100000
datatypes = "tuple", "list", "set"

setup_code = """
from random import randint
a = {datatype}(range({limit}))
"""
stmt_code = "randint(0, {limit}) in a"

for dt in datatypes:
    duration = timeit(
                  setup=setup_code.format(datatype=dt, limit=sample_size),
                  stmt=stmt_code.format(limit=sample_size),
                  number=num_iters)

    print("Searching for a random element in {} sized {} took {} seconds".format(
                                           sample_size, dt, duration))
                                         
##output                                         
Searching for a random element in 1000 sized tuple took 0.7152920619992074 seconds
Searching for a random element in 1000 sized list took 0.737770362989977 seconds
Searching for a random element in 1000 sized set took 0.14120669901603833 seconds                        ====

Thread performance
from __future__ import print_function
from threading import Thread

from performance import profile_time, print_log
import sys

if sys.version_info[0] == 2:
    range = xrange

def worker(w):
    y = 0
    print("Starting worker: {}\n".format(w), end="")
    for i in range(1000):
        for j in range(10000):
            y += i*j
    print("Worker {} complete: {}\n".format(w, y), end="")

@profile_time
def start_workers():
    pool = { }
    for i in range(16):
        pool[i] = Thread(target=worker, args=(i,))
        pool[i].start()
    print("Created 16 workers...\n", end="")

    for i in range(16):
        pool[i].join()
    print("All workers complete...\n", end="")

start_workers()

print_log()
===

read struct
from __future__ import print_function
import struct

silly_data_format = 'ich64s'
with open("silly_data.dat", "rb") as source:
    data = struct.unpack(silly_data_format, source.read())
    print(data)

#output
(42, b'a', 67, b"hello world from the 'C'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
=====

read line 

import readline

while True:
    line = input("Enter a line: ")
    print(line) 
===

local vs global
a = 10
def foo():
    a = 20
    print("In foo: a =", a)
    print(globals())
    print("-" * 30)
    print(locals())
    globals()['a'] = 40

foo()
print("In main: a =", a)

##output
In foo: a = 20
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external .sourcefileloader="" 0x10821f780="" at="" object="">, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 's1.py', '__cached__': None, 'a': 10, 'foo': }
------------------------------
{'a': 20}
In main: a = 40
====

##scope oo
color = "white"

def start():
    print("Started program...")

class Car:
    color = "red"

    def __init__(self):
        self.color = "green"

    def start(self):
        print("Started car...")

    def drive(self):
        start()
        print("Driving a", color, "car")
        print("Car.color =", Car.color)
        print("self.color =", self.color)

c = Car()
c.drive()

##output
Started program...
Driving a white car
Car.color = red
self.color = green 
=====

a = 10

def foo():
    print("In foo: a =", a)
    a = 40

foo()
print("In main: a =", a)

##output
Traceback (most recent call last):
  File "scope.py", line 7, in
    foo()
  File "scope.py", line 4, in foo
    print("In foo: a =", a)
UnboundLocalError: local variable 'a' referenced before assignment
====

from __future__ import print_function

a = 10

def foo():
    global a
    print("In foo: a =", a)
    a = 20

foo()
print("In main: a =", a)

##output 
In foo: a = 10
In main: a = 20
===

a = [10, 20, 30]

def foo():
    print("In foo: a =", a)
    a[0] = 100

foo()
print("In main: a =", a)

##output
In foo: a = [10, 20, 30]
In main: a = [100, 20, 30]
====

color = "white"

def start():
    print("Starting program...")

class Car:
    color = "blue"

    def __init__(s):
        s.color = "green"

    def start(self):
        print("Starting car...")

    def drive(self):
        self.start()
        print("Driving a", color, "car")
        print("Car.color =", Car.color)
        print("self.color =", self.color)

c = Car()
c.drive()

print(color)
print(Car.color)
print(c.color)

##output
Starting car...
Driving a white car
Car.color = blue
self.color = green
white
blue
green
=====

a = [10, 20, 30]
b = [40, 50, 60]

from itertools import chain

for i in chain(a, b):
    print(i)

##output
10
20
30
40
50
60
====

#print table

num = int(input("Enter a number: "))
for i in 1, 2, 3, 4, 5, 6, 7, 8, 9, 10:
    print(num, "x", i, "=", num*i)
 
##output
Enter a number: 5
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
5 x 6 = 30
5 x 7 = 35
5 x 8 = 40
5 x 9 = 45
5 x 10 = 50
====

print("Hello world")
name = "John"
print("Name is {}".format(name))
print("Square of 2 is", square(2))

def square(x): return x*x
===

from foo import square
print("square of 9 is", square(9))
====

color = "white"

class Car:
    color = "red"

    def __init__(self):
        self.color = "green"

    def drive(self):
        print("Driving a", color, "car")
        print("Car.color =", Car.color)
        print("self.color =", self.color)

c = Car()
c.drive()

Driving a white car
Car.color red
self.color =  green
=====

chaining
a = [10, 20, 30, 40]
b = [50, 60, 70, 80]
from itertools import chain

for i in chain(a, b): print i
====

cyclic reference

class Car:
    def __init__(self):
        print("Car object created...")

    def __del__(self):
        print("Car object destroyed...")

class Person:
    def __init__(self):
        print("Person object created...")

    def __del__(self):
        print("Person object destroyed...")

p = Person()
c = Car()
c.owner = p
p.owns = c

print("Created car and person...")
del c
del p
print("Variables c and p deleted...")
import gc
print(gc.collect())

##output
Person object created...
Car object created...
Created car and person...
Variables c and p deleted...
Person object destroyed...
Car object destroyed...
4
=========

delete 

class Person:

    def __init__(self):
        print("User object created...")

    def __del__(self):
        print("User object destroyed...")

class Car:

    def __init__(self):
        print("Car object created...")

    def __del__(self):
        print("Car object destroyed...")

p = Person()
c = Car()

p.owns = c
c.owner = p

print("Car and Person object created...")

#output
User object created...
Car object created...
Car and Person object created...
User object destroyed...
Car object destroyed...
=======

dict:
user_info = dict(name="John",
                 role="Admin",
                 dept="IT",
                 city="Bengaluru")

for key, value in user_info.items():
    print("{} -> {}".format(key, value))

#or
user_info = {"name": "nawraj","role": "Engineer","dept": "Eng"}

for key, value in user_info.items():
print("{} -> {}".format(key, value))

=====

duck typing

a = [10, 20, 30]
b = (10, 20, 30)

def c():
    print("Hello world")

class d:
    def __init__(self):
        print("Created an object...")

objs = a, b, c, d

print(objs)

def run(tasks):
    for t in tasks:
        if callable(t): t()

run(objs)

#output
([10, 20, 30], (10, 20, 30), , )
Hello world
Created an object...
====

findall 

"""
Preliminary exercise
====================
Implement the findall() function below that must return a list
of indices all occurrence of a substring found in a string
(both passed as arguments to the function).

Example usage:
--------------
   >>> quote = '''
   ... When I see a bird
   ... that walks like a duck
   ... and swims like a duck
   ... and quacks like a duck,
   ... I call that bird a duck
   ... '''

   >>> findall(quote, "duck")
   [37, 59, 82, 107]

"""

def findall(main_string: str, sub_string: str) -> list:
    """
    Returns a list of indices of each occurrence of
    sub_string in main_string

    Example usage:
    --------------
        >>> poem = '''
        ... A fly and flea flew into a flue,
        ... said the fly to the flea 'what shall we do ?'
        ... 'let us fly' said the flea
        ... and said the fly 'let us flee'
        ... and so they flew through a flaw in to the flue.
        ... '''

        >>> findall(poem, 'fly')
        [3, 43, 88, 120]

    """
    indices = []
    i = 0
    while True:
        i = main_string.find(sub_string, i)
        if i == -1: break
        indices.append(i)
        i += len(sub_string)
    return indices

if __name__ == '__main__':
    import doctest
    doctest.testmod()
    # Running this program using 'python3 findall.py' should
    # ideally pass all tests in the doctest - on implementation
    # of findall() function
======

name = "smith"

class User:
    """
    A simple User class

    eoiwru iowe uriowue rio wuerio
    sdlj fskldj fklsdj flksdjfkls

    """
    def __init__(self, name):
        """
            Create a new user object with 'name' passed
            as argument.
        """
        self.name = name

    def greet(self): print(self.name, "says Hello!")

    def welcome(): print("Welcome to Python...")

def square(x):
    "Returns square of argument x."
    return x*x

if __name__ == '__main__':
    print("Main program running...")
    print("a =", a)
    print("name =", name)
    print("square(2) =", square(2))
    u = User("John")
    u.greet()

##otuput 
Main program running...
a = 100
name = smith
square(2) = 4
John says Hello!    
=========

for loop implementation

a = [10, 20, 30, 40, 50]

#for i in a: print i,

iterator = iter(a)
if hasattr(a, '__getitem__') and hasattr(a, '__len__'):
    length = len(a)
    index = 0
    while index < length:
        i = a[index]
        index += 1

if hasattr(a, '__len__'):  # For fixed-length iterable objects
    length = len(a)        # (a.k.a collections/containers)
    while length:
        i = next(iterator)
        print (i),  # 'for' loop body
        length -= 1

else:                      # For generators/views/non-collection iterables
    try:
        while True:
            i = next(iterator)
            print (i),  # 'for' loop body
    except StopIteration:
        pass

#output
10
20
30
40
50
========

class Car:
    def __init__(self):
        print("Created a new car object...")

    def __del__(self):
        print("Car object is being destroyed!")

c1 = Car()
c2 = c1
c3 = c2
print(c1, c2, c3)

print("Changing c2...")
c2 = 100

print("Deleting c1...")
del c1

print("Changing c3...")
c3 = "Hello world"

print("End of program...")

##output
Created a new car object...
<__main__ .car="" 0x109a13780="" at="" object=""> <__main__ .car="" 0x109a13780="" at="" object=""> <__main__ .car="" 0x109a13780="" at="" object="">
Changing c2...
Deleting c1...
Changing c3...
Car object is being destroyed!
End of program...
=====

a = "10"

def foo():
    from builtins import int
    la = a

    for i in range(1000000):
        print("Value of a is", int(la))

foo()
========

def greet():
    print("Hello world")

def greet(user):
    print("Hello", user)

greet("John")
greet()

##output
Hello John
Traceback (most recent call last):
  File "greet_bad.py", line 8, in
    greet()
TypeError: greet() missing 1 required positional argument: 'user'
====

def greet(user=None):
    if user is None:
        print("Hello world")
    else:
        print("Hello", user)

greet("John")
greet()

Hello John
Hello world

========
use of sep

print("Hello world", "Another string", sep="")

## sep is the separator used between multiple values when printing. The default is a space (sep=' ')
===

use fo end

print("Hello world", end="\t")

# Python’s print() function comes with a parameter called ‘end’. By default, the value of this parameter is ‘\n’, i.e. the new line character. You can end a print statement with any character/string using this parameter
===

Infinite loop
from itertools import count
for i in count(10, 2):
    print(i)

# count(start=0, step=1) --> count object

from itertools import count

i = 0
while True:
    print(i)
    i += 1

for i in count():
    print(i)
====

module test

def foo():
    from time import ctime
    print("Time now is", ctime())

def bar():
    from time import ctime
    print("Time now in bar is", ctime())

foo()
bar()

Time now is Fri Oct 20 10:12:17 2017
Time now in bar is Fri Oct 20 10:12:17 2017

## ctime : Convert a time in seconds since the Epoch to a string in local time.
    This is equivalent to asctime(localtime(seconds)). When the time tuple is
    not present, current time as returned by localtime() is used.
 

 














Sunday, October 8, 2017

Linux Command Line

Terminal usage:

[email protected]:/users/nawlekha/Desktop/pyATS$ ls -lh
total 67992
[email protected]  1 nawlekha  staff    19M Mar  2  2017 CodeSchool-TryPython.pdf
-rw-r--r--   1 nawlekha  staff   210B Sep 21 18:01 LEGB.py
drwxr-xr-x  19 nawlekha  staff   646B Feb 28  2017 PyATS online sessions/
[email protected] 45 nawlekha  staff   1.5K Jun 19 10:25 Python 3 for begineers

[email protected]:/users/nawlekha/Desktop/pyATS$ ls -l /var/log
total 73464
drwxr-xr-x   3 root             wheel             102B Apr 13 21:14 Bluetooth/
[email protected]  1 root             wheel              12B Apr 13 21:13 CDIS.custom
drwxr-xr-x   2 root             wheel              68B Aug  6  2016 CoreDuet/

[email protected]:/users/nawlekha/Desktop/pyATS$ ls -l -a -h /var/log or ls -lah
total 73464
drwxr-xr-x  62 root             wheel             2.1K Oct  5 17:37 ./
drwxr-xr-x  28 root             wheel             952B Jul 21 17:29 ../
drwxr-xr-x   3 root             wheel             102B Apr 13 21:14 Bluetooth/
[email protected]  1 root             wheel              12B Apr 13 21:13 CDIS.custom
drwxr-xr-x   2 root             wheel              68B Aug  6  2016 CoreDuet/

Ctrl + A = move to beginning
Ctrl + E = move to end
Clear => clear terminal

Ctrl + U => deletes from cursor to beginning of line

Ctrl + K => deletes from cursor to end of line

Ctrl+Shift+C => Copy to clipboard

Ctrl+ Shift+V => Paste to clipboard

[email protected]:~/Desktop/pyATS$ man ls

~ means home folder

## to access folder name having space
[email protected]:~/Desktop/pyATS$ cd Python training_doc
-bash: cd: Python: No such file or directory
[email protected]:~/Desktop/pyATS$
[email protected]:~/Desktop/pyATS$
[email protected]:~/Desktop/pyATS$ cd Python\ training_doc
[email protected]:~/Desktop/pyATS/Python training_doc$

## to list all folders under directory
[email protected]:~/Desktop/pyATS$ ls -R Python\ training_doc
Foundations of Python Network  Programming, 3rd Edition.pdf  byte_of_python.pdf
PY_diff.txt                                                  dive-into-python3.pdf
Python  Scripting_new.pptx                                   thinkpython.pdf
apr-24.txt

## to represent parent directory of current directory
[email protected]:~/Desktop/pyATS$ cd ..
[email protected]:~/Desktop$

## to move back between folder
[email protected]:~/Desktop/pyATS$ cd -
/Users/nawlekha/Desktop/pyATs
[email protected]:~/Desktop/pyATs$

## this will take you from wherever you are to home folder
[email protected]:~/Desktop/pyATs$ cd
[email protected]:~$

## to create and remove directory
[email protected]:~/DEsktop/pyATS$ mkdir new_folder
[email protected]:~/DEsktop/pyATS$ rmdir new_folder

## in order to remove, folder it should be empty
[email protected]:~/DEsktop/pyATS$ mkdir new_folder
[email protected]:~/DEsktop/pyATS$ mkdir new_folder/hello
[email protected]:~/DEsktop/pyATS$ rmdir new_folder
rmdir: new_folder: Directory not empty

for this we can do rm -r ## delete recursively

## move file to other dir and also to rename files
[email protected]:~/DEsktop/pyATS$ vi forfun.txt
[email protected]:~/DEsktop/pyATS$

[email protected]:~/DEsktop/pyATS$ mv forfun.txt new_folder/
[email protected]:~/DEsktop/pyATS$
[email protected]:~/DEsktop/pyATS$ ls for*
ls: for*: No such file or directory

[email protected]:~/DEsktop/pyATS/new_folder$ ls
forfun.txt  hello/

[email protected]:~/DEsktop/pyATS$ mv new_folder/forfun.txt new_folder/funny.txt
[email protected]:~/DEsktop/pyATS$
[email protected]:~/DEsktop/pyATS$ cd new_folder/
[email protected]:~/DEsktop/pyATS/new_folder$ ls
funny.txt  hello/

## to move folder to current directory
[email protected]:~/DEsktop/pyATS$ mv new_folder/funny.txt .

[email protected]:~/DEsktop/pyATS$ ls fu*
funny.txt

## to move all .txt files to new folder
[email protected]:~/DEsktop/pyATS$ mv *.txt new_folder/

## to move back all files to current dir
[email protected]:~/DEsktop/pyATS$ mv new_folder/* .

## use of ? to delete files. this will delete file name with funny followed by any single character
[email protected]:~/DEsktop/pyATS$ cp funny.txt funny1.txt
[email protected]:~/DEsktop/pyATS$ cp funny.txt funny2.txt
[email protected]:~/DEsktop/pyATS$ rm funny?.txt

[email protected]:~/DEsktop/pyATS$ ls fun*
funny.txt

## find all files in current directory search based on name
[email protected]:~/DEsktop/pyATS$ find . -name "*.txt"
./apr-24.txt
./funny.txt
./Python 3 for begineers/12.3/employees.txt
./python training.txt
./Python training_doc/apr-24.txt
./Python training_doc/PY_diff.txt
./python_training_examples/one.txt
./python_training_examples/output.txt

[email protected]:~/DEsktop/pyATS$ find . -name "PyATS*"
./PyATS online sessions

[email protected]:~/DEsktop/pyATS$ find . -name "*folio*"
./portfolio.csv
./portfolio1.csv

## to search in different folder
[email protected]:~/DEsktop/pyATS$ find ~/ -name "*folio*"
/Users/nawlekha//Desktop/pyATS/portfolio.csv
/Users/nawlekha//Desktop/pyATS/portfolio1.csv
find: /Users/nawlekha//Library/Caches/com.cisco.Jabber/com.apple.opencl: Permission denied
[email protected]:~/DEsktop/pyATS$

## to gain root permission we can use sudo. For first time it will ask for password but there is some grace time within which it won’t prompt for password

[email protected]:~/DEsktop/pyATS$ sudo ls /root
Password: ## user password
ls: /root: No such file or directory
[email protected]:~/DEsktop/pyATS$ sudo ls
.DS_Store missing.csv
CodeSchool-TryPython.pdf new_folder

## to give up privilege
[email protected]:~/DEsktop/pyATS$ sudo -k

## if you want to work as root
[email protected]:~/DEsktop/pyATS$ su root
Password:

Now prompt will change to #

To switch back to $ prompt type exit

## Octal file permissions
file rwxrwxrwx
first rwx represent user or owner of the file
2nd rwx represent group the owner is the member of
3rd rwx represent all other group the owner is not the member of

user rwx 421 = 7
group rx  41 = 5
Other r    4  = 4

## we can change permission of file using chmod command
two ways to do so
-> octal (e.g. 755 644 777)
-> symbolic (e.g. a=r, g+w, and o-x)

user = u ,
to provide user read, write and execute permission: chmod u+rwx
group = g,
to provide group read permission: chmod g=r
others = o,
to remove read, write and execute from others : chmod o-rwx
All = a

+ adds permission; - removes permission
= adds permission but removes others

Comparision of octal and symbolic values

777 or a+rwx : rwxrwxrwx
755 or u+rwx,g=rx,o=rx : rwx-xr-xr
644 or u=rw,g=r,o=r : rw-r- -r- -
700 or u=rwx,g-rwx,o-rwx : rwx- - - - - -

## to run executable file on command line
./file.sh

==> executable means runs on its own without having to be loaded by other program first

[email protected]:~/DEsktop/pYATS$ ./LEGB.py
-bash: ./LEGB.py: Permission denied
[email protected]:~/DEsktop/pYATS$
[email protected]:~/DEsktop/pYATS$
[email protected]:~/DEsktop/pYATS$ ls -lh LEGB.py
-rw-r--r--  1 nawlekha  staff   210B Sep 21 18:01 LEGB.py
[email protected]:~/DEsktop/pYATS$
[email protected]:~/DEsktop/pYATS$
[email protected]:~/DEsktop/pYATS$ python3 LEGB.py
outer x
outer x
[email protected]:~/DEsktop/pYATS$

## still i was able to run file using python3 because it is not run on itself. it is run by other program

## let’s provide read, write and execute permission to user
[email protected]:~/DEsktop/pYATS$ chmod 744 LEGB.py
[email protected]:~/DEsktop/pYATS$
[email protected]:~/DEsktop/pYATS$ ls -lh LEGB.py
-rwxr--r--  1 nawlekha  staff   210B Sep 21 18:01 LEGB.py*
[email protected]:~/DEsktop/pYATS$ ./LEGB.py
./LEGB.py: line 4:
LEGB
Local, Enclosing, Global, Built-in
: command not found
./LEGB.py: line 12: syntax error near unexpected token `('
./LEGB.py: line 12: `def outer():'

## let’s remove read permission for user
[email protected]:~/DEsktop/pYATS$ chmod 244 LEGB.py or chmod u-r LEGB.py
[email protected]:~/DEsktop/pYATS$ cat LEGB.py
cat: LEGB.py: Permission denied
## let’s provide read permission again
[email protected]:~/DEsktop/pYATS$ chmod 755 LEGB.py
[email protected]:~/DEsktop/pYATS$ cat LEGB.py
'''
LEGB
Local, Enclosing, Global, Built-in
'''

#import builtins
#print(dir(builtins))


#x = 'global x'

def outer():
x = 'outer x'

def inner():
#x = 'inner x'
print(x)
inner()
print(x)

outer()

## when a user create a file in home directory, it starts with 664 permission
[email protected]:~/DEsktop/pYATS$ touch newfile
[email protected]:~/DEsktop/pYATS$ ls -lh newfile
-rw-rw-r—  1 nawlekha  staff     0B Oct  5 19:46 newfile

## we can change the ownership of the file
[email protected]:~/DEsktop/pYATS$ sudo chmod root newfile
## after changing ownership we won’t be able to write to it
"newfile" [readonly] 1L, 8C

## lets move back the ownership. Now i am able to write to file
[email protected]:~/DEsktop/pYATS$ sudo chown nawlekha newfile
[email protected]:~/DEsktop/pYATS$ vi newfile

## for changing group ownership we will use chgrp

## command to print whatever you give to it
[email protected]:~/DEsktop/pYATS$ echo "hello"
hello

## use of pipeline
[email protected]:~/DEsktop/pYATS$ echo "hello" | wc
       1       1       6
==> one line of text, one word and six character

[email protected]:~/DEsktop/pYATS$ echo "hello world from the command line" | wc
       1       6      34

## Cat 
concatenate and print files

[email protected]:/users/nawlekha/Desktop/pyATS$ cat comment.py

## to see first 10 and last 10 lines of a file
[email protected]:/users/nawlekha/Desktop/pyATS$ head comment.py
print('Hi i am Nawraj')

if False:
    print('This code will not execute')


if True:
    print('Hello nawraj')

"""
[email protected]:/users/nawlekha/Desktop/pyATS$ tail comment.py

#or

if False: """
def sum(x,y):
    return(x+y)

print(sum(1,2))
"""

[email protected]:/users/nawlekha/Desktop/pyATS$

## we can also customise it.  this will print 1st 5 lines of a file
[email protected]:/users/nawlekha/Desktop/pyATS$ head -n 5 comment.py
print('Hi i am Nawraj')

if False:
    print('This code will not execute')

[email protected]:/users/nawlekha/Desktop/pyATS$

## last 3 lines of file
[email protected]:/users/nawlekha/Desktop/pyATS$ tail -n 3 comment.py
print(sum(1,2))
"""
[email protected]:/users/nawlekha/Desktop/pyATS$

[email protected]:/users/nawlekha/Desktop/pyATS$ cat comment.py | cat -n | tail -n 5
    21     return(x+y)
    22
    23 print(sum(1,2))
    24 """
    25
[email protected]:/users/nawlekha/Desktop/pyATS$ cat comment.py | tail -n 5 | cat -n
     1     return(x+y)
     2
     3 print(sum(1,2))
     4 """
     5

## to view large files
[email protected]:/users/nawlekha/Desktop/pyATS$ less comment.py
press q to quit

## to filter piece of information
grep is used to search files for text that matches a given pattern

[email protected]:/users/nawlekha/Desktop/pyATS$ grep "sum" comment.py
def sum(x,y):
print(sum(1,2))
def sum(x,y):
print(sum(1,2))
[email protected]:/users/nawlekha/Desktop/pyATS$

[email protected]:/users/nawlekha/Desktop/pyATS$ grep -n "sum" comment.py
11:def sum(x,y):
14:print(sum(1,2))
20:def sum(x,y):
23:print(sum(1,2))
[email protected]:/users/nawlekha/Desktop/pyATS$

## grep is case sensitive
[email protected]:/users/nawlekha/Desktop/pyATS$ grep -n "Sum" comment.py
[email protected]:/users/nawlekha/Desktop/pyATS$

## for case in-sensitive we can use -i
[email protected]:/users/nawlekha/Desktop/pyATS$ grep -i "nawraj" comment.py
print('Hi i am Nawraj')
    print('Hello nawraj')
[email protected]:/users/nawlekha/Desktop/pyATS$

## to omit lines which we don’t want to see
[email protected]:/users/nawlekha/Desktop/pyATS$ grep -vi "sum" comment.py
print('Hi i am Nawraj')

if False:
    print('This code will not execute')


if True:
    print('Hello nawraj')

"""
    return(x+y)

"""

#or

if False: """
    return(x+y)

"""

## for regex we can use -E. To fetch he occurrence of letter "hijk"
[email protected]:/users/nawlekha/Desktop/pyATS$ grep -E "[hijk]" comment.py
print('Hi i am Nawraj')
if False:
    print('This code will not execute')
if True:
    print('Hello nawraj')
print(sum(1,2))
if False: """
print(sum(1,2))

## 6 or more any word character
[email protected]:/users/nawlekha/Desktop/pyATS$ grep -E "\w{6,}" comment.py
print('Hi i am Nawraj')
    print('This code will not execute')
    print('Hello nawraj')
    return(x+y)
    return(x+y)
[email protected]:/users/nawlekha/Desktop/pyATS$

## awk and sed
tools to extract or modify text from a file or stream

## print values from 2nd column
[email protected]:/users/nawlekha/Desktop/pyATS$ awk '{print $2}' apr-24.txt

## to sort output based on numbers
[email protected]:/users/nawlekha/Desktop/pyATS$ awk '{print $2 "\t" $1}' apr-24.txt | sort -n

## while awk is used for extracting data, sed is used for modify data
#every occurrence of you is replaced by him
[email protected]:/users/nawlekha/Desktop/pyATS$ sed s/you/him/ funny.txt
hello how r him.
i am looking for him.

## sort
#by default sort is based on the 1st character.
[email protected]:/users/nawlekha/Desktop/pyATS$ sort funny.txt
ahh !! i forgot to call you.
hello how r you.
i am looking for you.

# we can also do sort based on columns and numbers
## sort based on 2nd column numbers
[email protected]:/users/nawlekha/Desktop/pyATS$ sort -k2n simple.txt

### to remove duplicate entry
[email protected]:/users/nawlekha/Desktop/pyATS$ cat funny.txt
hello how r you.
i am looking for you.
ahh !! i forgot to call you.
i am looking for you.
[email protected]:/users/nawlekha/Desktop/pyATS$ sort -u funny.txt
ahh !! i forgot to call you.
hello how r you.
i am looking for you.

## to print file in reverse order
[email protected]:/users/nawlekha/Desktop/pyATS$ rev funny.txt
 .uoy r woh olleh
.uoy rof gnikool ma i
.uoy llac ot togrof i !! hha
.uoy rof gnikool ma i

## to change all lower case to upper
[email protected]:/users/nawlekha/Desktop/pyATS$ cat funny.txt | tr [:lower:] [:upper:]
HELLO HOW R YOU.
I AM LOOKING FOR YOU.
AHH !! I FORGOT TO CALL YOU.
I AM LOOKING FOR YOU.
[email protected]:/users/nawlekha/Desktop/pyATS$

## we have two editors. One is vi and another is nano
nano is by default present in MacOS.
== to write or save file in nano ctl + o
== to exit out of file ctl + x
== ctl + v to move down the screen
== ctl + y to move up the screen
== ctl + a to beginning of line
== ctl + e to end of line

## tar
create and manipulate tape archive files
.tar
.tar.gz
.tgz
.tar.bz2

tar -caf myfiles.tar.gz Exercise\ Files/

## Exercise and Files are are folders we want to compress

## to extract tar file
tar -xf mayflies.tar.gz

## Redirection
(standard input)stdin , keyboard input, descriptor 0
srdout, text on screen, 1
stderr, Error text on screen , 2

[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls
hello/      sample.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls 1>filelist.txt
## here 1 is stdout
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat filelist.txt
filelist.txt
hello/
sample.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$

## we can ommit 1 as well
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls > filelist2.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat filelist2.txt
filelist.txt
filelist2.txt
hello/
sample.txt

## usage of stdr
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls notreal
ls: notreal: No such file or directory

[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls notreal 2> fileslist4.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat fileslist4.txt
ls: notreal: No such file or directory

single “>” will redirect the file
double “>>” will append the file

[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat fileslist4.txt
ls: notreal: No such file or directory
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ >fileslist4.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat fileslist4.txt
## fileslist4 is empty. > has overhidden the file

[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ ls > filelist5.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ echo "and some appended text" >> filelist5.txt
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$ cat filelist5.txt
filelist.txt
filelist2.txt
filelist5.txt
fileslist4.txt
hello/
sample.txt
and some appended text
[email protected]:/users/nawlekha/Desktop/pyATS/new_folder$

Problem: You have received a log file from your admin. In logs there are some breaking attempts by hacker .Your task is
1. Extract the log file
2. look for invalid user authorisation requests
3. Create a file containing the usernames

Solution:
## tar -xvf log.tar.gz
## cat auth.log
## cat auth.log | grep “input_userauth_request” | awk ‘{print $9}’ | sort -u >> users.txt
## cat users.txt

## to find which linux distribution you are using
cat /etc/* -release

In mac we should use
[email protected]:/users/nawlekha/Desktop/pyATS$ uname -a
Darwin NAWLEKHA-M-Q1GZ 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64

mac oS is not linux it is unix variant

## uname -a works in linux too

## in linux to see free memory 
free -h
cat /proc/cpuinfo

## to see space across volumes
df -h

## to see space taken by files and folders
du -h

## “dnf” is used to install software in fedora . It is replacement of yum
eg. sudo def install nano

## in ubuntu we use “apt” to install software
e.g. sudo apt-get update

Quiz.
1. Select the command that gives you the absolute path of the folder where you are currently working.
pwd

2. ls -l shows the permissions and ownership of files, among other things.
Yes, this istrue.

3. What command do you use to create a folder?
mkdir

4. Which of the following commands copies test1.txt to test2.txt?
cp test1.txt test2.txt

5. What character do you use with the find command to match any number of characters?
*

6. What are the two basic user roles in Linux?
normal and superuser

7. "rwxrw-r--" indicates that the owner of the file can read and write, but not execute the file.
Yes, this is false.

8. The command line is a text-based interface to type commands and direct text-based input and output to screen, files, and other programs.
You’re correct!

9. What is the general pattern of a command line statement?
command, options, arguments

10. What does ~ represent in a command line statement?
the user's home folder

11.What is the keyboard shortcut to jump to the end of the command line?
Ctrl + E

12. Which typed statement will show you more information about a particular command?
man [commandName]

13.What is the core Unix philosophy?
Tools should do one thing, and do it well.

14. What is the result of this command: echo "command" | wc
"1 1 8"

15. Select the command that lists the last few lines of a log file.
tail log.txt

16. The grep command prints the lines of text that match a pattern .
You’re correct!

17. Which 'awk' command shows the first column of data from a log file?
wk '{print $1}' logs.txt

18. What are the two modes in Vim?
insertion mode and command mode

19. You can use ^O to save a file in nano
Yes, this istrue.

20. Tar files do not offer compression themselves.
Yes, this istrue.

21. Select the name that has the descriptor of 1.
stdout

22. Which command help you select lines that have specific text you are looking for?
grep

23. Write the command that lists the contents of all the files in the systems /etc folder that end with -release.
cat /etc/*-release

24. This command outputs the RAM value for the local machine
free -h

25. Which command installs nano on Fedora?
sudo dnf install nano


Miscellaneous

Your assistant needs to print directory stack, what would you suggest?
NAWLEKHA-M-N1AJ:bin nawlekha$ dirs
/usr/local/bin

You have a large text file, and you need to see one page at a time. What will you do?
You can achieve the above result by pipeling the output of 'cat file_name.txt' with 'more' command.
[[email protected] ~]# cat file_name.txt | more

What are daemons?
Daemons are services that provide several functions that may not be available under the base operating system. Its
main task is to listen for service request and at the same time to act on these requests. After the service is done, it
is then disconnected and waits for further requests.

You have lots of running jobs, how would you remove all the running processes, without
restarting the machine?
The Linux command 'disown -r' will remove all the running Processes.

A user is new to Linux and he wants to know full list of available commands, what would
you suggest him?
A command 'compgen -c' will show a
full list of available commands.
[[email protected] ~]$ compgen -c

You need to track events on your system. What will you do?
For tracking the events on the system, we need a daemon called syslogd. The syslogd daemon is useful in tracking
the information of system and then saving it to specified log files.
Running „syslogd„ application in terminal generates log file at the location „/var/log/syslog„. The syslogd application is
very useful in troubleshooting Linux sytems. A sample log file looks similar to below.

Tell us the difference between Telnet and SSH?
Telnet and SSH both are communication protocol which are used to manage remote system. SSH is Secured, which
requires exchanging of key opposite of telnet which transmit data in plain text, which means telnet is less secure
than SSH.

What is the difference between command „ping‟ and „ping6′?
Both the commands are same and used for the same purpose except that the fact that ping6 is used with ipv6 IP
address

You want to search for all the *.tar files in your Home directory and wants to delete all at
once. How will you do it?
We need to use find command with rm command to delete all “.tar” files.
# find /home/ -name '*.tar' | xargs rm -rf

Which ports should you open in host firewall for an email server?
Open port 110 which is used by the POP3 e-mail protocol.
Open port 25 which is used by the SMTP e-mail protocol.
Open port 143 which is used by the IMAP e-mail protocol.

List out few of the differences between Softlink and Hardlink ?
a) Hardlink cannot be created for directories. Hard link can only be created for a file.
b) Symbolic links or symlinks can link to a directory.
c) Removing the original file that your hard link points to does not remove the hardlink itself; the hardlink still
provides the content of the underlying file.
d) If you remove the hard link or the symlink itself, the original file will stay intact.
e) Removing the original file does not remove the attached symbolic link or symlink, but without the original file, the
symlink is useless

Differentiate relative path from absolute path
Relative path refers to the path relative to the current path. Absolute path, on the other hand, refers to the exact path
as referenced from the root directory.

What is pid?
Pid is short for Process ID. It is used primarily to identify every process that runs on the UNIX system, whether it
runs on the foreground or runs at the background. Every pid is considered unique.

I want to see how many interfaces (Ethernet cards) are working using single command?
Ifconfig


Sunday, October 1, 2017

Python miscellaneous

Use Procedural Language (if you want to maintain scripts yourself)

- modularity and reusability

object oriented.(suitable for large team)
- individual objects (independent), delegation and concerns, 

Why python?
Implementation of python - Cpython(python written in C) and Pypy (Python written in python)

python is slower if we use Cpython but fast with Pypy, 
code is interpreted while running in python
Python is easy to maintain. data types in python are limitless
python3 is dynamic language. that’s why is used popularly. most errors in python occurs in run-time
Cpython is official maintained by python author
youtube is run on python
Pypy is ongoing program but popular


Scripting languages:
Go language
scala 
Julia
Java
Perl
Python
cobol
C
C++

python2:
range is inefficient to count large numbers (generate and count) ,called as iterator
xrange is used for same in python2 (memory efficient) = on the fly, on demand, called as generator

xrange is replcaed by range in python3

all collections have iterable. 

>>> import sys
>>> sys.getsizeof(a)
72
>>> ## 72 byte
... 
>>> b = xrange(10000000)
>>> sys.getsizeof(b)
40
>>> ## 40 bytes

There is module called “six” which works both in python2 and python3
Also tool called “2to3” to port python2 to python3. This is installed by default while installing python3

nawlekha@NAWLEKHA-M-Q1GZ:~/Desktop/pyATS$ 2to3 -w hello.py

nawlekha@NAWLEKHA-M-Q1GZ:~/Desktop/pyATS$ cat hello.py

## old program will backup with hello.py.bak

dict can use () or {} 

another implementation of python is Jython. python implemented in Java

Iron Python : python on dot-net

Pyjs : Python incorporated in java script

Scipython is replacement of matlab, fortran

In python everything is object. even print() function is object. 
anything that u can name is object

function are first class object in python


>>> a = 10
>>> ## a has address of object or reference of object not value
a is just label
>>> print(a)
10
>>> a = 'hello world'
>>> print(a)
hello world
>>> 

##to copy list  in python 2   either slice or list
>>> a = [10, 20, 30]
>>> b = a[:]
>>> b
[10, 20, 30]

slicing is used to create copy of new object
>>> a = [10,20,30]
>>> b = list(a)

In python3 we use copy
>>> b = a.copy()
>>> b

[10, 20, 30]

object doesn’t have scope but variable have

we can have class inside function
every name in python is variable

## keys are case-insensitive in dict

nawlekha@NAWLEKHA-M-Q1GZ:~/Desktop/pyATS$ python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "hello world"
>>> b = "hello world"
>>> c = a
>>> id(a)
4330160368
>>> id(b)
4330160496
>>> id(c)
4330160368
>>> a == b
True
>>> a == c
True
>>> a is b
False
>>> a is c
True
>>> 
>>> 
>>> 
>>> import sys
>>> sys.getrefcount(b)
2
>>> sys.getrefcount(b) - 1
1
>>> d = a
>>> e =a
>>> e = a
>>> f = a
>>> sys.getrefcount(b) - 1
1
>>> 
>>> 
>>> sys.getrefcount(a) - 1
5
>>> 
>>> 
>>> sys.getrefcount(a) - 1
5
>>> 
>>> 
>>> del c
>>> 
>>> sys.getrefcount(a) - 1
4
>>> 
>>> 


overtime the it will not check object. if it is already there reference will point to same

>>> name = "Nawraj" 
>>> import __main__
>>> __main__.__dict__['name']
'Nawraj'

## string are immutable

>>> a = "hello"
>>> print(a, id(a))
hello 4330156584
>>> a += "world"
>>> print(a, id(a))
helloworld 4330143152
>>> 

## for mutable strings ..use byte array
 >>> a = bytearray(b"hello")
>>> print(a,id(a))
bytearray(b'hello') 4330156752
>>> a += b"world"
>>> print(a,id(a))
bytearray(b'helloworld') 4330156752
>>> 

>>> a = "This is a test string"
>>> b = "This is a test string"
>>> a == b ## based on values
True
>>> 

>>> print(id(a), id(b))
4330151416 4330151488
>>> 
>>> 
>>> print(hash(a), hash(b))  ## hash is calculated based on values.
1551792690137147714 1551792690137147714

in python3 strings is by default unicode data 

>>> a = "hello world"  ## this is unicode 
>>> type(a)

## byte array is unhashable
>>> a = bytearray(b"Hello world")
>>> a = bytearray(b"Hello world")
>>> 
>>> 
>>> hash(a)
Traceback (most recent call last):
  File "", line 1, in
TypeError: unhashable type: 'bytearray'


## collections
3 tpyes
set >> unordered collection of unique hashable objects
>>> a = {10, 20 ,30 ,5 , -1 , 44}
>>> a
{5, 10, 44, 20, -1, 30}
## it does not support item indexing

>>> print(a[0])
Traceback (most recent call last):
  File "", line 1, in
TypeError: 'set' object does not support indexing

## set is mutable. we can add or remove but can't insert. elements in set are hashable
>>> a.add(40)
>>> a.add(50)
>>> print(a)
{5, 40, 10, 44, 50, 20, -1, 30}

>>> a.remove(44)
>>> a.remove(-1)
>>> a
{5, 40, 10, 50, 20, 30}
## used basically for comparision. it’s inefficient using list

complexitiy is O(1)
## element search in set is very fast

>>> a = {10,20,44, 55,25}
>>> b = {65,35,10,20,44}
>>> print(a & b) ## intersection
{10, 44, 20}
>>> print(a -b) ## set difference
{25, 55}
>>> print(a | b ) ## union
{65, 35, 10, 44, 20, 55, 25}
>>> print(a ^ b)  ## symmetric difference
{65, 35, 55, 25}
>>> 
>>> c = {10,20,44}
>>> print(c < a) ## c is subset of a
True
>>> 

>>> print(a > c) ## a is superset of c
True
>>> 

## set is not indexable but it is iterable


## convert list to set to list

>>> a = [10,20,30,40,30,30]
>>> a = list(sorted(set(a)))
>>> a
[10, 20, 30, 40]
>>> 

sequences  >>> str bytes, bytes array, list, tuple, collection.deque, array.array

>>> a = [10,20,30,40,50]
>>> 20 in a
True

>>> c = {"name": "nawraj", "place": "bangalore"}
>>> "place" in c
True
>>> 

complexity is O(n)

## timeit function is used to capture time in python
from timeit import timeit

>>> a = {"name": "nawraj", "role": "Admin"}
>>> b = {"name": "lekhak", "place": "Bangalore"}
>>> print(a.keys() - b.keys())
{'role'}

>>> a = [10,20,30,40,50]
>>> a.insert(1,35)
>>> a
[10, 35, 20, 30, 40, 50]
>>> ## if i want to insert it in such a way list will be in sorted order
... 
>>> 
>>> from bisect import insort  ## takes the midpoint and comapre like binary tree
>>> a = [10,20,30,40,50]
>>> insort (a, 35)
>>> insort (a, 17)
>>> a
[10, 17, 20, 30, 35, 40, 50]
>>> 

we can do append and sort but it is inefficient

>>> a = []
>>> a.append(25)
>>> a.append(35)
>>> a.append(67)
>>> a.append(70)
>>> 
>>> 
>>> a
[25, 35, 67, 70]
>>> a.pop()
70
>>> a.pop()
67
>>> ## adding element in any order and removing in sorted order called priority queue
... 
>>> 

>>> from heapq import heappush, heappop
>>> a = []
>>> heappush(a, 35)
>>> heappush(a, 37)
>>> heappush(a, 40)
>>> heappush(a, 46)
>>> 
>>> 
>>> print(heappop(a))
35
>>> print(heappop(a))
37
>>> print(heappop(a))
40
>>> print(heappop(a))
46
>>> a
[]

mapping >>>

## zip is parallel compare. for parallel iteration

>>> staff = "john", "nawraj"
>>> places = "bangalore", "delhi"

>>> for s,p in zip (staff, places): print(s,p)
... 
john bangalore
nawraj delhi

The goal of __repr__ is to be unambiguous
The goal of __str__ is to be readable

PEMDAS : Parenthesis Exponent Multiplication Division Addition Subtraction

###print
>>> age = 30
>>> name = nawraj
>>> print("%s is %s years old" % (name, age))
nawraj is 30 years old
>>> print('{0} is {1} years old'.format(name, age))
nawraj is 30 years old
>>>

If you are running file directory then file name is __main__
if you are running file by importing first file then it will print first file name

Generator doesn’t hold entire result in memory.
Generator is better with performance

format in custom way:
print("First: {foo}, Second: {bar}".format(bar=22.42,foo=41))

# reuse single. used more than once
print("More: {0:6.2f} or {0:6.3f}".format(5.1234))

# ignore multiple. Extra parameters ignored.
print("More: {1:6.2f} or {1:6.3f}".format(12.345, 5.1234))

Class name
1. private (_)
- begins with single underscore
def _copy(list):

- means used in present module
not available elsewhere
module “import” ignored
internal use only

2. Framework-reserved (_ _)
- Two or more
  must start and end with _
def __init__

pip = pip install packages
Pypi = python package index
PEP : Python enhancement proposal

# class defines methods and data attributes of particular object
# The special __init__ method of class.Its job is to initialize or assign some values to its data attributes whenever we first create object.
# attributes are data we can assign values to and which can be used later.
# methods are used to perform operation on data attributes
# class attributes can be used outside class

lass Employee:
  def __init__(self):
    self.firstname = 'nawraj'
    self.lastname = 'Lekhak'

def main():
  print('This python programming is now starting')

main()

whenever you define main it should be on level of class not within class because it is not class method and considered as main function.

### create class attributes
class Person:
  'Base class of a person'

  # class attributes
  first_name = 'Nawraj'
  last_name = 'lekhak'
  age = 30

def main():

  #print the title of the program

  print("\t *************Welcome to Employee Database ***********")
  print()

  # Print the class attributes values.
  print("First name is:", Person.first_name)
  print("Last name is:", Person.last_name)
  print("Age:", Person.age)
  print() ## to add additional line for seperation

# Call the main method to start the program
main()

##create object based on Employee class

class Employee:
  'Base class of a Employee'

  # set default constructor
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'

def main():
  # create object based on Employee class
  employee_01 = Employee()
  employee_02 = Employee()
  employee_03 = Employee()

##Obtain object attributes
class Employee:
  'Base class for our Employee'

  # set default constructor
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'

  def get_first_name(self):
    return self.first_name

  def get_last_name(self):
    return self.last_name

  def get_uid(self):
    return self.uid

def main():
  # create object based on Employee class
  employee_01 = Employee()

  ## Access and print the first last_name
  print('First name:', employee_01.get_first_name())
  print('Last name:', employee_01.get_last_name())
  print('Uid:', employee_01.get_uid())

# call main method
main()

##Changing object attribute values

class Employees:
  'Base class for our Employees'

  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "n/a" # to reference data attributes
    self.last_name = "n/a"
    self.uid = 'n/a'

def main():
  # create object based on Employees class
  employee_01 = Employees()
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)
  print()

  # Let's modify this object attribute values
  employee_01.first_name = 'Nawraj'
  employee_01.last_name = 'Lekhak'
  employee_01.uid = '388921'

  # Print new assigned values
  print("New Values:")
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)

# start out program
main()

##Acessing object methods

class Employees:
  'Base class for Employees'

  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "Nawraj"
    self.last_name = "Lekhak"
    self.uid = '102'

  ## Getter method
  def get_first_name(self):
    return self.first_name

  def get_last_name(self):
    return self.last_name

  def get_uid(self):
    return self.uid

  ## set method
  def set_first_name(self, xfirstname):
    self.first_name = xfirstname

  def set_last_name(self, xlastname):
    self.last_name = xlastname

  def set_uid(self, xuid):
    self.uid = xuid

def main():
  # create object based on Employees class
  employee = Employees()

  ## access getter methods
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())
  print()

  # Modify object attributes Values
  employee.set_first_name('John')
  employee.set_last_name('Williams')
  employee.set_uid('111')

  # access setter methods
  print("New Values:")
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())

# start out program
main()

## Passing arguments to constructor

class Employee:
  'Base class for Employees'

  def __init__(self, myFirstName, myLastName, myUID):
    self.first_name  = myFirstName
    self.last_name = myLastName
    self.user_id = myUID

  def show_employee_details(self):
    print('First Name:', self.first_name)
    print('Last name:', self.last_name)
    print("user id:", self.user_id)

def main():
  # create new object
  employee = Employee('Nawraj', 'Lekhak','741')
  employee01 = Employee('Ram', 'Bhatt','101')

  # Display employee show_employee_details
  employee.show_employee_details()
  print()
  employee01.show_employee_details()

# start out program
main()

## destructor. Also known as garbage collector.

class MyClass:

  def __init__(self):
    print("Create new object..")

  def __del__(self):
    print("Destroying used object..")

def main():
  # create new object
  object1 = MyClass()
  object2 = MyClass()
  object3 = MyClass()

  print("\n\n") ## create two new line

  del(object1)
  del(object2)
  del(object3)

# start out program
main()

## Public access modifier

class BankAccount:

  def __init__(self, my_initial_balance):
    self.balance = my_initial_balance

  def get_balance(self):
    return self.balance

def main():
  # Create new bank Account.
  account = BankAccount(500)

  #Display the current balance.
  print('Current balance is $', account.get_balance())

  ## Access object attribute value outside class
  account.balance += 800

  print('New balance is : $', account.get_balance())

# start out program
main()

## Private access modifier

class BankAccount:

  def __init__(self, my_starting_balance):
    ## __ to make data attribute private to use within class
    self.__balance = my_starting_balance

  def get_balance(self):
    return self.__balance

  def deposit(self, amount_to_deposit):
    self.__balance += amount_to_deposit

def main():
  # Create new bank Account.
  account = BankAccount(500)

  account.deposit(400)

  #Display the starting balance.
  print('Starting balance is: $', account.get_balance())

  ## This will not be acessible
  print('Starting balance is: $', account.balance())

  account.balance += 4000
  print('Starting balance is: $', account.get_balance())

# start out program
main()

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance

  def get_balance(self):
    return self.__balance

  def deposit(self,amount):
    self.__balance += amount

  def withdraw(self, amount):
    self.__balance -= amount

class SavingsAccount(BankAccount): # child class from BankAccount
  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)

def main():
  # create savings account object
  savings_account = SavingsAccount(200)

  # Display current __balance
  print('SAving account balance $', savings_account.get_balance())
  savings_account.deposit(300)
  print('New account balance $', savings_account.get_balance())

# start program
main()

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance

  def get_balance(self):
    return self.__balance

  def deposit(self,amount):
    self.__balance += amount

  def withdraw(self, amount):
    self.__balance -= amount

class CheckingAccount(BankAccount): # child class

  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)

def main():
  # create checking account object
  checking_account = CheckingAccount(300)

  # Display current __balance
  print('Initial account balance $', checking_account.get_balance())
  checking_account.deposit(300)
  print('New balance with deposit $', checking_account.get_balance())
  checking_account.withdraw(200)
  print('New balance after withdraw $', checking_account.get_balance())

# start program
main()

# Inheritance. Class inherit from another class
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance

  def get_balance(self):
    return self.__balance

  def deposit(self,amount):
    self.__balance += amount

  def withdraw(self, amount):
    if amount > self.__balance:
      print("You do not have enough fund")
    else:
      self.__balance -= amount

def main():
  # create checking account object
  account = BankAccount(100)

  # Display current balance
  print('Initial account balance $', account.get_balance())
  account.deposit(300)
  print('New balance with deposit $', account.get_balance())
  account.withdraw(500)
  print('New balance after withdraw $', account.get_balance())

# start program
main()

# Import. 
import savings_account # name of file without extension
import checking_account

def main():

  # create a saving and checking account

  mySavingsAccount = savings_account.SavingsAccount(100)
  myCheckingAccount = checking_account.CheckingAccount(500)

  # Display initial balance for both account
  print('SAving account balance $', mySavingsAccount.get_balance())
  print('Checking account balance $', myCheckingAccount.get_balance())

# start program
main()

# Overriding parent method within child class with same name. Also called polymorphism. 

class BankAccount:

  def __init__(self,myBalance):
    self.__balance = myBalance

  def get_balance(self):
    print('Calling the get_balance method from "Parent" class')

class CheckingAccount(BankAccount):
  def __init__(self, myBalance):
    BankAccount.__init__(self,myBalance)

  def get_balance(self):
    BankAccount.get_balance(self)## this will call parent class method. commenting this will call child class method
    print('Calling the get_balance method from "Child" class')

def main():
  account = CheckingAccount(50)
  account.get_balance()

main()

f __name__ == ‘__main__’:
       main()
holds true if you are running script directly instead of import some module/file
# original string is always unchanged in python
python doesn’t pass by value or pass by null , it’s pass

python debugger :
python script.py -pdb
import pdb
pdb.set_trace()

Some useful ones to remember are:
b: set a breakpoint
c: continue debugging until you hit a breakpoint
s: step through the code
n: to go to next line of code
l: list source code for the current file (default: 11 lines including the line being executed)
u: navigate up a stack frame
d: navigate down a stack frame
p: to print the value of an expression in the current context
“To return both the key and value in a tuple, you can use the items() function:”
“A decorator is a function that takes one function as input and returns another function

Functions as arguments”
“If you don’t say global within a function, Python uses the local namespace and the variable is local. It goes away after the function completes.
Python provides two functions to access the contents of your namespaces:
locals() returns a dictionary of the contents of the local namespace.
globals() returns a dictionary of the contents of the global namespace”

“Uses of _ and __ in Names
Names that begin and end with two underscores (__) are reserved for use within Python, so you should not use them with your own variables. This naming pattern was chosen because it seemed unlikely to be selected by application developers for their own variables.”
For instance, the name of a function is in the system variable function .__name__, and its documentation string is function .__doc__:”

“Packages"
We went from single lines of code, to multiline functions, to standalone programs, to multiple modules in the same directory. To allow Python applications to scale even more, you can organize modules into file hierarchies called packages.
Maybe we want different types of text forecasts: one for the next day and one for the next week. One way to structure this is to make a directory named sources, and create two modules within it: daily.py and weekly.py. Each has a function called forecast. The daily version returns a string, and the weekly version returns a list of seven strings.
Here’s the main program and the two modules. (The enumerate() function takes apart a list and feeds each item of the list to the for loop, adding a number to each item as a little bonus.)
from sources import daily, weekly”

“You’ll need one more thing in the sources directory: a file named __init__.py. This can be empty, but Python needs it to treat the directory containing it as a package”

“Let’s try again, this time including the special Python object initialization method __init__:
>>> class Person():
...     def __init__(self):
...         pass
This is what you’ll see in real Python class definitions. I admit that the __init__() and self look strange. __init__() is the special Python name for a method that initializes an individual object from its class definition. 1 The self argument specifies that it refers to the individual object itself.
When you define __init__() in a class definition, its first parameter should be self. Although self is not a reserved word in Python, it’s common usage. No one reading your code later (including you!) will need to guess what you meant if you use self.”

Inheritance:
Creating a new class from an existing class but with some additions or changes. It’s an excellent way to reuse code. When you use inheritance, the new class can automatically use all the code from the old class but without copying any of it.
You define only what you need to add or change in the new class, and this overrides the behaviour of the old class. The original class is called a parent, superclass, or base class; the ”“new class is called a child, subclass, or derived class. These terms are interchangeable in object-oriented programming.
So, let’s inherit something. We’ll define an empty class called Car. Next, define a subclass of Car called Yugo. You define a subclass by using the same class keyword but with the parent class name inside the parentheses (class Yugo(Car) below):
>>> class Car():
...     pass
...
>>> class Yugo(Car):
...     pass
...
Next, create an object from each class:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()”

“Without doing anything special, Yugo inherited the exclaim() method from Car. In fact, Yugo says that it is a Car, which might lead to an identity crisis. Let’s see what we can do about that.”

“Use super() when the child is doing something its own way but still needs something from the parent (as in real life).”

“The names of these methods begin and end with double underscores (__). You’ve already seen one: __init__ initializes a newly created object from its class definition and any arguments that were passed in.”

@decorator_function
def display():
print(‘display function ran’)
is same as
display = decorator_function(display)

__name__ : Every module in Python has a special attribute called __name__. It is a built-in variable that returns the name of the module.

__main__ : Like other programming languages, Python too has an execution entry point i.e. main. 'main' is the name of the scope in which top-level code executes. Basically you have two ways of using a Python module: Run it directly as a script, or import it. When a module is run as a script, its __name__ is set to __main__.
Thus,the value of __name__ attribute is set to __main__ when the module is run as main program. Otherwise the value of __name__ is set to contain the name of the module.
__file__ represents the file the code is executing from
os.path.dirname(__file__) gives you the directory the file is in
os.path.pardir stands for ".." which means one directory above the current one
os.path.join(os.path.dirname(__file__), os.path.pardir) joins the directory name and ".."
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) resolves the above path and gives you an absolute path for the parent directory of the directory your file is in

With statement in Python
In Python you need to give access to a file by opening it. You can do it by using
the open() function. Open returns a file object, which has methods and attributes
for getting information about and manipulating the opened file.
With statement
With the "With" statement, you get better syntax and exceptions handling.
"The with statement simplifies exception handling by encapsulating common
preparation and cleanup tasks."
In addition, it will automatically close the file. The with statement provides
a way for ensuring that a clean-up is always used.

Without the with statement, we would write something like this:
file = open("welcome.txt")

data = file.read()

print data

file.close()  # It's important to close the file when you're done with it

With Statement Usage
Opening a file using with is as simple as: with open(filename) as file:
with open("welcome.txt") as file: # Use file to refer to the file object

   data = file.read()

   do something with data
Opens output.txt in write mode
with open('output.txt', 'w') as file:  # Use file to refer to the file object

    file.write('Hi there!')

Notice, that we didn't have to write "file.close()". That will automatically be
called.

The @ symbol is used for class, function and method decorators.

The most common Python decorators you'll run into are:
@property
@classmethod
@staticmethod

Static Methods:
Simple functions with no self argument.
Work on class attributes; not on instance attributes.
Can be called through both class and instance.
The built-in function staticmethod()is used to create them.

Benefits of Static Methods:
It localizes the function name in the classscope
It moves the function code closer to where it is used
More convenient to import versus module-level functions since each method does not have to be specially imported
@staticmethod
def some_static_method(*args, **kwds):
    pass

Class Methods:
Functions that have first argument as classname.
Can be called through both class and instance.
These are created with classmethod in-built function.
 @classmethod
def some_class_method(cls, *args, **kwds):
    pass

##from given array or list [1,3,4,5,0,2] write a function which returns 2nd largest element
>>> def second_largest(given_list):
...     largest = None
...     second_largest = None
...     for current_number in given_list:
...             if largest == None:
...                     largest = current_number
...             elif current_number > largest:
...                     second_largest = largest
...                     largest = current_number
...             elif second_largest == None:
...                     second_largest = current_number
...             elif current_number > second_largest:
...                     second_largest = current_number
...     return second_largest

>>> print(second_largest([1,3,4,5,0,2]))
4

>>> print(second_largest([-2,-1]))
-2

>>> print(second_largest([2, 2, 1]))
2

>>> print(second_largest([2]))
None
>>> print(second_largest([]))
None

>>> 

#Write a function that takes two strings and returns True if they are reverses of each other
>>> def are_reverses(string_1, string_2):
...     for i in range(len(string_1)):
...             i_2 = len(string_2) - i - 1
...             if string_1[i] != string_2[i_2]:
...                     return False
...     return True
... 
>>> print(are_reverses("ABC", "CBA"))
True
>>> 
>>> print(are_reverses("CBA", "AAA"))
False

>>> 

## compare two strings as numbers
>>> def larger_than(a,b):
...     if len(a) > len(b):
...             return True
...     elif len(a) < len(b):
...             return False
...     for i in range(len(a)):
...             if a[i] == b[i]:
...                     continue
...             elif a[i] > b[i]:
...                     return True
...             else:
...                     return False
...     return False
... 

>>> print(larger_than('525','1111'))
False

>>> print(larger_than('6525','1111'))
True


## 2 dimensional arrays
1. as a list of list
>>> a = [[1,3,9,4], [5,0,8,-3]]
or
>>> a = [[1,3,9,4],
...     [5,0,8,-3]]

>>> a
[[1, 3, 9, 4], [5, 0, 8, -3]]

>>> a[0]
[1, 3, 9, 4]
>>> a[1]
[5, 0, 8, -3]

>>> a[0][1] = 100
>>> a
[[1, 100, 9, 4], [5, 0, 8, -3]]
>>> 

Two ways to iterate over a 2D array
1. First way

>>> for row in a:
...     for item in row:
...             print(item)
... 
1
100
9
4
5
0
8
-3

2. 
>>> for i in range(len(a)):
...     for j in range(len(a[i])):
...             print(a[i][j])
... 
1
100
9
4
5
0
8
-3

### you are given a 2D array with the same number of rows and columns. Write a function that adds up the diagonal elements and returns the sum

>>> def diagonal_sum(given_2d):
...     total = 0
...     for i in range(len(given_2d)):
...             total += given_2d[i][i]
...     return total
... 

>>> diagonal_sum([[1,0],[0,1]])
2
>>> diagonal_sum([[1,2,3],[4,5,6],[7,8,9]])
15

### Can rooks attack each other in chess board
>>> def rooks_are_safe(chessboard):
...     n = len(chessboard)
...     for row_i in range(n):
...             row_count = 0
...             for col_i in range(n):
...                     row_count += chessboard[row_i][col_i]
...             if row_count > 1:
...                     return False
...     for col_i in range(n):
...             col_count = 0
...             for row_i in range(n):
...                     col_count += chessboard[row_i][col_i]
...             if col_count > 1:
...                     return False
...     return True


>>> rooks_are_safe([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,0,0]])
True
rooks_are_safe([1])
True
rooks_are_safe([[1,0],[1,0]]) ## they can attack each other
False
rooks_are_safe([[0,0,0],
[1,0,1],
[0,0,0]])
False