Python

From bibbleWiki
Jump to navigation Jump to search

Intro

Python 2 and 3 differences

print "fred" // OK Python 2
print("fred") // Not OK Python 2

Whitespace

Uses full colon and four spaces instead of brackets e.g.

for i in range(5):
    x = i * 10
    print(x)

Rules

  • Prefer four spaces
  • Never mix spaces and tabs
  • Be consistent on consecutive lines
  • Only deviate to improve readability

Help

help(object) gives help. e.g. for the module math

help(math)

Scalar Types, Operators, Control and Other

Types

  • int (42)
  • float (4.2)
  • NoneType (None)
  • bool ( True, False) 0 = False !=0 = True

Operators

  • == value equality
  • != value inequality
  • < less-than
  • > greater-than
  • <= less-than or equal
  • >= greater-than or equal

Control

if statementes

if True: 
    print("Its true")

h = 42
if h > 50: 
    print("Greater than 50")
elif h < 20:
    print("Less than 20")
else:
    print("Other")

while loops

while c != statement0: 
    print(c)
    c -= 1 // c = c-1
    print("Its true")

while True: 
    response = input()
    if int(response) % 7 == 0: 
       break

for loops

cities = ["London", "Paris", "Berlin"]

for city in cities:
  print(city)

Other

Conditional Expressions

No big surprise but

# Condition statement
if condition:
    result = true_value
else: 
    result = false_value

# Condition expression (elvis result ? a:b
# result = true_value if condition else false_value
def sequence_class(immutable)
  return tuple if immutable else list

Lambdas

Lambdas consist of the lambda keyword, argument separated by full colon and expression

lambda arg : expr

e.g.

is_odd = lambda x: x % 2 == 1

Looking a sorted the arguments are

 sorted(iterable, key=None, reverse=False) --> new sorted list

The key argument must be a callable.

scientists = ['Maggie C', 'Albert E', 'Niels B']

# using a lambda, splits the names on space and this result is sorted
sorted(scientists, key=lambda name: name.split()[-1])

# Assigning shows
last_name = lamba name: name.split()[-1]
last_name
<function <lambda> at 0x103011c0
#e.g.
last_name("Fred Bloggs")
'Blogs'

# equivalent to
def first_name(name)
    return name.split()[0]

Data types

Decimal

This can be found in the decimal module and is precise to 28 places. Note the quotes in the examples as using no quotes means we are using floats - arggghhhh

Decimal('0.8') - Decimal('0.7')

# Result 
Decimal('0.1')

# set this to stop usage of float constructors
decimal.getcontext().traps[decimal.FloatOperation] = True

# This will fail
Decimal0.8)

Modulos in python

The standard approach to a%b = r is not how python implement this instead they use b*q + r = a. For example

In c++

#include <iostream>
int main()
{
	auto a = -7;
	auto b = 2;
	auto c = (a) % b;
	std::cout << "c = " << c << std::endl;
}

In python it uses b*q + r = a. See [[1]]

a = -7;
b = 2;
c = (a) % b;
print(c) // 2

str

Double and single quotes are supported. Strings are immutable. Multiline

"""This is 
a multiline
string"""

m = "This string\nspans multiple\nlines"

Raw Strings like c# @

path = r'C:\users\merlin\Documents'

Format string

m = "The age of {0} is {1}".format('Jim', 32)
print(m) //  The age of Jim is 32

# Or without numbers
m = "The age of {} is {}".format('Jim', 32)


# f-strings are like c#
value = 3000
m = f"The value is {value}"

bytes

These work like strings, well ascii strings as and can be created like below

b'some bytpes'
print(b[0]) // 115

decoding to bytes

norsk = "some norsk characters"
data = norsk.encode('utf8')
norwegian = data.decode('utf8')

lists

General

List are a sequence of lists

m = [1,14,5]

// Can be different types
m = ['apple', 7, false]

// Add are mutable
b = []
b.append(1.666)
b.append(1.4444)
print(b) // [1.666, 1.4444]

// Constructor
print(list("characters")) // ['c','h','a','r','a','c','t','e','r','s']

Negative indexing

