Week 22 Sandbox

Python - Sequences (cont.)


        #As comprehensions are expressions, nested iterables can be created by having the expression
        #for a comprehsion also be a comprehsion.
        #This code says, 'for 5 iterations (i in range(5)), create an inner array using comprehension through array [1,2,3,4]).'
        >>> nested_comp = [[x for x in [1,2,3,4]] for i in range(5)]
        >>> print(nested_comp)
        [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]


        #Single item tuples MUST be declared with a trailing common following the item
        ('only one',)


        #Can loop through dictonary as if was ordered by passing into enumerate
        #enumerate() takes in an iterable and returns a tuple with a counter, where counter acts as index value
        >>> my_dict = {'person': 'Tom', 'age': 32, 'city': 'NYC'}
        >>> for index, value in enumerate(my_dict):
        >>>     print(f"{index}: {value}")
        0: person
        1: age
        2: city


        #Can loop through multiple itetables by passing iterables into zip() during loop
        >>> questions = ["Name", "How old", "Resident city"]
        >>> answers = ["Tom", 32, "NYC"]

        >>> for q, a in zip(questions, answers):
        >>>     print(f'{q}? {a}')
        Name? Tom
        How old? 32
        Resident city? NYC


        #reversed() returns a reversed sequence copy of the sequence passed into it
        >>> for i in reversed(questions):
        >>>     print(i)
        NYC
        32
        Tom


        #'in' and 'not in' can be used on all sequences to boolean check existence of value in sequence
        >>> print(f'"Tom" is in "answers" list? {"Tom" in answers}')
        "Tom" is in "answers" list? True


        #Can use comparison operators on sequences, such as lists and dictionaries
        #in such comparisons done lexicographical, comparing 1st index to 1st index, etc. and returing final result
        #for if all ==, all >, etc.

        >>> names = ['Yang Wen-li', 'Reinhard von Lohengramm', 'Siegfried Kircheis']
        >>> dupe__names = ['Yang Wen-li', 'Reinhard von Lohengramm', 'Siegfried Kircheis']
        >>> bad_names = ['Yang Wen-li', 'Reinhard', 'Siegfried Kircheis']
        >>> print(f'{names == dupe__names} {names == bad_names}')
        names == dupe__names? True. names == bad_names? False.

        >>> num_list = [1, 2, 3]
        >>> another_num = [1, 2, 4]
        >>> print(f'''num_list > another_num? {num_list > another_num}
        >>>           num_list < another_num? {num_list < another_num}''')
        num_list > another_num? False
        num_list < another_num? True
        
      

Python - Modules & Packages


        #Python modules are simply scripts that can be imported into other scripts, then had their methods called,
        #vars accessed, etc. from the including script

        #module.py
        >>> def hello_module():
        >>>     print("This function exists in module.py")

        >>> module_var = "I am a module variable."


        #week22.py
        >>> import module
        >>> module.hello_module()
        >>> print(module.module_var)

        This function exists in module.py
        I am a module variable.

        #Can get all names (in array of strings) in module by calling dir(my_module) on imported module)


        #week22.py
        >>> print(dir(module))

        ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
         '__spec__', 'hello_module', 'i', 'module_var']


        #Packages are collections of modules where can import all modules in package in a single export
        my_package/
            __init__.py
            base_module.py
            ...
            inner_pack/
                _init__.py
                inner_module.py


        #Package imports (what to include in package) defined in __init__.py files contained within package/sub-package folders
        #importing via 'from .my_module import *' style imports all modules name into same package level namespace

        #base_module.py
        >>> def print_me():
        >>>     print("I am a module in the base my_package package")

        >>> def _hidden_function():
        >>>     print("I am not available")


        #my_package/__init__.py
        >>> from .base_module import *
        >>> from .inner_pack import *


        #inner_module.py
        >>> def print_me():
        >>>     print("I am a module in the nested package inner_pack")


        #inner_pack/__init__.py
        >>> from .inner_module import *


        #week22.py
        >>> import my_package
        >>> my_package.inner_print_me()


        #Modules can also be imported into private namespaces within a package
        #This prevents nameclashes and huge namespaces, but requires more verbose reference

        #my_package/__init__.py
        >>> import my_package.base_module
        >>> import my_package.inner_pack.inner_module


        #inner_pack/__init__.py
        >>> import my_package.inner_pack.inner_module


        #week22.py
        >>> import my_package
        >>> my_package.base_module.base_print_me()
        >>> my_package.inner_pack.inner_module.inner_print_me()


        #Importing modules into import script using 'from' with module/sub-package specific
        #imports gives each module/sub-package its own namespace in importing file, thus removing need to
        #reference imports via package name

        #week22.py
        >>> from my_package import inner_pack, base_module
        >>> base_module.base_print_me()
        >>> inner_pack.inner_module.inner_print_me()


        #Can also import just specific parts of module

        #base_module.py
        >>> def base_print_me():
        >>>     print("I am a module in the base my_package package")

        >>> def base_hidden_function():
        >>>     print("I am not available")


        #week22.py
        >>> from my_package.base_module import base_print_me
        >>> base_print_me()
        >>> base_hidden_function()

        I am a module in the base my_package package

        Traceback (most recent call last):
          File "week22.py", line 216, in <module>
            base_hidden_function()
        NameError: name 'base_hidden_function' is not defined

        
      

