Pages

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

over the time 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 types
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




No comments:

Post a Comment