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 are statements that assign an object to a name (variable)
- Assignments create object references
- Names are created when first assigned
- Names must be assigned before being referenced
- Some operations perform assignments implicity
# 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
- Sequence assignment: the same number of items must be on the left and right of the = except when using the unpacking * syntax
- The unpacking sytax can be usd anywhere in the target (the stuff on the left side of the equal sign)
- A multi target assignment assigns all the given names to the object all teh way to the right
Print Keyword Arguments- Keyword Arguments - you use a special "name=value" syntax to pass the arguments by name instead of position
- sep: string inserted between each objects text, which default to a single space if not passed
- end: the string that is added to teh end of the printed text, defaults to '\n'
- file: specifies the file, standard output stream, or other file-like object to which the text will be sent (defaults to sys.stdout) ,file=open('data.text','w')
- flush: aklkiws prints to mandate that their text be flushed through the output stream immediately to any waiting recipients
- There are no switch statements in python
Syntax Revisited - Statements execute one after another, until you say otherwise
- Block and statement boundaries are detected automatically
- Blank lines, spaces, and comments are usually ignored (Spaces matter in terms of indentation)
- Docstrings are ignored but are saved by tools
Truth and Boolean Tests - All objects have an inherent boolean true or false value
- Any nonzero number or nonempty object is true
- Comparisons and equality test are applied recursively to data structures
- Comparisons and equality test return True or False
- Boolean and and or operators return true or false operand object
- Boolean Operators (Below) stop evaluationg as soon as the result is known
- X and Y
- X or Y
- not X
[] 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
while loops repeatedly execute a block of (normally indented) statements as long as a test at the top keeps evaluating to a true value
break a loop with the break statement
continue: jumps tothe top of the closest enclosing loop
pass: does nothing at all: its an empty statement placeholder
Loop else block: runs if and only if the loop is exited normally
for loops step through the items in any ordered sequece or other iterable object
The built in range function produces a series of successively higher integers, which an be used as indexes in a for
The built in zip function returns a series of parallel-item tuples, which can be used to traverse multiplse sequences in a for
- zip truncates to the shorter iterable
- produces an vakue generator (iterable) of tuples
The built in enumerate function generates both values in indexes for items in an iterable
Iterations and Comprehensions
- Any obect with a __next__ method to advance to the next result, which raises a StopIteration at the ned of a series of results, is considered an iterator in Python
- The next() built in function calls the __next() methos on an object
- The iter() function returns the iterator for an object
- List comprehension syntax is derived from a construct in set theory notation that applies an operation to each item in a set, but you don;t have to know set theory to use this tool
# Nested List Comprehension
[x+y for x in 'abc' for y in 'lmn']
- Other python iterables:
- sorted sorts items in an iterable
- zip combines items from iterables
- enumerate pairs items in an iterable with relative positions
- filter selects items for which a function is true
- reduce runs pairs of items in an iterable through a function
- Other:
- min: finds the min value in an iterable
- max: finds the max value in iterable
- sum: sums all the numbers in an iterable
- any: retruns true if any vals in an iterable return true
- all: returns true if all vals in iterable return true
Functions and Generators
- A function is a device that groups a set of statements so they can be run more than once in a program - a packaged procedure inoked by name
- Functionas allow for code reuse and procedure decomposition (split code into smaller pieces of code that have well defined roles)
def - is executable code
- creates an object and assigns it a name when it is encountered
lambda- creates an object but returns it as a result
return
- creates an object but returns it as a result
- sends a result back to the caller
yield - sends a result object back to the caller, but remembers where it left off
global - declares module-level vaiables that are to be assigned
nonlocal - declares enclosing function variables that are to be assigned
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:
Names assigned inside a def can only be seen by the code within that def. You cannot even refer to such names from outside the function.
Names assigned inside a def do not class with variables outside the def, even if the sam names are used elsewhere. A name X assigned outside a given def is completely different variable from a name X assigned inside that def
If a variable is assigned inside a def, it is local to that function
If a variable is assigned in an enclosing def, it is nonlocal to nested functions
If a variable is assigned outside all defs, it is global to the entire file
Scope Detais
- The enclosing module is a global scope
- TRhe global scope spans a single file only
- Assigned names are local unless declared global or nonlocal
- All other names are enclosing function locals, globals, or built ins
- Each call to a function creates a new local scope
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:
- Comprehension variables - the variable X used to refer to the current iteration ite, in a comprehension expression such as [X for X in I]
- Exception variables - the variable X used to reference the raised exception ina try statement handler clause such as except E as X
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
- Arguments are passed automatizally assigning objects to local variable names
- Assigning to argument names inside a function does not affect the caller
- Changing a mutable object argument in a function may impact the caller
- Immutable arguments are effectively passed "by value"
- Mutable arguments are effectively passed "by pointer"
You can return multiple results from a function by wrapping them in a tuple or other collection type
Argument Matching Basics
- Positionals - match arguments from left to right
- Keywords - match arguments by argument name
- Defaults - specify values for optional arguments that aren't passed
- Varrgs collecting = collect arbitrarily many positional or keyword arguments
- Varargs unpacking = pass arbitrarily many positional or keyword arguments
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}
- Generator functions are coded as normal def statements, but use yield statements to return results one at a time, suspending and resuming their state between each
- Generator expressions are similar to the list comprehensions of the prior section, but they return an object that produces results on demand instead of building a result list.
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
- The Python module is the highest level program organizational unit, which packages program code and data for reuse, and provides self-contained namespaces that minimize variable name clashes across your programs.
- Modules are processed with two statements and one import function:
- import: lets a client fetch a module as a whole
- from allows clients to fetch particular names from a module
- imp.replad provides a way to reload a moudle's code without stopping Python
- modules have three uses:
- Code reuse
- System namespace partioning
- Implementing shared services or data
- How Imports Work
- Find the module's file
- Compile it to byte code (.pyc files stored in pycache directory)
- Run the module's code to build the objects it defines
- When we use the import * statement when importing, we get copies of all names assigned at the top level of the referenced module
- imports are only run once
- import statements may be nested
- Using import * or from X_MODULE import name, you might corrupt your namespace
- Get namespace dictionarites with __dict__
Classes and OOP
Object Oriented Programming (OOP) offers a different and often more efficient way of programming in which we factor code to minimize redundancy and write new programs by customizing existing code instead of changing it in place
Inheritence: inheriting properties and methods from a parent class
Composition: different classes for different functionality coming together in a larger class
Classes
- serve as instance factories. Their attributes provide behavior - dat and functions - that is inherited by all the instances generated from them
Instances
- Represent the concrete items in a program's domain. Their attributes record ata that varies per specific object
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
- Each time you call a class, you get an instance of that class
- The class statement creates a clas object and assigns it a name
- Assignments inside class statements make class attributes
- Class attributes provide object state and behavior
- Classes can override built in type operations
- define a __add__ methos to configure how class responds to +
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) #
- I have experience with OOP from lexical - might want to implement more OOP on node server. Might be helpfule for database stuff (retreiving, cleaning, and validating data, controlling properties all in one place, just less code in general)