#!/usr/bin/env python # coding: utf-8 # ## Basics of Python language # # ## Variables and type # # ### Symbol names # # There are a number of Python keywords that cannot be used as variable names. These keywords are: # # and, as, assert, break, class, continue, def, del, elif, else, except, exec, finally, for, from, global, if, import, in, is, lambda, not, or, pass, print, raise, return, try, while, with, yield # # Note: Be aware of the keyword `lambda`, which could easily be a natural variable name in a scientific program. But being a keyword, it cannot be used as a variable name. # ## Fundamental types / lists / touples # In[1]: x=1 print (type(x)) x=1.0 print (type(x)) x=True print (type(x)) x=1.0+1.j print (type(x)) x = 'my string' print (type(x), len(x)) x = [1,2,3.0,2.+3j] y = (1,2,3.0,2.+3j) print(type(x)) print(type(y)) x[2]='ss' print([type(i) for i in x], [type(i) for i in y]) # ## Formating strings and print statement # In[2]: from math import * print('This is old '+'type', pi, 1) # In[3]: # formatting string print('This is old %10s %12.10f %5d' % ('type',pi, pi)) # In[4]: print(1.0, 'str1', 'str2', 'str3'+'str4') # **Python2 type of printing, still very useful** # In[5]: from math import * print( 'value of pi is %20.5f and integer part %d' % (pi, pi) ) # In[6]: print( 'value of pi is %20.5s and integer part %d' % (pi, pi) ) # In[7]: str(pi) # **More Python3 oriented printing** # In[8]: print('value of pi is {} and integer part {}'.format(pi, int(pi))) # In[9]: print('value of pi is {0:6.4f} and integer part {1:d}'.format(pi, int(pi))) # In[10]: print('value of pi is {1:6.4f} and integer part {0:d}'.format(int(pi),pi)) # We can also skip default values, like position argument or types # In[11]: print('value of pi is {:6.4f} and integer part {:}'.format(pi,int(pi))) # In[12]: print('value of pi is {:} and integer part {:}'.format(pi,int(pi))) # ## Lists # The syntax for creating list is [..,..,] # # The index starts with 0, like in C (not like in Fortran). Negative indices allowed. # # List can contain different types, and can be nested # # Slicing works just as with strings or arrays. Notation **[start:end:step]**, defaults can be omitted # In[13]: l1=[1,'a',1.0,-1.1] print(l1[0], l1[-1]) l2=[1,[1,[2,3]]] # nested list print(l1[1::2] ) # start with 1st, and take every second till the end print(list(range(10))) # range used in loops print(list(range(1,10,2))) # similar sintax: start,end,step # In[14]: list(range(1,10,2)) # In[15]: l1[1:-1:2] # In[16]: list(range(1,10,2)) # In[17]: from numpy import * array([[1j,2],[3,4]]) # Modifying a list: # * append # * insert # * remove # * del # # In[18]: l=[] # empty list l.append('A') # add at the end l.append('d') l.append('f') print(l) l[1]='e' # modify print(l) l.insert(1,'k') # insert at specific location to extent the list print(l) l.remove('f') # remove specific element, which must exist print(l) del l[1] # remove an element at specific location print(l) # In[19]: a=[3,4,7,8,9,10] print(3 in a) if 10 in a: print(a) # ## Tuples # # Are like lists, except non-modifiable. # # Most important use in returning from a function, and use in print statement. # In[20]: point=(10,20,30) #point[2]=2 print(point[2]) # point[0]=1 # can not be done point=(1,20,30) # modifying the entire tuple is allowed, i.e., it is not a constant #unpack it x,y,z = point print(x,y) print('I want nice printout %10.5f' % (pi,)) def Simple(): # need to return several things return ('a','b','c') x,y,z = Simple() print(x,y,z) # ## Dictionaries # # Dictionaries are like lists, but their keys are not integers, rather key can be any scalar type or tuple of scalar types # # Dictionary is constructed by {...} # In[21]: # This acts just like list di={} di[0]='a' di[1]='b' di[2]='c' print(di) print(di[0], di[1], di[2]) # Generic keys dj={} dj[300]='c' dj[600]='d' dj['cde']=1 dj[(1,2)]=5 print(dj) print(dj[(1,2)]) ## Python2 : if dj.has_key((3,4)): if (3,4) in dj: print(dj[(3,4)]) else: dj[(3,4)]='something' print(dj) # can also construct all at once dk={300:'c', 600:'d', 'cde':1, (1,2):5} # print it out by iterating over keys for k in dk.keys(): print(k, dk[k]) print() # print by iterating over (key,value) pairs for k,v in dk.items(): print(k,v) # In[22]: list(dk.keys()) # In[23]: list(dk.items()) # In[24]: from math import * di={(100,100):pi,(100,300):2*pi} print(di) # ## Control flow # # * if, else, elif # # * loops using for & while # # * List comprehensions: inline for statement # # [ < do something with i > for i in data] # In[25]: statement1=statement2=statement3=True statement2=False if (statement1): if (statement2): print ('both statements are true') elif (statement3): print ('statement2 is wrong but statement3 is true') # In[26]: c=d=f=[3] d[0]=4 d=[3] print(c,d) # In[27]: bool(100) # In[28]: for x in range(-3,3,1): print(x) ls=['scientific','computing','in','Python'] for word in ls: print(word) for i,word in enumerate(ls): print('At index',i, 'we have string',word) for i in range(len(ls)): word = ls[i] print('At index',i, 'we have string',word) # In[29]: list(enumerate(ls)) # In[30]: i=0 while i<5: i+=1 print(i) # In[31]: for i in range(10000): if i>=5: break print(i) # In[32]: l1=[] for x in range(5): l1.append(x**2) print(l1) # In[33]: l1=[x**2 for x in range(5)] print(l1) # In[34]: [(j,i) for j in range(5) for i in range(5)] # In[35]: for i in range(5): for j in range(5): print (i,j) # ## Functions # # * A functions starts with keyword `def` # * Very useful is the `docstring`, i.e., a string at the begining, which explains what the function does # * Multiple things (classes or values) can be returned by tuples # * function can have default and keyword arguments # In[36]: def funca(s): """Print a string and tell how many characters it has. Returns the length """ print(s,'has',len(s),'characters') return (len(s),s) l,s = funca('something') print(funca('This string')) help(funca) # If we explicitly list the name of the arguments in the function calls, they do not need to come in the same order as in the function definition. This is called keyword arguments, and is often very useful in functions that takes a lot of optional arguments. # In[37]: def funca(s,prnt=False,extra='p'): """Print a string with extra attached, and returns new string. """ sn = s+extra if prnt: print(sn,'has',len(sn),'characters') return sn funca('This str',extra='q') # ## Unnamed functions (lambda function) # # In Python we can also create unnamed functions, using the lambda keyword: # In[38]: def f2(x): return x**2 # is equivalent to f1 = lambda x: x**2 # In[39]: f1(3), f2(3) # This technique is useful for example when we want to pass a simple function as an argument to another function, like this: # In[40]: from scipy import integrate help(integrate.quad) # In[41]: from scipy import integrate from numpy import * def square(x): return x**2 integrate.quad(lambda x: sin(x)/x,0,1) integrate.quad(square, 0,1,) # ## Classes # # A class can contain attributes (variables) and methods (functions). # # A class is defined by `class` keyword, and the class can contains a number of class method definitions (a function in a class). # # * Each class method should have an argument self as its first argument. This object is a self-reference. # * Some class method names have special meaning, for example: # * `__init__` : Constructor, i.e., the name of the method that is invoked when the object is first created. # * `__str__` : A method that is invoked when a simple string representation of the class is needed, as for example when printed. # * `__repr__` : Representation of the class when printed # * `__call__` : Functor, i.e, called when the instance is “called” as a function # * There are many more, see http://docs.python.org/2/reference/datamodel.html#special-method-names # * Note: each class member starting with __ is private. If it does not start with __, it is public. # In[42]: class Point: """ Simple class for representing a point in a Cartesian coordinate system. """ def __init__(self, x, y): """ Create a new Point at x, y. """ self.x = x self.y = y #print('you gave me x and y=', x, y) def translate(self, dx, dy): """ Translate the point by dx and dy in the x and y direction. """ self.x += dx self.y += dy self.t = 1.0 def __str__(self): return("Point at [%f, %f]" % (self.x, self.y)) def __call__(self,z): return self.x,self.y,z # In[43]: q = Point(2., 3.0) q.translate(1,1) print(q.x, q.y, q.t) print(q) q(4.5) # In[44]: p = Point(1.,2.) print( p ) p.translate(1,1) print(p) print(p(1.0)) print(p.x,p.y) # ## Modules # # For additional modularity, modules are provided in Python. # # The modularity in Python is: # * variables, functions # * classes, which combine variables and functions # * modules, which combine classes, variables and functions into a unit, usually a file # # Module is a python file (`*.py`) or a module created by compiler (`*.so`) # # Outside jupyter (in regular python) we just create a file with the code, and call it a module. # # Within jupyter, we need to write code into a file, and than load the module (file). # In[45]: get_ipython().run_cell_magic('file', 'mymodule.py', '# This will write the following content into a file\n"""\nExample of a python module. Contains a variable called my_variable,\na function called my_function, and a class called MyClass.\n"""\n\nmy_variable = 0\n\ndef my_function():\n """\n Example function\n """\n return my_variable\n \nclass MyClass:\n """\n Example class.\n """\n\n def __init__(self):\n self.variable = my_variable\n \n def set_variable(self, new_value):\n """\n Set self.variable to a new value\n """\n self.variable = new_value\n \n def get_variable(self):\n return self.variable\n \nprint(__name__)\n\nif __name__ == \'__main__\':\n "test the module"\n m = MyClass()\n m.set_variable(1.)\n print(m.get_variable())\n') # In[46]: get_ipython().system('cat mymodule.py') # In[47]: import mymodule as my import scipy as sy #help(mymodule) # In[48]: m = my.MyClass() m.set_variable(10) m.get_variable() # In[ ]: