Week 24 Sandbox

Python - Arbitrary Keyword Args


        #In addition to positional arbitrary args, Python also provides keyword arbitary args
        >>> def cities_lived(first_name, last_name, **cities):
        >>>     if cities:
        >>>         print(f"{first_name} {last_name} has lived in {len(cities)} cities:\n")
        >>>         for order, city in cities.items():
        >>>             print(f"{order} city: {city.title()}")
        >>>     else:
        >>>         print(f"{first_name} {last_name} has not lived in any cities")

        Tom Markel has lived in 3 cities:

        first city: Chicago
        second city: Portland
        third city: Seattle
        
      

Python - Exceptions


        #Python try-catch is a bit different than in other languages, and instead uses try-except
        #multiple 'except' with different exception types can be defined, as well as an 'else' to execute only if no exception thrown
        #last, typical 'finally' statement can be added to 'try', to execute regardless of if exception thrown or not

        >>> def catch_me(numer, denom):
        >>>     try:
        >>>         int(numer)/int(denom)
        >>>     except ValueError:
        >>>         print("catch_me() call failed due to non-numerical args")
        >>>     except ZeroDivisionError:
        >>>         print("catch_me() call failed due to zero denominator")
        >>>     except:
        >>>         print("catch_me() call failed due to other exception")
        >>>     else:
        >>>         print(f"Divsion {numer} / {denom} = {numer / denom}")
        >>>     finally:
        >>>         print(f"catch_me() call finished")

        >>> catch_me(12, "fish")
        catch_me() call failed due to non-numerical args
        catch_me() call finished

        >>> catch_me(12, 0)
        catch_me() call failed due to zero denominator
        catch_me() call finished

        >>> catch_me(12, 3)
        Divsion 12 / 3 = 4.0
        catch_me() call finished


        #Can assign alias to exception to access various properties of exception
        >>> def zero_catch(numer, denom):
        >>>     try:
        >>>         int(numer)/int(denom)
        >>>     except ZeroDivisionError as zero_error:
        >>>         print("Error: ", zero_error)
        >>>         print("Exception Type: ", type(zero_error))
        >>>         print("Exception args: ", zero_error.args)

        >>> zero_catch(12, 0)

        Error:  division by zero
        Exception Type:  <class 'ZeroDivisionError'>
        Exception args:  ('division by zero',)


        #Can manually throw specified exception with 'raise'
        >>> def check_curse(name):
        >>>     if(name == 'Damien'):
        >>>         raise NameError('Cursed Name')

        >>> try:
        >>>     check_curse('Damien')
        >>> except Exception as exp:
        >>>     print(f'Warning: exception type {type(exp)} thrown. Exception message: {exp}.')

        Warning: exception type <class 'NameError'> thrown. Exception message: Cursed Name.

        
      

PHP - Namespaces


        //Namespaces provide a way to prevent name clashes across files through encapsulation
        //They allow files to be grouped and together, then imported into a file, but each in their own namespace
        //For example, two packages, both with User classes, could exist fine in the same including file without name clash errors occuring
        //Even code without a user assigned namespace still has a namespace in PHP: the global namespaces

        //Here is an example of namespaces. Assume each namespace is also part of a large package based namespace.

        //NamespaceA.php
        <?php
        namespace SmilingStallman\DevBlog\Week24\ASpace;

        class Namespacer{
          public function printNamespace(){
            echo "I am NamespaceA";
          }
        }
        ?>


        //NamespaceB.php
        <?php
        namespace SmilingStallman\DevBlog\Week24\BSpace;

        //same classname as class in NamespaceA.php
        class Namespacer{
          public function printNamespace(){
            echo "I am NamespaceB";
          }
        }
        ?>


        //week24-practice.php
        <?php
        namespace SmilingStallman\DevBlog\Week24;
        include 'week-24-php/NamespaceA.php';
        include 'week-24-php/NamespaceB.php';

        function printNamespace(){
          echo "I am Namespace Week24";
        }

        //unqualified name access - similar to relative filename reference
        printNameSpace();   //"I am Namespace Week24"

        //qualified name access - similar to relative filepath reference
        $a = new ASpace\Namespacer();
        $a->printNameSpace();   //"I am NamespaceA"

        //fully qualified name access - similar to absolute filepath reference
        $b = new \SmilingStallman\DevBlog\Week24\BSpace\Namespacer();
        $b->printNameSpace();   //"I am NamespaceB"

        //'use' combined with aliasing allows namespace importing while also preventing name clashes and provides shorter reference names
        use \SmilingStallman\DevBlog\Week24\Aspace\Namespacer as AspaceClass;
        $a_alias = new AspaceClass();
        $a_alias->printNameSpace();   //"I am NamespaceA"
        //Functions and consts can also be imported into a namespace using the 'function' and 'const' keywords combined with 'use'
        ?>

        
      