You can use negative indexing - errrr

s = [3,186,4431,74400, 1048443]
print(s[-1]) // 1048443
print(s[-2]) // 74400

Slicing

Subscript of lists can be achieved with the following

s = [3,186,4431,74400, 1048443]
print(s[1:3]) // 186, 4431
print(s[1:-1]) // 186, 4431, 74400

List Comprehension Syntax

Generally this is

[expr(item) for item in iterable]
words = "Why sometimes I have believed"
print([len(word) for word in words]) // [3,9, 1, 4, 8]

Dict

General

Dict are value pairs

m = {'1': 'Apple', '2': 'Orange'}

print(m['1']) // Apple

# Replaces
m['1'] = 'Banana']
print(m['1']) // Banana

# Update will add if it does not exist or replace
m.update(2:'Applie')

Dict Comprehensions

Like lists above

{expr(key:) expr(value) for item in iterable}
country_to_capital = { 'UK': 'London',
                       'Brazil': 'Brasilia',
                       'Sweden': 'Stockholm' }

capital_to_country = { capital: country for country, capital in country_to_capital.items()}
print(capital_to_country) // {'Brasilia': Brazil, 'London': 'UK', 'Stockholm': 'Sweden'}

Set

Set are values like a dictionary with no key and must be unique

k = {91,109}
k.add(54)

# Error if not found
k.remove(91) 

# No Error if not found
k.discard(91)

With sets we can compare. e.g.

blue_eyes = {'Olivia','Harry', 'Lily', 'Jack','Amelia'}
blond_hair = {'Harry', 'Jack','Amelia', 'Mia','Joshua'}

# Combined
print(blue_eyes.union(blond_hair)) // {'harry','Jack','Amelia','Joshua','Mia','Olivia','Lily'}

# In both
print(blue_eyes.intersection(blond_hair)) // {'harry','Jack','Amelia'}

# Not in this
print(blond_hair.difference(blue_eyes)) // {'Mia','Joshua'}

# Not in other
print(blond_hair.symmetric_difference(blue_eyes)) // {'Mia','Joshua','Olivia','Lily'}

Tuples

Tuples look like lists but have round brackets.

t = ('Apple', 3.5, False)
# to make a single you need to use the trailing comma or it thinks it is a single type e.g.
t = ('Apple',) 
# to index one with pairs use second index e.g
t = ((220,284),(220,285),(220,284),(220,281))
print(t[0][1])

Unpacking like javascript works and swapping

def minmax(items):
    return min(items), max(items)

lower, upper = minmax([83, 33, 84,32, 85, 31, 86])
print(lower) // 31
print(upper) // 86

a = 'Apple'
b = 'Pear'

a, b = b, a

print(a) // Pear
print(b) // Apple

Ranges

Range supports arguments stop, start, stop or start, stop, step. e.g.

# 0-5
range(5) 

# 10-20
range(10,20)

# 10-20 step 2
range(10,20,2)

Iteration

Iterators

Here is how to iterate

s = [1,2,3,4]
myIterator = iter(s)
item1 = next(myIterator)
print(item1) // 1
item2 = next(myIterator)
print(item2) // 2

Generators

Generator functions

This is just like javascript redux stuff

def gen123():
    yield 1
    yield 5
    yield 3

myIterator = gen123()
print(next(myIterator)) // 1
print(next(myIterator)) // 5
print(next(myIterator)) // 3
print(next(myIterator)) // Exception

# Or 
for v in gen123():
    print(v)
...
1
5
3

Generator Expressions

Syntax can be defined as

(expr(item) for item : iterable)
million_squares = (x*x for x in range(1,1000001))

# Generate and output last 10
list(million_squares)[-10:]

# Again will yield nothing
list(million_squares)

Iteration tools

islice

from itertools import count, islice