Python - String Formatting II


        #format() allows for formating by passing in arguments into "formate fields," either by index or key
        #As f-strings are a Python 3.6 addition, this will be used for formatting strings in older code
        >>> print('Hello. My name is {1}. I am {0} years old. I live in {city}'.format('32', 'Mark', city='Denver'))
        Hello. My name is Mark. I am 32 years old. I live in Denver


        #Manual string formatting allows another to format strings, and is often used with repr()
        #repr() takes and object and returns a string representation of it
        >>> cities = ['Chicago', 'Portland', 'Denver', 'Seattle']
        >>> print('\nThe cities are: ' + repr(cities) + f'. There are {4} cities in ' + repr(10) + ' states.')

        The cities are: ['Chicago', 'Portland', 'Denver', 'Seattle']. There are 4 cities in 10 states.

        >>> def double(n):
        >>>     return n * 2

        >>> print('\nThree doubled is ' + repr(double(3)) + '\n')

        Three doubled is 6

        >>> for x in range(1, 10):
        >>>     print(repr(x), repr(x*x).rjust(2), repr(x**3).rjust(4))

        1  1    1
        2  4    8
        3  9   27
        4 16   64
        5 25  125
        6 36  216
        7 49  343
        8 64  512
        9 81  729

        
      

Python - File & input() I/O


        #Python's open() method opens a file in a specific read/write mode and assigns the file to a file object
        #Various methods, such as read(), write(), readline(), etc. can then be called on that object for interaction with the file
        >>> file = open('read_me.txt', 'r+')


        #Can read whole file (or up to a max num chars/bytes, if pass in 'size' arg) by calling read() on file object
        >>> print(file.read(150))
        This is the first line

        The second line is a blank line. This is the third line.
        This is the fourth line. It is a long line. Whenever I find myself g


        #Python uses pointer for file interaction. A read(50) reads to the 50th char. A following read() would start at the 51st char.
        #To set the pointer position, use seek(offset, position) where 'position' specifies where to start,
        #and offset moves pointer by n from that char/byte.
        #Note that seeking is limited to only end of file last char and start of file offsets for text file objects
        >>> file.seek(0)
        >>> print(file.readline())

        This is the first line

        >>> file.seek(400, 0)
        >>> print(file.readline())

        er hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street,
        and methodically knocking hats off - then, I account it high time to get to sea as soon as I can.


        #Get current pointer position with .tell()
        >>> print(file.tell())
        621


        #list(file_var) and file_var.readlines() will both return a list of a file, one line per index for text files
        >>> file.seek(0)
        >>> print(repr(list(file)))

        ['This is the first line\n', '\n', 'The second line is a blank line. This is the third line.\n', "This is the fourth line. It is
         a long line. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever
         I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially
         whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately
         stepping into the street, and methodically knocking people's hats off - then, I account it high time to get to sea as soon
         as I can.\n"]


        #Write fo file via write() method which takes in a string for text files or byte object for binary
        #write(), like read, writes from current pointer, so be careful of accidental overwriting
        >>> file.seek(0, 2)
        >>> for line in range(4):
        >>>     file.write(f"I am new line {line}
        >>> ")
        >>> file.seek(0)
        >>> print(file.read())

        This is the first line

        The second line is a blank line. This is the third line.
        This is the fourth line. It is a long line. Whenever I find myself growing grim about the mouth; whenever it is a damp,
        drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the
        rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong
        moral principle to prevent me from deliberately stepping into the street, and methodically knocking hats off -
        then, I account it high time to get to sea as soon as I can.
        I am new line 0
        I am new line 1
        I am new line 2
        I am new line 3


        #Can read user input through use of input(), which pauses script, asks for input, then returns input in string and continues
        >>> age = input("Hello. How old are you?:")
        >>> print(f"Your age is {age}. You will be 100 in {100 - int(age)} years.")
        Hello. How old are you?:
        32
        Your age is 32. You will be 100 in 68 years.
        
      

Python - While Loops


        #While loop is in standard for of 'while condition:'
        >>> message = ''
        >>> while message != 'quit':
        >>>     message = input("Enter a message to be repeated or type quit to exit: ")
        >>>     print(message)

        Enter a message to be repeated or type quit to exit: First message
        First message
        Enter a message to be repeated or type quit to exit: Hey look, another input
        Hey look, another input
        Enter a message to be repeated or type quit to exit: quit
        quit


        #While loops can be used to iterate through a collection, like for loops
        >>> full_list = ["One", "Red", 3, ["Inner", "List"]]
        >>> empty_list = []

        >>> while full_list:
        >>>     empty_list.append(full_list.pop(0))

        >>> print(repr(empty_list))

        ['One', 'Red', 3, ['Inner', 'List']]


        #'while' combined with 'in' and 'remove()' makes a nice way to remove all X from list
        >>> colors = ["red", "blue", "red", "yellow", "black", "red"]

        >>> while "red" in colors:
        >>>     colors.remove("red")

        >>> print(repr(colors))

        ['blue', 'yellow', 'black']

        
      

PHP - Namespaces


        //Namespaces allow you to segregate functions, consts, and classes similar to how you would in a directory structure
        //This prevents nameclashes when using classes, etc. across multiple packages
        //Below is an example of two classes with the same name, both being used in the same requiring class, through seperate Namespaces

        //---dog.php---
        <?php

        //create namespace
        namespace canine\dog;

        class dog{
          private $name;

          function __construct($name){
            $this->name = $name;
          }

          function get_name(){
            echo "dog.php name is: $this->name";
          }
        }
        ?>


        //---canine.php---
        <?php

        //create another namespace
        namespace dog\big;

        //same class name as dog.php
        class dog{
          private $name;

          function __construct($name){
            $this->name = $name;
          }

          function get_name(){
            echo "canine.php name is: $this->name";
          }
        }
        ?>


        //---test.php---
        require_once('dog.php');
        require_once('canine.php');

        //shorten with alias
        use \canine\dog\dog as dogA;
        use \dog\big\dog as dogB;

        $testDog = new dogA("Spot");
        $anotherDog = new dogB("Yang Wen-li");

        echo $testDog->get_name();      //Spot
        echo $anotherDog->get_name();   //Yang Wen-li

        //reference via full namespace
        $longPathDog = new \canine\dog\dog("Killer");
        echo $longPathDog->get_name();  //killler

        
      

Checkpoint

At my current job, the bulk of my work is adding procedural code to existing procedural code. Unfortunately, the codebase is very old, was built with very little design prior, and has very tight coupling and poor encapsulation, single responsibility, etc.. Also, the manager here is very resistant to re-factoring. While new code I write is more organized, with distinct seperation between front-end views and back end implementation, since this is not an OOP shop, my OO is a bit rusty, having not worked much with it since Java in 2014, and some with React and JavaScript last year (before switching to functional programming for front-end shortly after).

Last month, I was put in charge of the long term project of re-designing and re-building our existing primary customer and internal facing API, which is currently a hodge podge of procedural code written by many people over the years. Since I will be doing this OO, I want to brush up on my OO foundations and review design patterns, as well as SOLID principles, like dependency inversion and injection, first.

Thus, I started reading, "Clean Architecture," by Robert C. Martin. A third of the way into it, though, while I found the book to be an excellent quick overview of many design concepts and principles, it was too terse and quick moving for what I was looking for. Then, taking a break from reading this and looking more at PHP frameworks used in API design and implementation, I realized a lot of OO design patterns and principles are covered by frameworks from the get-go (ex. Laravel as an MVC framework, handling dependency injection, autoloading, etc. largely automatically by design).

So, I started studying Laravel. But as I got further into it, again I found myself still wanting more brush-up and knowledge expansion on core OOP, OO design, and design patterns before continuing studying Laravel. Being someone who only knows how to use an implementation of knowledge instead of also holding that knowledge is not something I want to be as a dev, as I believe frameworks and libraries should never act as a substitute for core language and theory knowledge.

Thus, finally, I ended up back at more core OO design and architecture studies as desired before using frameworks which implement them. Given this current path, here is what I'm planning for coming studies:

  1. OOP & OOD - foundations first
    1. "The Object-Oriented Thought Process" --> review of base OOP, why OOP?
    2. "Head First Design Patterns" --> more details and further into OOD
  2. Laravel & PHP - implementation of foundations
    1. Laravel official docs
    2. Laracasts
    3. "Modern PHP: New Features and Good Practices"
  3. API Design - the API itself
    1. Undetermined learning resource(s)
  4. Python - in concurrence with above
    1. Python official docs
    2. "Python Crash Course"
  5. That of course also means that Laravel goes on hold until I have an expanded understanding of the foundations that build the framework's patterns. This is not ideal for while also hunting a new dev job, as I'd like to learn more frameworks, languagues, and libraries immediatly, but I'd still rather go down the right path for full foundational learning, than the rushed path with only partial understanding.

Python & Object Oriented Foundations Review Notes