Python Part 2 - Other Objects

Dave White

Statements

In python, statements are also objects from the statement class.
So far, we've only seen single line statements.
Next we will introduce block-statements which cannot be contained in a single line, but include segments that could be read as a single line.
Block-statements include:

Conditional statements

a=3
b=3
if a==b:
    print("Values are the same")
a=0
b=3
if a==b:
    print("Values are the same")
a=0
b=3
if a==b:
    print("Values are the same")
else:
    print("Values are not the same");
a=3
b=-3
if a==b:
    print("Values are the same")
elif a==(b*-1):
    print("Values have the same magnitude")
else:
    print("Values are not the same");
a=0
b=8
if a==b:
    print("Values are the same")
elif a==(b*-1):
    print("Values have the same magnitude")
elif a==0:
    print("A is zero")
else:
    print("Values are not the same");
a=3
b=3
if a==b:
    print("Values are the same")
elif a==(b*-1):
    print("this is a duplicate entry")

With Strings

name='DNW'
if name=="DNW":
    firist="David"
elif name="JKB":
    first="Justin"
elif name="MLW":
    first="Melanie"
else:
    first='unknown'

Script objects

Script are not block-statements, rather file objects.
Working with block statements becomes tedious working solely in the REPL.
Working from scripts is perhaps preferable from here on out.

Creating a script

Open a new terminal

mkdir myPython
cd myPython
touch myscript.py

python

In your editor of choice open the file you just created

def main():
    print("my first script")


if __name__ == '__main__':
    main()

python

Open a terminal

cd path/to/myscript.py
python myscript.py

python

Details

Simplification:
Your script is an instance of "file class"
name is a special attribute of your script
Its value describes how the file is called
When run directly from a terminal, as a script, (like we did) it takes on the value main
*.py files can be used for other purposes
To ensure it runs the way we want, we always include the

if __name__== '___main___':

python
The body of our script gets put in the 'main()' function.

Try this:

if __name__ == '__main__':
    #main()
    print(__name__)

python

Loop statements

For loops

For loops iterate over collection or iterable types.
On each iteration, some variable takes on a value of the iterator (collection or iterable).

A=[1,2,3]
for i in A:
    print(i)
A=["apple","pineapple","cherry"]
for i in A:
    print(i)

Range type

If you would rather iterate over a sequence, use the range type.

myrange=range(10) # numbers 1 through 9
print(myrange)
class(myrange)
myrange.start()

for i in myrange:
    print(i)

for i in myrange:
    print(i+1)

myrange=range(1,10) # numbers 2 through 9
for i in myrange:
    print(i)

myrange=range(1,10,2) # numbers 2 through 9, every other value
for i in myrange:
    print(i)

The values of range are not generated until needed, thus very large ranges take up a negligible amount of data.

For + Conditional

for i in range(10):
    if (i==0):
        pass
    elif (i==10):
        print("end")
    elif (not i % 2==0) or (i==2):
        print(str(i) + ": odd")
    else:
        print(str(i) + ": even")

Function objects

def seq_even_or_odd(largest):
    for i in range(largest+2):
        if (i==0):
            pass
        elif (i==largest+1):
            print("end")
        elif (not i % 2==0) or (i==2):
            print(str(i) + ": odd")
        else:
            print(str(i) + ": even")

seq_even_or_odd(10)

args

def my_sum(*args):
    out = 0
    for x in args:
        out += x
    return out

my_sum(1,2,3)

kwargs

def my_sum(**kwargs):
    kwargs.values()
    vals=list(kwargs.values())
    keys=list(kwargs.keys())
    for i in range(len(kwargs)):
        print(vals[i])
        print(keys[i])

my_sum(abc=3)
def my_w_comb(*args,**kwargs):
    kwargs.values()
    vals=list(kwargs.values())
    keys=list(kwargs.keys())

    wSum=1
    wProd=0

    for i in range(len(kwargs)):
        if keys[i] == "plus":
            wSum=vals[i]
        elif keys[i]=="prod":
            wProd=vals[i]
    result=0;
    for i in range(len(args)):
        result=(wSum+args[i])*(wProd)

    return result

