In [ ]:
def spam(): # Functions are declared with the 'def' keyword, its name, parrentheses and a colon
print "spam" # Remeber to use indentation!
spam() # Functions are executed with its name followed by parentheses
In [ ]:
def eggs(arg1): # Functions arguments are declared inside the parentheses
print "eggs", arg1
eggs("eggssss") # Function calls specify arguments inside parentheses
In [ ]:
def func(arg1, arg2, arg3): # There is no limit of arguments
print "func", arg1, arg2, arg3
func("spam", "eggs", "fooo")
In [ ]:
print func("spam", "eggs", "fooo") # By default functions return None
In [ ]:
def my_sum(arg1, arg2):
return arg1 + arg2 # Use the return keyword to output any result
print my_sum(3, 5)
In [ ]:
print my_sum(3.333, 5)
print my_sum("spam", "eggs") # Given that Python is a dynamic language we can reuse the same method
In [ ]:
def my_pow(arg1, arg2=2): # It is possible to define deault values for the arguments, always after arguments without default values
return arg1 ** arg2
print my_pow(3)
In [ ]:
def my_func(arg1, arg2=2, arg3=3, arg4=4):
return arg1 ** arg2 + arg3 ** arg4
print my_func(3, arg3=2) # Use keyword arguments to call skip some of the arguments with default value
In [ ]:
def my_func(arg1=1, arg2=2, *args): # This arbitrary list is a (kind-off) tuple of positional arguments
print args
return arg1 + arg2
print my_func(2, 3)
In [ ]:
print my_func(2, 3, 5, 7)
In [ ]:
spam = (5, 7)
print my_func(2, 3, *spam) # It is possible to unpack a tuple or list as an arbitrary list of arguments
In [ ]:
def my_func(arg1=1, arg2=2, **kwargs): # This arbitrary 'args' list is a (kind-off) tuple of positional arguments
print kwargs
return arg1 + arg2
print my_func(2, 3)
In [ ]:
print my_func(2, 3, param3=5, param4=7)
spam = {"param3": 5, "param4": 7}
print my_func(2, 3, **spam) # It is possible to unpack a tuple or list as an arbitrary list of arguments
In [ ]:
def function_caller(f):
f()
In [ ]:
def func_as_arg():
print 'There should be one-- and preferably only one --obvious way to do it.'
In [ ]:
function_caller(func_as_arg) # Functions can be passed as arguments
In [ ]:
class Spam: # 'class' keyword, camel case class name and colon :
pass
spammer = Spam() # Class instantiation: spammer becomes an instance of Spam
print spammer
In [ ]:
class Eggs(Spam): # Ancestor superclasses inside parentheses for inheritance
a_class_attr = "class_val" # Class attributes inside the body, outside class methods. Must have value
def __init__(self, attr_val): # __init__ is called in the instances initialization (not constructor)
self.attr = attr_val
def method(self, arg1, arg2=None): # Method declaration. Indented and receiving self (the instance)
print "'method' of", self
print self.attr, arg1, arg2 # Access instance attributes using self with a dot .
def second_method(self):
self.attr = 99.99
self.method("FROM 2nd") # Methos may call other methods using self with a dot .
In [ ]:
egger = Eggs(12.345) # Provide __init__ arguments in the instantiation
print egger
In [ ]:
print egger.attr # Retrieve instance attributes with a dot
print egger.a_class_attr # Retrieve class attributes with a dot
In [ ]:
print Eggs.a_class_attr
egger.a_class_attr = "new value"
In [ ]:
print egger.a_class_attr
print Eggs.a_class_attr
In [ ]:
print Eggs
In [ ]:
egger.method("value1", "value2")
egger.second_method()
print egger.method
print Eggs.method
In [ ]:
inst_method = egger.method
inst_method("valueA", "valueB")
In [ ]:
class Spam:
def spam_method(self):
print self.__class__ # __class__ is a special attribute containing the class of any object
print type(self)
spammer = Spam()
In [ ]:
spammer.spam_method()
print spammer
print type(spammer)
In [ ]:
# Why type says it is an 'instance' and not a 'Spam'?
class Spam(object): # Inherit from 'object'
def spam_method(self):
print self.__class__
print type(self)
spammer = Spam()
print spammer
print type(spammer) # This is a new-style class
By default all classes are old-style until Python 3
Other changes introduced Python 2.2: new, new dir() behavior, metaclasses, new MRO (also in 2.3)
More info: http://www.python.org/doc/newstyle/
In [ ]:
class OldStyleClass():
pass
old_inst = OldStyleClass()
print type(old_inst)
In [ ]:
# Let's inherit from an old-style class
class NewStyleSubClass(OldStyleClass, object): # Multiple inheritance
pass
new_inst = NewStyleSubClass()
print type(new_inst)
In [ ]:
class Spam(object):
spam_class_attr = "spam" # Class attributes must have value always (you may use None...)
def spam_method(self):
print "spam_method", self, self.spam_class_attr
print self.__class__
class Eggs(object):
eggs_class_attr = "eggs"
def eggs_method(self):
print "eggs_method", self, self.eggs_class_attr
print self.__class__
In [ ]:
class Fooo(Spam, Eggs): # Specify a list of ancestor superclasses
fooo_class_attr = "fooo"
def fooo_method(self):
self.spam_method()
self.eggs_method() # Retrieve superclasses attributes as if they were yours
print "fooo_method", self, self.fooo_class_attr
print self.__class__
In [ ]:
foooer = Fooo()
foooer.fooo_method()
In [ ]:
foooer.spam_method()
foooer.eggs_method() # self is ALWAYS an instance of the subclass
In [ ]:
print foooer.spam_class_attr
print foooer.eggs_class_attr
print foooer.fooo_class_attr # We have access to all own and ancestors' attributes
In [ ]:
# Given that Python is a dynamic language...
class Spam(object):
pass
spammer = Spam()
spammer.name = "John"
spammer.surname = "Doe"
spammer.age = 65
spammer.male = True # ... this is legal
In [ ]:
print spammer.name
print spammer.surname
print spammer.age
print spammer.male
In [ ]:
class Spam(object):
def method(self, arg=None):
print "Called 'method' with", self, arg
@classmethod # This is a decorator
def cls_method(cls, arg=None):
print "Called 'cls_method' with", cls, arg
@staticmethod # This is another decorator
def st_method(arg=None):
print "Called 'st_method' with", arg
spammer = Spam()
In [ ]:
spammer.method(10)
In [ ]:
Spam.method(spammer, 100) # Although it works, this is not exacty the same
In [ ]:
print spammer.method
print Spam.method # It is unbounded, not related with an instance
In [ ]:
spammer.cls_method(20)
Spam.cls_method(200)
In [ ]:
print spammer.cls_method
print Spam.cls_method # Both are a bounded method... to the class
In [ ]:
spammer.st_method(30)
Spam.st_method(300)
In [ ]:
print spammer.st_method
print Spam.st_method # Both are a plain standard functions
In [ ]:
print "'__name__' value:", __name__
In [ ]:
def func():
print "Called func in", __name__
In [ ]:
print "'func.__module__' value:", func.__module__
In [ ]:
!cat my_modules.py
In [ ]:
!python my_modules.py
In [ ]:
import my_modules
In [ ]:
# What will it happen if we import the module again?
import my_modules
In [ ]:
### All code is evaluated (executed) only once the first time it is imported
In [ ]:
func()
my_modules.func()
In [ ]:
from my_modules import func
func()
In [ ]:
func()
In [ ]:
!rm -rf basic_tmp
!mkdir basic_tmp
!echo 'print "This is the __init__.py", __name__\n' > basic_tmp/__init__.py
!cp my_modules.py basic_tmp
In [ ]:
!python -c "import basic_tmp.my_modules"
In [ ]:
!python -c "from basic_tmp.my_modules import func;func();print my_modules"
In [ ]:
!python -c "from basic_tmp.my_modules import func as the_module;the_module();print the_module.__name__"
LESSONS LEARNT:
module_name_3, module_name_4)
In [ ]:
!rm -rf basic_tmp
!mkdir basic_tmp
!echo 'print "This is the __init__.py", __name__\n' > basic_tmp/__init__.py
!cp my_modules.py basic_tmp
!echo 'from my_modules import func\n' > basic_tmp/__init__.py
In [ ]:
!python -c "from basic_tmp import func;func()"