Topic outline

  • Kia ora Guest user

    Welcome to CGI506 Technical Development 1, S2-20. In this course you will be developing basic skills and knowledge of programming for animation and game development and to develop custom tools and functions for a successful production pipeline.

    On completion of this course you will be able to:

    1. Investigate and compare different programming languages used for animation, visual effects and real time applications. 
    2. Evaluate the effectiveness of different scripting/programming languages for selected applications. 
    3. Select and use a number of different scripting languages to achieve the desired effects and tools.  
    4. Create custom tools to facilitate a production scenario. 

    Alongside the class time and your project work, you will be provided with extra learning material, videos and tasks to help you improve your skills.  Work at your own pace spending roughly 4 hours a week on these. 

    All courses in this programme include an assessment of professionalism. Professionalism includes your active engagement in class activities, your ability to communicate with your peers and tutor, how you work in a team and more importantly how you manage your self.

    Usually we cover one session per week. Note this course content and schedule, may be adjusted as we understand more about our needs as a group of learners. 

  •  

    In this session we will be looking at how to document your code, then moving onto one of our most advanced topics so far, which is Object Oriented Programming.

    Docstrings and Documenting your code:

    In Python it is essential to document your code. Especially when one begins working with other people, a team, or in a company. You will almost never be the only developer working on any particular program, so it's essential to get into the habit of documenting as you go. Fortunately Python makes this pretty easy.

    This is a docstring in a function:

    def build_lamborghini():
       """This function builds and returns a Lamborghini"""
       # Code goes here …..

    Docstrings can span multiple lines like this:

    def build_porsche(horsepower=220):
       """
       This function builds and returns a popular German sportscar.
       """
       # the keyword “pass” tells Python not to do anything here or get upset at the empty function
       pass

    Or they can be at the top of the module like this, before the imports.

    (The hash is inserted by some editors, but is not always present, don’t worry about it)

    # -*- coding: utf-8 -*-
    """
    This module contains some functions for making our lives easier.

    The first function is called brush_teeth() and does what you might expect.

    You can use it like so:
    >>> from habits import brush_teeth
    >>> brush_teeth()
    Teeth Brushed!
    """
    import os, sys, random

    # … Code goes below this

    This is called a module docstring.

    The great thing about docstrings is that they can be easily accessed.  Remember the help() function we learned in week 2?

    >>> help(build_porsche)
    Help on function build_porsche in module __main__:

    build_porsche(horsepower=220)
       This function builds and returns a popular German sportscar.

    Or by directly using Python’s built-in method, a double score attribute visible from dir().: (The backslash letter “\n” tells Python that this is a newline)

    >>> build_porsche.__doc__
    '\n    This function builds and returns a popular German sportscar.\n    '

    And later when we get to documenting our Python modules automatically, we will rely heavily on docstrings to do the majority of the work for us.

    Now many companies have different standards and conventions for docstrings, but I will draw your attention to one of the most popular which we currently use. And that is the Google style docstring.

    It consists of 3 parts: the description, the arguments, and the return type. It looks something like this:

    def build_porsche(name, horsepower=220, wheels=4):
       """
       This function builds and returns a popular German sportscar.

       Args:
           name (str): Name for this car
           horsepower (int): How powerful to make the engine
           wheels (int): How many wheels it should have

       Returns:
           Porsche(class): An instance (copy) of our porsche
       """
    # code goes here

    Each of the 3 parts I mentioned are optional, you don’t have to include a Returns section if your function does not return anything.

    Read briefly through this document, and don’t be too concerned with understanding some of the funky stuff like classes and @properties, we’ll be getting to that later.

    https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html

    Now for our exciting topic.

    Object Oriented Programming (or OOP, for short):

    In Python virtually everything is an object. Python is very flexible when it comes to designing our objects, combining or rearranging as we see fit, or making entirely new ones. Everything that comes with Python is flexible and open for us to modify. There will even be an example of this later.

    The concept of Object Oriented Programming is one that applies to any advanced language, not just python. It’s the idea of having a blueprint or as we call it-- Class, which is the definition of an object before gets created. The class definition acts as a blueprint, and when called, produces a “instance” working copy of it.

    First, we start by making a class:

    class Superhero(object):
       """
       This is a blueprint for extraordinary humans.
       """
       pass

     You can see by the keyword “pass” that this definition currently does nothing.

    Also, note that “object” is being supplied as an argument to our class. However class definitions are a little different than function definitions, and all the arguments given here actually used as “superclasses” for inheriting properties from. We will come back to this.

    Finally, notice I already applied a docstring to this class.

     

    Let’s play with this Superhero class interactively:

    >>> Superhero
    <class '__main__.Superhero'>
    >>> Superhero()
    <__main__.Superhero object at 0x05522410>
     
    >>> ironman = Superhero()
    >>> thanos = Superhero()
    >>> ironman
    <__main__.Superhero object at 0x055FD2D0> 

    Don’t worry about all the extra information here, but notice that the first time python tells us we have a class (blueprint) and each time after when we call it with (), python gives us an instance, a working copy. A class on its own is not something you can work with, just like how a blueprint of a car is not something you can drive.

    Let’s add a variable to this class, and also a function for fun. For brevity, I’ll leave out the docstring.

    class Superhero(object):
       iq = None
     
       def speak(self):
           print 'Give me your infinity stones!'
     

    thanos = Superhero()
    thanos.iq = 200
    print thanos.iq
    # prints 200
     
    thanos.speak()
    # prints “Give me your infinity stones!”

     Now-- functions on classes are called “methods”, Although they are essentially the same thing with one subtle difference. Did you notice the keyword “self”?

    By default, methods on classes must always start with “self” as the first argument.

    self” Provides us access to the instance we are calling, so you can actually design the methods to access itself. This can be confusing in theory, so let’s demonstrate by adding another function.

    class Superhero(object):
       iq = None
     
       def speak(self):
           print 'Give me your infinity stones!'
     
       def brag(self):
           print 'And my IQ is {}!'.format(self.iq)
     
    thanos = Superhero()
    thanos.iq = 200
    thanos.brag()
    # will print “And my IQ is 200!” 

    In theory you can think of it as "brag(thanos)" But the great thing is self will work the same way no matter how you name your variable.

    Note: Python is flexible and you can actually use any name besides "self" for your methods. The word "self" is simply a convention which is useful to Python developers because it makes the code much more standard and easy to understand, even if it’s written by someone else. In all my experience, Python developers have always used “self”. Now if you really wanted to confuse your instructor and classmates, you could change this, though I’m not recommending this at all ;)

    One more thing. Variables defined on the class as we have done it are called class variables, they will actually remain the same between different instances  since we really have defined the variable on the class and not on the instance. So if we want a variable that sticks only to the instance, how do we do this?

    This is where we’ll first see special methods in Python. Whenever you see a name that both begins and ends with double underscores, (__) these referred to native methods python already has. And we can in fact, extend or replace the special methods to change the ways that objects work.

    The most common special method you will see when working with classes is the __init__ method.

    class Superhero(object):
       def __init__(self):
           self.iq = None

    What this does is set up iq as a variable on a per-instance basis. Which means now we can do this:

    thanos = Superhero()
    thanos.iq = 200
     
    ironman = Superhero()
    ironman.iq = 201
    thanos.brag()
    # And my IQ is 200!
    ironman.brag()
    # And my IQ is 201! 

    Inheritance and Overloading:

    This is where the power of object-oriented-programming really begins to show. Most complex working systems rely on inheritance to simplify the code-base, and understanding this will give you an advantage.

    class Superhero(object):

       def __init__(self):
           self.iq = None
     
       def speak(self):
           print 'Give me your infinity stones!'
     

    class Ironman(Superhero):
     
       def speak(self):
           print "Never!! I have {} reasons not to!".format(self.iq)
     
       
    thanos = Superhero()
    ironman = Ironman()
    thanos.speak()
    # Give me your infinity stones!
    ironman.iq = 200
    ironman.speak()
    # Never!! I have 200 reasons not to!

    You can see when we define Ironman, we pass Superhero as an argument or a “superclass”, (also known as “parent class”). This tells Ironman that his blueprint is the same as Superhero.

    Then, we can selectively overwrite, replace, or extend methods found on the parent class.

    You can see how Ironman already had self.iq, and we simply overloaded (overwrote) the “speak” method.

    If you’d like to watch a video on the basics of OOP, here is a 20 minute review. (not 54 as indicated by Youtube). This doesn’t go as in-depth as we do.

    https://www.youtube.com/watch?v=L1eu7m4XFTg

     

    Private attributes (variables):

    It’s time to discuss the concept of “private” variables, which although it has no implementation in Python, it is a common concept found in other languages that has found its way into Python only through developer convention (writing style).

    When we see a variable or attribute that begins with a single underscore, it indicates that the develop himer who wrote this code intended the variable to be “private”. This just means that they don’t expect us to call this variable directly, but rather that it is something that should not be used or accessed directly. However this is only implied convention-- in Python there are no rules in this regard. Naming our variables with or without an underscore will not make any difference in the way the program behaves.

    Here’s an example of how a private variable might be used:

    class Superhero(object):
       def __init__(self):
           self.iq = None
           self._last_eaten = None
     
       def eat(self, something):
           self._last_eaten = something
           print 'Mmmmm tasty'
           
       def previous_meal(self):
           print 'I last ate {}'.format(self._last_eaten)
     
    spidey = Superhero()
    spidey.previous_meal()
    # I last ate None
    spidey.eat('Sardines and Sauerkraut')
    spidey.previous_meal()
    # I last ate Sardines and Sauerkraut

    Making Superheroes Super:

    When working with inherited classes, we may want to extend methods rather than replace them. Python has a keyword for this: “super”. When used, it returns the parent class for use. Here is how it looks in practice:

    class Superhero(object):

     def use_ability(self):
           print 'Powering up to...',

    class Spiderman(Superhero):
       
       def use_ability(self):
           super(Spiderman, self).use_ability()
           print 'shoot webs!'
     
    class SpidermanJunior(Spiderman):
       
       def use_ability(self):
           super(SpidermanJunior, self).use_ability()
           print 'and climb walls!'        

    When run, we see:

    >>> spidey = Spiderman()
    >>> little_spidey = SpidermanJunior()
    >>> spidey.use_ability()
    Powering up to... shoot webs!
    >>> little_spidey.use_ability()
    Powering up to... shoot webs!
    and climb walls!

    We can even do multiple inheritance:
    class A(object):
       def walk(self):
           print 'Walking'
     
    class B(object):
       def swim(self):
           print 'Swimming'
       
    class C(A, B):
       def __init__(self):
           self.walk()
           self.swim()


    >>> c = C()
    Walking
    Swimming

    Whew! You’ve made it this far, it’s time to review the terminology we have learned so far.

    • class

    • superclass

    • methods

    • inheritance

    • private attributes (variables)

    • super

    • instances

    Your goal this week is to go back to some of your programs you’ve written, and carefully consider if they could be re-written in a more object oriented manner. Think about tasks as potentially representing objects rather than procedures. For example, in a text based exploration game, each environment could be a class that inherits from some common environments.

    Be creative!

    If you’re still struggling, or would like further review with any of the OOP concepts we discussed today, I’d recommend this video example where Eli creates a virtual Deck of Cards with OOP.

    https://www.youtube.com/watch?v=t8YkjDH86Y4