Reviewing Python Part 2

Reviewing Python with *Learning Python*, 5th ed., by Mark Lutz part 2. This is the end of my review of pytrhon. I am going to review numpy, pandas, and matplotlib next before returning to

Reviewing Python with Learning Python, 5th ed., by Mark Lutz

Statements ans Syntax

Python is normally coded one statement per line and indent all the statements in a nested block the same amount (indentation is part of Python's synatx)

# Assignments
smap = 'Spam'
spam, ham = 'spam', 'ham' # Tuple assignment
[spam,ham] = ['yum','YUM'] # List assignment
a,b,c,d = 'spam' # Sequence assignment
a, *b = 'spam' # Extended sequence unpacking
spam = ham = 'lunch' # multi target assignment
spam += 42 # Augmented assignment
[] or 3 # return left operand if true
[] or {} # Else return right operand
[] and {} # Return left operand if false
3 and [] # return right operand
# Ternary Operator
Y = 2
X = []
Z = [1,2,3] 
A = Y if X else Z # [1,2,3]
B = Y if Z else X # 2

while and for Loops

Iterations and Comprehensions

# Nested List Comprehension
[x+y for x in 'abc' for y in 'lmn']

Functions and Generators

Arguments are passed by position, unless you say otherwise. Values you pass in a function call match argument names in a function's definiton from left to right by default/ Function calls can also pass arguments with name=value keyword syntax.

Polymorphism: functions shouldn't care about the types of the arguments

Variables defined inside function are called local variables - the variable is visible only to code inside the function def and exists only while the function runs

By default, all names assigned inside a function are associiated with that function's namespace, and no other. This means that:

Scope Detais

LEGB rule: when you use an unqualified name inside a function, Python searches up four scopes: the local (L) scope, then the scope of any enclosing (E) defs and lamndas, then the global (G) scope, and then the built-in (B) scope

Most statement blocks and other constructs do not localize the names used within them, with the following exceptions:

The global statement is a namespace declaration. Global allows you to use a global variable inside a function. The nonlocal statement only has meaning inside a function. It allows you to change one or more names defined in a syntactically enclosing function's scope. Nonlocal names must have been previously defined in an enclosing def when the nonlocal is reached, or an error is raised.

Argument passing basics

You can return multiple results from a function by wrapping them in a tuple or other collection type
Argument Matching Basics

Using *iterable or **dict in a call alows us to package up arbitrarily many positional or keyword arguments in sequences (and other iterables) and dictionaries, respectively, and unpack them as seperate, individual arguments when they are passed to the function

def f(a,*pargs,**kargs):
  print(a,pargs,kargs)

f(1,2,3,x=1,y=2)
# 1 (2, 3) {'x': 1, 'y': 2}
def gensquares(N):
  for i in range(N):
    yield i ** 2
for i in gensquares(5):
  print(i,end=", ")

Look at the time module to perform benchmarking

Modules and Packages

Classes and OOP

OOP is mostly about an argument names self and a search for attributes un trees of linked objects called inheritence. Objects at the bottom of the tree inherit attributes from objects higher up in the tree - a feature that enables us to program by customizing code, rather than changing it or starting from scratch

class FirstClass:
    def setdata(self,value):
      self.data = value
    def display(self):
      print(self.data)
x = FirstClass()
y = FirstClass()
x.setdata(2)
x.display()
class SecondClass(FirstClass):
  def display(self): # this method overrives FirstClass.display since it is found first
    print("The current value = '%s'" % self.data) 
a = SecondClass()
a.setdata(10)
a.display()
class ThirdClass(SecondClass):
  def __init__(self,value): # on ThirdClass(value)
    self.data = value
  def __add__(self,other): # on self + other
    return ThirdClass(self.data+other)
  def __str__(self): # On print(self), str()
    return '[Third Class %s]' % self.data
  def mul(self,other): # In-place change: named
    self.data*=other
c = ThirdClass(5)
(c+10).display() # The current value = '15'
c.mul(10) 
c.display() # The current value = '50'
print(c) #