my_w_comb(1,2,3,4, plus=3,prod=2)

Default values

def print_info(name, lang="python", version="3.7"):
    print("Using function " + name + " with " + lang + " version " + version)

print_info('my_w_comb')
print_info("fmincon","matlab","R2017")

Optional

def print_info(name, lang="python", version=None):
    if not version:
        print("Using function " + name + " with " + lang)
    else:
        print("Using function " + name + " with " + lang + " version " + version)

print_info('my_w_comb')
print_info("fmincon","matlab","R2017")

Module objects

A collection of file objects can be encapsulated together as a module.
Modules are features that can be added to python.
Modules can be further encapsulated by nesting as submodules.
A module or group of modules can encapsulated into a package object for easy distribution.
Popular modules find their way packages into an centralized online repository accessed via pip.
Anybody can create packages and submit their code to be included in the central pip repo.

Once a package is installed, they can be loaded into the REPL or script via the import command.

datetime is a module that comes pre-installed with python.
numpy is not

import datetime
print(datetime.date.today())
type(datetime)

import datetime as dt
print(dt.date.today())
type(dt.date)
dir(dt)

from datetime import date
print(date.today())
type(date.today)

from datetime import *
print(date.today())
print(timezone.utc)

from datetime import date as d
print(d.today())
type(d)

Classes

Creating


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

#-----------------------------------

def main():
    a=experiment("test")
    print(a)
    print(a.name)

#-----------------------------------
...

Constructor

init is the name binding for the constructor method.
Reminder: constructor is called whenever an instance is created
All classes have constructors, but explicit creation is optional

dunder = double underlines
dunder attributes and methods are special in some way.

A=[1,2,3]
dir(A)

We can call on the constructor again by

A.__init__((2,3,4))

But will only reconstruct if the type is mutable (which is true in this case).

self

'self' is a special binding that refers to the object itself.
A function's signature is the argument types that are relevant.
init as a function must determine which instance of init it must call.
This process is called dispatch.
In python the first argument "self" defines the signature.
Because only one argument defines signatures in python, python is called a single dispatch language

Calling

a.__init__("diffname")

is equivalent to

__init__(a,"diffname")

Although python has disabled this type of syntax to keep things clear.
Thus in python, when calling an object the first argument is implied by the object itself.

methods

class experiment:
    def __init__(self,name):
        self.name=name
        self.get_date()

    def get_date(self):
        self.date=date.today()
class experiment:
...

files

Class definitions should go into their own file

touch __init__.py # create package
touch myClasses.py # class definitions file

in init.py

import .myClasses.myClasses

move datetime import and class to new file
save both
try running

from myClasses import experiment as exp

change experiment to exp

cmd arguments

In myScript.py

import sys

def main(name):
    if not name:
        name=None
    a=exp(name)
    print(a.name)


if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(sys.argv[1])
    else:
        main()

in terminal

python myScript.py Dave

Second class

in myClasses.py

from datetime import date

class subject:
    def __init__(self,name=None,age=None,height=None,weight=None):
        if type(age)==str:
            age=float(age)

        if type(height)==str:
            height=float(height)

        if type(weight)==str:
            weight=float(weight)

        self.name=name
        self.age=age
        self.height=height
        self.weight=weight

    def get_bmi(self):
        if not self.weight or not self.height:
            return None

        return self.weight/(self.height^2) * 703

class experiment:
    def __init__(self,subject=None):
        if subject == None:
            error('no subject defined')
        self.subject=subject
        self.get_date()

    def get_date(self):
        self.date=date.today()

in myScript.py

import sys
from myClasses import experiment as exp
from myClasses import subject as subj

def main(*argv):
    mysubj=subj(*argv)
    myexp=exp(mysubj)
    print(myexp.subject.name)
    print(myexp.subject.get_bmi())


if __name__ == '__main__':
    main(*sys.argv[1:])

in Terminal

python myScript.py Dave 32 5.11 200

Environemnts

TODO

pyenv

TODO

VirutalEnv

TODO