# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3.9.7 64-bit
#     language: python
#     name: python3
# ---

# # Basics in Python
# * as we remember, python is an interpreted programming language without compiler building process
# * python is a multiparadigm language, everything is possible --- but overall its working like an object oriented programming language
# * datatypes are dynamically handled

# ## more datatypes
# lets have a look at a few more datatypes and special datatypes

# +
# as we remember we dont need to clarify a datatype of a variable

my_number = 42 # is automatically interpreted as integer
# expert knowledge: we generate a object variable from the builtin class Integer it is possible to use functions from Integer class

my_float_number = 42.42 # with the dot identifier it is possible to create floating numbers this variable is interpreted as float

my_text = "example text" # this variable is interpreted as string
# expert knowledge: a string is a chain of chars (list of chars) char is the simple datatype, string is a complex char datatype

my_boolean = True # this variable is interpreted as boolean

# to show the datatype of our variable we can use the builtinfunction type() inside a print statement

print(type(my_number), type(my_float_number), type(my_text), type(my_boolean))
# -

# As we see in the printout all datatypes are types of classes, that means every datatype comes with class functions. We will see later in this training how this work

# +
# in some programs it is possible to enforce a datatype
# because of dynamically handling of datatype variables it could happen that python will change a variable type during program runtime
# in case of datatype changing in special programs, error could appear
# to enforce a datatype we use signal words (builtin functions)

my_variable_1 = int(42) # this variable is an integer
my_variable_2 = int(42.42) # also an integer the floating point part will be delete
my_variable_3 = float(42) # floating point datatype
my_variable_4 = str(42) # a number as a string datatype
my_variable_5 = bool(42) # a boolean datatype if the number is > 0 its always true in Python Boolean statements are True or False (case-sensitive)
my_variable_6 = bool(" ") # whitespace string is True --> behind a whitespace is a character ascii sign which is a True context
my_variable_7 = bool("") # empty string is always False


print(  my_variable_1, type(my_variable_1), "\n",
        my_variable_2, type(my_variable_2), "\n",
        my_variable_3, type(my_variable_3), "\n",
        my_variable_4, type(my_variable_4), "\n",
        my_variable_5, type(my_variable_5), "\n",
        my_variable_6, type(my_variable_6), "\n",
        my_variable_7, type(my_variable_7) )

# +
# the naming of a variable does have a huge variance
# it is possible to use "_" underline signs between words 
# the possible variable naming styles are:
# --> camelCase
# --> PascalCase
# --> snake_case
# it is also possible to use numbers inside a variable name but not for the first sign
# the first variable letter need to be an underline or a normal letter
# reserved functions or special signs are not allowed
# if u using a reserved function (built-in) or reserved word (str, int, ...) u overwrite the functionallity --- this will devastate your program
# during development most of all programmer using temporary variable names, in production code we need to keep the topic information for our variable
# because of nearly no limitation for the variable name i prefer to use snake_case with lower letter with descritpion of the variable topic

# if the variable does have the topic to keep a sensor gps signal frame we call the variable name
my_gps_sensor_frame = "some gps sensor data" # gps could have diferent datatypes but this is not the topic at the moment

# some developer keep the datatype inside the variable also
str_my_gps_sensor_frame = "..." # depends on you how to style your variables

# capital letter variables are normally cons inside a program
MY_AWESOME_CIRCLE_CONSTANT = 3.1415 # short form of Pi for calculation
WINDOW_WIDTH = 640   # window size paramter in guis
WINDOW_HEIGHT = 480  # window size paramter in guis

# sometimes we dont know how to store or use a variable
# in such a case it is possible to use a placeholder variable instead of a complete variable name
# the common placeholder for variables is "_"
_ = "dont like to use a this variable"

# in python it is also possible to use multiple variable allocations
var_1 = var_2 = var_3 = 42 # all 3 variables are integer with value of 42

# -

# we learned that a variable (a single value without variable storage also!) are objects of classes
# if we keep in mind that everything in python is handled the object oriented way we understand how variables are stored and used
# the interpreter decide what kind of datatype our variable is and create an object from the specific class dynamically
# to learn what kind of class function we could use, we need to have a look at the specific class documentation
# the builtin function for selecting the class function is help()
# it is possible to use help on the class directly, the function, variable or a value
help(int)

# +
# help shows us the docstring documentation of the class/function/variable
# under the method section we see a few methods with syntax "_" "_" "name" "_" "_"
# these double underlined functions are special system relevant functions
# they are activated with special code instructions, for example if we program
# print(my_variable)
# the internal __str__ function is called
#
# my_var1 + my_var2 
# the internal __add__ function is called 
# 
# function names with a start of 2 underline signs are private and hidden functions and not direct callable
# in class context everything with double underline in the beginning is hidden and only accessable inside the class
# functions which we are able to use normally start with a normal letter and always have round brackets (with or without parameters)
#
# functions from an object are callable by typing a dot after the object
# this process could explain as a concatination of objects with functions
# if we concat a object to a function, the return "object" of the function is also able to add a concatination
# this codestyle is typically python in oneliner code instruction
#
# lets have a look to an example
my_string_variable = "this is a test string"
print("Normal: \n", my_string_variable)

