#!/usr/bin/env python

def main():
    print("Hello World!")

if __name__ == '__main__':
    main()


"""
Explanation: Python Fundamentals
Anatomy of your very simple first script
End of explanation
"""


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

def main(*args, **kwargs):
    """Simple main function that prints stuff"""
    print(args)  # args is a tuple of positional params
    print(kwargs) # kwargs is a dict with keyword params
    ## The names are just a mere convention

if __name__ == '__main__':
    main(sys.argv)  # input params from the command line


"""
Explanation: Shebang: /usr/bin/python2 or /usr/bin/python3
main function
What the heck is __name__ or __file__ ? Snake charmers

Adding parameters to your script ...
Your first imports and function parameters: *args, **kwargs
$ python hello_montoya.py "Hello" "My name is Iñigo Montoya" "You killed my father" "Prepare to Die"
Encoding
Check out How to unicode
Brief history
ASCII (American Standard Code for Information Interexchange) 1968.
English alphabet took the conversion of a letter to a digit, between 0-127.
Mid 1980's computers -> 8-bit (0-255)
But what happens to accents? cyrilic alphabets? French (Latin1 or ISO-8859-1) Russian (KOI8)?
UNICODE Standarization with 16-bit (2^16 = 65.535 distinct values)
Definitions

Character: smallest component of text ("A", "É")
Unicode: code points: integer value usually denoted in base 16
Unicode string: Serie of code points from 0 to 0x010ffff.
Unicode scapes:
- \xhh -> \xf1 == ñ
- \uhhhh ->
- \Uhhhhhhhh
Encoding: translates a unicode string sequence of bytes

Comment -- X: Y -- (inspired by Emacs, PEP 263)
# -*- coding: latin-1 -*-
In Python 3 the default encoding: UTF-8
All strings → python3 -c 'print("buenos dias" "hyvää huomenta" """おはようございます""")' are unicode
IMPORTS
Official Docs
Namespace is designed to overcome this difficulty and is used to differentiate functions, classes, variables etc. with the same name, available in different modules.
A Python module is simply a Python source file, which can expose classes, functions and global variables. When imported from another Python source file, the file name is sometimes treated as a namespace.
__main__ is the name of the scope in which top-level code executes. 
A module’s __name__ variable is set to __main__ when read from standard input, a script, or from an interactive prompt.
```python
!/usr/bin/env python
import os
import sys
if name == "main":
settings_module = "settings.local"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)

```
A Python package is simply a directory of Python module(s).
__init__.py.
The __init__.py file is the first thing that gets executed when a package is loaded.
rocklab/ ...
spacelab/
    __init__.py
    manage.py
    utils/
        __init__.py
        physics.py
        multidimensional/
            __init__.py
            laws.py
    rockets/
        __init__.py
        engine.py  
        base.py  # Defines a rocket model that exposes all its functionality
Relative imports specific location of the modules to be imported are relative to the current package.

laws.py

python
from ..phsycis import gravity

base.py

```python
from ..mutidimensional.laws import InterDimensionalTravel
from .engine import (Motor, turn_on_engine, turn_off_engine,
                     Bolt)
Avoid the use of \ for linebreaks and use parenthesis. Lisp people will be happy
from .engine import Motor, turn_on_engine, turn_off_engine, \
                    Bolt        
```
Absolute imports an import where you fully specify the location of the entities being imported.

base.py

python
from utils.multidimensional.laws import Infinity
from rockets.engine import Motor
Circular imports happen when you create two modules that import each other.

rockets.engine.py

```python
from .base import bad_design_decision
D'ouh
def inside_the_function(*params):
    # avoid circular import, or ci. Is good to mention (IMAO)
    from rockets.base import bad_design_decision
    bad_design_decision(params)
```
Here comes the future
Official Docs
Note for absolute imports:
from __future__ import absolute_import
Keep Reading
About imports in Python
About future imports
End of explanation
"""


# Immutable Objects

age = 60 # int
weight = 77.8  # float
infinite = float('inf')
name = "Rick" # basestring/str
nick_names = ("Sanchez", "Grandpa") # tuple

jobs = frozenset(("scientist", "inventor", "arms salesman", "store owner"))

# Mutable Objects
interests = ['interdimensional travel', 'nihilism', 'alcohol']
info = {
    "name": name,
    "last_names": last_names,
    "age": age
}
redundant = set(interests)


# Information from objects
type(age) # int
isinstance(age, int)  # True 
type(infinite)
type(name)
isinstance(name, basestring)
# typve vs isinstance: type doesnt check for object subclasses
# we will discuss the type constructor later on

"""
Explanation: Programming Types
Boolean: True, False
NoneType: None
Mutable / Immutable Objects
Some immutable types:
int, float, long, complex
str
bytes
tuple
frozen set

Some mutable types:
byte array
list
set
dict
End of explanation
"""


# integers
print(id(age))
age += 10
print(id(age))
age -= 10
print(id(age))


# Strings
print(name + ": Wubba lubba dub-dub!!")
print(name.replace("R", "r"))
print(name.upper(), name.lower())



# Tuples
operations = "test",  # note the comma as it makes it a tuple!!! | tuple.count/index
print(id(operations))
operations += ('build', 'deploy')
print(operations, id(operations))



## Tuple assignment

def say(*args):
    print(args)
    
say(range(8))

# Packing 
test, build, deploy = "Always passing", "A better world", "Your mind"

# OK but use parenthesis :)

(test, build, deploy) = ("Always passing", "A better world", "Your mind")


print("Test: ", test)
print("Build: " + build)
print("Deploy: " + deploy)



# Unpacking
test, build, deploy = operations
print(test, build, deploy)

# You are warned:  # ERROR -- too many values to unpack
# https://docs.python.org/3.6/tutorial/controlflow.html#unpacking-argument-lists

# lists



"""
Explanation: Why immutable objects?
End of explanation
"""


print(1 + 3)
print(2 ** 10)
print(5 % 3)




print(10 / 4) 2

from __future__ import division

print(10 / 4) 2.5  # float division

10 // 4  # in python 3 to get integer division

import operator

operator.add(2, 4)
operator.gt(10, 5)

"""
Explanation: Operators
Official Docs
Arithmetic Operators: + - / // * ** %
End of explanation
"""