thousand_primes = islice( (x for x in count() if is_prime(x), 1000)
# thousand_primes is a special islice object which is iterable
# converting to a list
list(thousand_primes)[-10:]
[7841,7853, ..... 7919]
# so to sum first thousand primes
sum(islice( (x for x in count() if is_prime(x), 1000))
3682913

zip

Combine groups together e.g.

sunday = [10,20,30]
monday = [101,201,301]
for item in zip(sunday, monday)
    print(item)
...
(10,101)
(20,201)
(30,301)

Exceptions

General

def convert(s):
    try:
      number = ''
      for token in s:
         number += DIGIT_MAP[token]
      x = int(number)
    # Can be on one line
    # except (KeyError, TypeError):
    except TypeError:
       x = -2
       raise # rethrow
    except KeyError:
       x = -1
       raise # rethrow
    return x

Functions

General Functions

These are created as below

def foo(arg1, arg2):
    return arg1 * arg2

Default Arguments

def foo(arg1, arg2=9):
    return arg1 * arg2

Be aware that the def assignment is only run once. Therefore These are created as below

def add_spam(menu=[]):
    menu.append('spam')

add_spam() // ['spam']
add_spam() // ['spam','spam']

Advice is to make default arguments not mutable. i.e. not strings and not ints

def add_spam(menu=None):
    if(menu==None)
       menu = []
    menu.append('spam')
    return menu

add_spam() // ['spam']
add_spam() // ['spam']

Extended Formal Arguments (params)

Intro

Remember we have positional and keyword arguments in python

Positional arguments

def with an argument prefixed with an asterix means the arguments being passed are a tuple. e.g.

def test(*arg):
    print(args)
    print(type(args))

test(1,2,3)
1,2,3
<class tuple)

Keyword arguments

def with an argument prefixed with two asterix means the arguments being passed are a dict. e.g.

def test(name, **kwargs):
    print(name)
    print(kwargs)
    print(type(kwargs))

test('img', src="monet.jpg", alt="Sunrise by Claude", border=1)
img
{'src':'monet.jpg', 'border':'1', 'alt':'Sunrise by Claude'}
<class dict)

Extended Call Syntax

Equally the calling of functions can use keyword two asterix. Doing so means the positional parameters are satisfied and the remaining parameters are used to make keyword arguments. e.g.

def color(red, green, blue, **kwargs):
    print("r =", red)
    print("g =", green)
    print("b =", blue)
    print(kwargs)

k = {'red': 21,'green': 22,'blue': 23,'alpha': 24, 'beta': 25}

color(**k)

r = 21
g = 22
b = 23
{'alpha' :24, 'beta': 25}

Returning Functions

Intro

In python you can return a function and execute it.

def enclosing():
    def local_function():
        print('Hi')
     return local_function

lf = enclosing()
lf() # // Hi

Factories

We can combine the values are creation of the function with the arguments of the execution of the function. Look at variable exp which is created on execution of raise_to e.g.

def raise_to(exp):
    def raise_to_exp(x):
        return pow(x,exp) 
    return raise_to_exp

myfoo = raise_to(2)
myfoo(10) # // 100
myfoo(5) # // 25

Decorators

Intro

Like c# the functions can be decorated. e.g.

def escape_unicode(f):
    @functiools.wraps(f)
    def wrap(*args, **kwargs):
        x = f(*args, **kwargs) 
        return ascii(x)
    return wrap

@escape_unicode
def northern_city()
    return ;'Troms0' 

northern_city() // "'Troms\\xf8'"

The functools.wrap is necessary to help the support tools such as help.

With parameters

Like typescript you can pass arguments to your decorator by wrapping a decorator in a function and returning the decorator. e.g.