print("function call with capital first letter: \n", my_string_variable.capitalize())
print("function call uppercase: \n", my_string_variable.upper())
print("function call to upper, and concat the result of upper with a function call swap which returns upper case to lower case: \n", my_string_variable.upper().swapcase())

print("function call with capital and swap: \n", my_string_variable.capitalize().swapcase())

# -

# concatination of functions are very strong and multiple times usable
# try to find the limitation of concatination :)


# ## a few special datatypes
# * we already learned a few basic datatypes, with external modules it is also possible to expand the number of datatypes in all ways
# * special datatypes are normally called as complex
# * in programming languages complex datatypes are structures with subdatatypes or compound datatypes

# +
# type of lists
# if we want to work with lists it is possible to use the list datatype
# in python we have a few list objects which are enforced by different brackets
# the classical list is enforced by a square bracket
# all list keeping objects of other datatypes/variables
my_list_1 = [1,2,3,4] # this variable is a normal list, an array
my_list_2 = [1,2, "text", 42.42] # it is also possible to mix the elements of datatype

print("normal list:", my_list_2, type(my_list_2))
# standard lists are mutable, that means we are able to directly add more values without overwright the variable
# to add a value to the list, we can use .append(value) function
my_list_1.append(42)
print("after append:",my_list_1)

# the second list type we want to have a look on is a tuple
# a tuple list is a immutable list datatype
# it is also possible to mix the sub datatypes
# tuples are enforced by normal brackets
my_list_3 = (42, 55, "GMT", "zone")
print("tuple list", my_list_3, type(my_list_3))
# we could use a tuple datatype if we want to struct different datatypes to a fix variable
# it is also possible to use a tuple for multiple return inside function
# for example a gps sensor dataset consist of longitude, latitude, time and height --- the perfect datatype after sensor readout is tuple
# tuple lists are not changeable --> immutable, we always need to overwrite our variable or convert back to lists with using the list() function

# the third datatype is used in different ways
# a set is a unique value list of possible mixed types
# a set list is immutable and will keep only unique values
# to enforce a set we need to program with curly brackets
my_list_4 = {42,55 , 20, 42, "test", 17, 0, 4} # keep an eye on 42 its 2 times inside the declaration
print("set list", my_list_4, type(my_list_4)) # 42 we see only once
# with sets it is also possible to use mathematically set functions like join, innerjoin a.s.o - try it out by yourself :) 

# sets are very special lists it is also possible to use the set property to expand our posibilities
# it is possible to declare a new code block with a ":" sign... in case of lists the code block instruction is interpreted as a new declaration
# inside sets this means it is possible to interprete the value left from the double dot as a unique variable and the part right of the double dot as a value to the variable
# this special code instruction is better known as key value list
# inside a key value list the key need to be unique (set property), the value could be what ever we want (variable property)
my_list_5 = {42: "test value", "banana":[1,2,3], "sth.more":{ "subthing_1": 42.42, "subthing_2":1704}}
print("dictionary list:", my_list_5, type(my_list_5))
# in python programming language a key value list is better known as "Dictionary"

# +
# lists or list variants (datatype that could be a list too) are iterable
# iterable means we are able to call an element of a list directly
# the list call instruction is a square bracket
# with tuples and arrays it is possible to ask for an element by calling the element position
# we start counting with 0 that means the first element of a list is called with [0]
print("element 1 of list 1:", my_list_1[0]) # the first element of list_1 counting from left to right

# it is also possible to call the last element (or first element from the right position)
print("last element of list 3", my_list_3[-1])

# inside the square bracket it is possible to have a range of values with stepping
# for a range of values we need to use the doubledot instruction again
# if we want to have "from value_1 to value_3" we need to call [0:2] left to right orientation
# the stepping is also callable with again a double dot [0:20:2] --> every second value from 0 to 20 element
print("first two elements of list 2", my_list_2[:2]) # first two elements of list 2

# sets are special list, to call an element of a set we need to use a function
# it is only possible to get a set value element by element in order
# the function pop will show us the value elementwise
# after pop using the value is deleted from the set
print("set before pop:", my_list_4)
print("pop value:",my_list_4.pop())
print("set after pop:",my_list_4)

# a dictionary works a little bit different
# we are able to call the key value directly on the dictionary
# if we using the normal iteration counter (0,1,2,3,...) we get an error
# to get values of the dictionary it is necessary to use a function or direct key call
print("dictionary:",my_list_5)
print("keys of dictionary:",my_list_5.keys())
print("call a specific key from dictionary:",my_list_5["sth.more"])