PHP - OOP General Practice


        //Lets say you want to design a base Animal class that separates common behaviors from shared behaviors. How could you do this?
        //One way would be to create an interface for each behavior, then implement different implementations of that behavior
        //By adding these behaviors via interface composition, the interface can then be set to any behavior at construction
        //Different Animal subclasses can then also set these behavior objects
        //The result is unique behaviors decoupled from both parent class, and child classes, with a uniform interface

        //Animal.php - the base parent class
        <?php
        abstract class Animal{

          //unique behaviors...note no instantiation here
          protected $moveBehavior;
          protected $noiseBehavior;

          protected function set_move($moveObject){
            $this->moveBehavior = $moveObject;
          }

          protected function set_noise($noiseObject){
            $this->noiseBehavior = $noiseObject;
          }

          //concrete function shared across all classes
          public function eat(){
            echo "Animal is eating";
          }

          //wrappers, separating interface of class from behavior interfaces
          public function makeNoise(){
            $this->noiseBehavior->makeNoise();
          }

          public function move(){
            $this->moveBehavior->move();
          }

        }
        ?>


        //AnimalMove.php - Animal behavior interface
        <?php
        interface AnimalMove{
          public function move();
        }
        ?>


        //AnimalNoise.php - Animal noise interface
        <?php
        interface AnimalNoise{
          public function makeNoise();
        }
        ?>


        //MoveWalk.php - An implementation of move behavior interface
        <?php
        require_once 'AnimalMove.php';

        class MoveWalk implements AnimalMove{
          public function move(){
            echo "I am walking";
          }
        }
        ?>


        //MoveWalk.php - Another implementation of move behavior interface
        <?php
        require_once 'AnimalMove.php';

        class MoveFly implements AnimalMove{
          public function move(){
            echo "I am Flying";
          }
        }
        ?>


        //Bark.php - An implementation of noise behavior interface
        <?php
        require_once 'AnimalNoise.php';

        class Bark implements AnimalNoise{
          public function makeNoise(){
            echo "I am barking";
          }
        }
        ?>


        //Dog.php - An extension of Animal
        <?php
        require_once 'Animal.php';

        //For greater decoupling, could remove default behaviors and set by arg only
        require_once 'Bark.php';
        require_once 'MoveWalk.php';

        class Dog extends Animal{
          public function __construct($noise=null, $move=null){
            $this->set_move($noise == null ? new MoveWalk() : $move);
            $this->set_noise($move == null ? new Bark() : $noise);
          }

        }
        ?>


        //test.php - testing the above
        <?php

        require_once 'Dog.php';
        require_once 'Bark.php';
        require_once 'MoveFly.php';

        //create a standard dog with default behaviors
        $animal = new Dog();

        $animal->makeNoise();   //I am barking
        $animal->move();        //I am walking

        //create a magical flying doge through injecting behaviors
        $bark = new Bark();
        $fly = new MoveFly();
        $another_animal = new Dog($bark, $fly);

        $another_animal->makeNoise();   //I am barking
        $another_animal->move();        //I am flying

        $animal->eat();                 //I am eating
        $another_animal->eat();         //I am eating
        ?>
        
      

Python - Scope & Namespaces


        #Python has local, enclosing, global, and built-in (LEGB) level scope
        #local is the innermost function scope, enclosing is the scope of all functions wrapping the function
        #global is module level Scope, built-in holds Python core names
        >>> scope = "I am global scope"

        >>> def scope_func():
        >>>     scope = "I am enclosing scope for nested"

        >>>     def nested():
        >>>         scope = 'I am local scope for nested'
        >>>         print(scope)                                #I am local scope for nested

        >>>     nested()
        >>>     print(scope)                                    #I am enclosing scope for nested

        >>> scope_func()
        >>> print(scope)                                        #I am global scope


        #Python does not have an equivalent of block level scope,
        #so names defined in 'for', 'while', etc. exist in same scope as statement defined in
        >>> for one in range(1):
        >>>     temp = 'I still exist after this loop'

        >>> print(temp)

        I still exist after this loop. I am a global var.


        #Python name resolution is cascading, checking the innermost scope first, then move up from local->enclosing->global->built-in
        #Note that only resolution is cascading and creating a same name var in an inner scope does not change the outer scope var value
        >>> global_var = 'global'

        >>> def many_scopes():
        >>>     def inner_scope():
        >>>         global_var = "different namespace"
        >>>         def another_scope():
        >>>             print(global_var)

        >>>         another_scope()
        >>>     inner_scope()
        >>>     global_var = "new value"

        >>> many_scopes()
        >>> print(global_var)

        different namespace
        global                  #resolution cascading but assignment not


        #Python allows changing of namespace for variables in global and enclosing/local scope via 'global' and 'nonlocal' keywords
        #'global' switches a local/enclosing variable to global namespace
        >>> x = 'global'

        >>> def global_func():
        >>>     global x
        >>>     x = 'I am in global namespace'

        >>> global_func()
        >>> print(x)

        I am in global namespace


        #Likewise, Python also has nonlocal variables, in which declaring a var nonlocal
        #sets it's namespace to namespace of nearest enclosing function
        >>> def non_local():
        >>>     x = "outermost"
        >>>     def middle():
        >>>         x = "middle"
        >>>         def inner():
        >>>             nonlocal x
        >>>             x = "inner"
        >>>             print(x)    #"inner"

        >>>         inner()
        >>>         print(x)        #"inner" as x inside inner() is in same namespace as middle() due to 'nonlocal'

        >>>     middle()
        >>>     print(x)            #"outermost"
        
      

Python & PHP Notes