def validator(f):

    # Start of Decorator
    def wrap(*args):
        @functiools.wraps(f)
        if(args[index] < 0:
            raise ValueError("Argument {} must be non-negative.'.format(index))   
         return f(*args)

    return wrap
    # End of Decorator

return validator

@check_non_negative(1)
def create_list(value, size):
    return [value] * size

The functools.wrap is necessary to help the support tools such as help.

Class Decorator

Instances of Classes can be used as Decorators provide they implement the __call__ method

Multiple Decorator

Decorators can be multiple. They are executed in reverse order. i.e. decorator1, decorator2

@decorator1
@decorator2
def northern_city()
    return ;'Troms0'

Modularity

Importing defs

Best to be selective

from words import (fetch_words, print_words)

// could be BAD BAD!!
from words import *

Passing arguments

import sys

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

Comments

def fetch_words(url):
    """Fetch a list of words from a URL.
    
    Args:
        url: The URL of UTF-8 text document.

    Return:
        A list of strings containing the words from
         the document.
    """
    story = urlopen(url)
    story_words = []
    for line in story:
        line_words = line.decode('utf8').split()
        for word in line_words:
            story_words.append(word)
    story.close()
    return story_words

Scope of Objects

Types of Scope

  • Local - Inside current function
  • Enclosing - Inside enclosing function
  • Global - At the top level of the module
  • Built-in - In the special builtins module

Overriding Scope

global

Not using global creates a new count and it shadows the global count.

count = 0

def show_count(): 
    print(count)

def set_count(c)
    global count = c

set_count(5)
show_count()

nonlocal

Where there are functions within functions the nonlocal keyword may be used. e.g.

count = 0

def enclosing(): 
    count = 5
    def local():
       nonlocal count
       count = 25

Objects and Types

Named references to objects

Assigning variables is the same as references. Use id() to prove this.

s = [1,2,3]
r = s
s[0] = 500
print(r)
[500,2,3]
p = [4,5,6]
q = [4,5,6]
print(p == q) // True
print(p is q) // False

Passing Arguments are like references

Passing arguments is like passing references

m = [9,15,24]
def modify(k):
    k.append(39)
    print("k = ", k)

modify(m)
k = [9,15,24, 39]
print(m)
[9,15,24, 39]

Passing Arguments are like references II

Or are they. g is reassigned not mutated

f = [14, 23, 37]
def replace(g):
    g = [17,28, 45]
    print("g = ", g)

replace(f)
g = [17,28, 45]
print(f)
[14,23,37]

Classes

General

class Fight:
    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows

   def registration(self):
       return self._registration

   def model(self):
       return self._model

   def num_rows(self):
       return self._num_rows

Access

There is no public, protected or private in Python

Inheritance

Intro

This is achieved using brackets on the name

class MyBaseClass:
   def registration(self):
       return self._registration

   def model(self):
       return self._model

   def num_rows(self):
       return self._num_rows

class Fight(MyBaseClass):
    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows

Base Class Init

This is not called by default. To call the base class call super. e.g.

class RefridgeratedShippingContainer(ShippingContainer):
    MAX_CELSIUS = 4.0

    def __init__(self, owner, contents, celsius):
        super().__init__(owner, contents)

Factories for Derived Classes

Using extended call arguments we can work around creating derived classes using base class. e.g.

class BaseClass:

    def create_default(cls, attr1):
         return cls(attr1, *args, **kwargs)

    def __init__(self, attr1):
        self._attr1 = attr1

class DervivedClass(BaseClass):

    def __init__(self, attr1, attr2):
        self._attr1 = attr1
        self._attr2 = attr2

f = DervivedClass.create_default('A1','A2')

Static methods

Note if you are calling static methods on classes you should use self and not the class name as this will provide polymorphic behavior unless you do not want this :)


String and Representations

Bit of python up themselves here. Basically repr is for developers and explicit where str is for clients.

class Point2D
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __str__(self):
        return '({}, {})'.format(self.x, self.y)

    def __repr__(self):
        return 'Point2d(x={}, y={})'.format(self.x, self.y)

Properties

Getters and Settters

Not great but this appears to be like this

class MyClass:

    # Getter
    @property
    def myattribute(self)
        return self._myattribute

    # Setter
    @myattribute.setter
    def myattribute(self,value)
        self._myattribute = value

Derived Class Getters and Settters

In derived class the the getter can be overridden by just redefining. Setter requires you to reference the class which contains the property. e.g.

class MyClass:

    # Getter
    @property
    def myattribute(self)
        return self._myattribute

    # Setter
    @myattribute.setter
    def myattribute(self,value)
        self._myattribute = value

class Derived(MyClass):

    # Setter
    @MyClass.myattribute.setter
    def myattribute(self,value)
        if(value > 10):
           raise ValueError("Value out of range")
        self._myattribute = value

Horrible access to base class setter

You can access this be calling the baseclassname.attribute.fset(self,value). Which is horrible like this.

class MyClass:

    # Getter
    @property
    def myattribute(self)
        return self._myattribute

    # Setter
    @myattribute.setter
    def myattribute(self,value)
        self._myattribute = value

class Derived(MyClass):

    # Setter
    @MyClass.myattribute.setter
    def myattribute(self,value)
        if(value > 10):
           raise ValueError("Value out of range")
        MyClass.myattribute.fset(self,value)

__call__

No idea why this is good but essentially it allows to call an instance of an object with no method or rather the method name __call__

class Test
    def __init__(self)
       self._cache = {}

    def __call__(self, arg1)
       if arg1 not in self._cache:
          self._cache[arg1] = socket.gethostbyname(arg1)
          return self_cache[host]

f = Test()
f('bibble.co.nz')

Static Attributes

You qualify the attribute with the class name

class Test:

    a_static = 112 

    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows
       Test.a_static = Test.a_static + 1

Static Method

Intro

These seem very similar. The tutorial said the rule is simple if you need to refer to the class object within the method, e.g. a class attribute, use class method.

@staticmethod

No access needed to either class or instance objects.

class Test:
    
    a_static = 1337

    @staticmethod
    def _get_next_serial():
        result = Test.a_static
        Test.a_static = += 1
        return result

    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows
       Test.a_static = Test._get_next_serial()

@classmethod

Requires access to the class object to call other class methods or the constructor

class Test:

    a_static = 1337

    @classmethod
    def _get_next_serial(cls):
        result = cls.a_static
        cls.a_static = += 1
        return result

    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows
       self.a_static = Test._get_next_serial()

A typical use may be a factory. e.g.

class Test:

    a_static = 1337

    @classmethod
    def create_empty_test(cls):
        return cls("","", 0)

    @classmethod
    def create_default_test(cls):
        return cls("XXX","YYY", 1)

    def __init__(self, registration, model, num_rows)
       self._registration = registration
       self._model = model
       self._num_rows = num_rows
       self.a_static = Test._get_next_serial()

File IO

The with is used like using in c#

Reading

with open('dog_breeds.txt', 'r') as reader:
    # Read and print the entire file line by line
    line = reader.readline()
    while line != '':  # The EOF char is an empty string
        print(line, end='')
        line = reader.readline()

Packages

Packages

Python finds packages by looking at sys.path. You can see this by doing

import sys
sys.path 

# For entry 0
sys.path[0]

# To add you can
sys.path.append('/mypath');

Another approach is to add your path to PYTHONPATH

export PYTHONPATH=$PYTHONPATH:/mypath

Make a Package

mkdir -p /mypath/reader
touch /mypath/reader/__init__.py

For a simple reader class the contents of __init__.py may be (absolute)

from reader.reader import Reader

For a simple reader class the contents of __init__.py may be (relative)

from .reader import Reader

Controlling whats imported

You can do this by specifying the __all__ content. Looks like a def file in windows dlls. e.g.

from reader.compressed.bzipped import opener as bz2_opener
from reader.compressed.gzipped import opener as gzip_opener

__all__ = ['bz2_opener', 'gzip_opener']

Namespace packages

These are packages split across to directories and the root directories do not contain a __init__.py. Importing namespace packages

  • Python scans all entries in sys.path
  • if a matching directory with __init__.py is found, a normal package is loaded
  • if foo.py is found then it is loaded
  • Otherwise, all matching directories in sys.path are considered part of a namespace package
 path1
   | 
   --split_farm
     | 
     -- bovine
       | 
       -- __init__.py
       -- common.py
       -- cow.py
       -- ox.py

 path2
   | 
   --split_farm
     | 
     -- bird
       | 
       -- __init__.py
       -- chicken.py
       -- turkey.py

Executable Directory

You can make a executable by providing a __main__.py in the directory.

  project
  |
  -- __main__.py
  -- project
       | 
       -- __init__.py
       -- stuff.py
  -- setup.py

You can then run the code with

python3 reader

Zipping up the directory and it can be distributed as python treats zips a directories. e.g.

python3 reader.zip