# it is also possible to concat list calls if we have a sublist inside a list
# because of multiple datatype mix and declaration it is possible to have multidimensional lists
# inside our dictionare we have a subdictionary we want to call
print("subdictionary call with listconcat:", my_list_5["sth.more"]["subthing_2"])

# if we want to change a value inside a dictionary we only need to call the keys and declarate new values to the key
# it is also possible to add new keys if we calling a non existing key with a declaration
my_list_5["new_key"] = (1,2,3,4,5)
print("after adding a new key:",my_list_5)


# -

# as we remember strings are a special datatype, list of chars
# that means strings are list like objects and iterable
# we are able to do a list call on a string too, this helps us if we want to work with strings directly for comparison a.s.o.
print("Stringtext:", my_string_variable)
print("first 10 signs:",my_string_variable[:10])
print("first 10 every second sign:", my_string_variable[:10:2])
print("string readorder from left perspective:", my_string_variable[::-1])


# +
# as we said with sets we are able to do mathematical operation
# example to use set functions
my_list_set1 = {1,2,3,4,5,6,7,8}
my_list_set2 = {2,4,6,8,10}

print(my_list_set1.difference(my_list_set2)) # delta set set1
print(my_list_set1.intersection(my_list_set2)) # same in both
# a lot more is possible
# -

# ## palindrom check

# +
# with the knowledge of listcalls and class.function calls we are able to develop a short program
# lets develop a short palindrom check
# a palindrom is a word or sentence which is the same from both readorders (left to right == right to left - readorder)

# first we define a palindrom string
my_palin = "A man, a plan, a canal: Panama."
print("original:\n", my_palin)

# inside the string we have special characters we need to replace
# "," - comma; ":" - doubledot; "." - dot; " " - whitespace
# additional to the replacement we need to lower or uppercase all signs
my_corrected_palin = my_palin.replace(":","").replace(",","").replace(".","").replace(" ","").lower()
print("corrected:\n", my_corrected_palin)

# next we need to change the readorder
my_changed_readorder_palin = my_corrected_palin[::-1]
print("change readorder:\n", my_changed_readorder_palin)

# last we check both variables
print("is our string a palindrom?\n", my_changed_readorder_palin == my_corrected_palin)

# -

# ## print syntax
# * with the print function it is possible to print to our active terminal variables or text
# * the normal structure inside a string is comma separation of variables, this have an effect that between two variable information a whitespace is automatically added
# * if we using string concatination we need to ensure that all dataparts are string datatype (need to enforce it to string)
# * it is also possible to multiply string with a number, if we want to print 3 times "bla" we need to program: 3*"bla" 
# * if we using string statements it is also possible to use excape commands like backslash n for newline as we see above
# * sometimes we dont want to do the escape command or we want to fill our string statement with variables
# * in past python version (< v3.7) it was possible to set placeholder inside a string statement with curly brackets and replace the placeholder with variables and format function usage
# * today the format function is used if we have dynamically URL calls which are changed depending on what we want - that means we declare a general URL GET request with placeholder, and replace the placeholder with our variables, for example data from different users is cylce call with changing user information
# * if want to print a string as it is - raw text, it is possible to add a small r infront of the string, this formats the string to raw text string
# * since python 3.7 it is possible to format a string as a f-string which let us handle the variable replacement more easy

# +
# normal string usage with comma separation
print("1234", "\n", "5678")

# a docstring is a string also
my_docstring = """this docstring test is a test
additional line
                indent with pressing tabulator key on keyboard
    indentation with whitespaces

this is again a new line
"""
print("formatted printout depends on the docstring: \n",my_docstring)

# -

# string concatination with string enforcement and calculation
print("1" + "2" + str(42) * 3)

# string replacement of placeholder with format
print("on my text at position {} we have a variable {} with values".format(42, "testname") )
print("on my text at position {0} we have a variable {0} with values".format(42, "testname") )
print("on my text at position {0} we have a variable {1} with values".format(42, "testname") )
print("on my text at position {1} we have a variable {0} with values".format(42, "testname") )
print("on my text at position {1} we have a variable {1} with values".format(42, "testname") )

# string raw text
print(r"your button expect an integer value for example = {my_int} with value 42.")
print(r"with a \n  escape command you add a newline to your print statement")

# f-string direct replacement of variables inside the string
my_int = 99
print(f"value of my variable is {my_int} with datatype: {type(my_int)}")

# * congratulation you finished this lesson
#
# ## what we learned
# * datatypes in python with enforcement
# * special structured datatypes (lists, dictionary)
# * using a class function (function calls)
# * list calls
# * print syntax
#

StarcoderdataJupyterScriptsDedupFiltered