You are on page 1of 16

Object, Decorator, Duck

December 20, 2018

In [101]: class Book(object):


def __init__(self,title,author,pages):
print("A Book is Created")
self.title = title
self.author = author
self.pages = pages

def __str__(self):
return ("Title: {}, Author: {}, pages: {}".format(self.title,self.author,self.

def __len__(self):
return self.pages

def __repr__(self):
return ("Book({!r}, {!r}, {!r})".format(self.title, self.author, self.pages))

def __del__(self):
print("A Book is Destroyed")

def __hello__(self):
print("Hello World")

book = Book("Python", "Jose", 145)

A Book is Created

In [102]: len(book)

Out[102]: 145

In [22]: str(book)

Out[22]: 'Title: Python, Author: Jose, pages: 145'

In [23]: repr(book)

Out[23]: "Book('Python', 'Jose', 145)"

1
In [24]: print(book)

Title: Python, Author: Jose, pages: 145

In [ ]:

In [1]: def func():


return 1

In [2]: func()

Out[2]: 1

In [3]: s = 'This is a global variable'

def func():
print(locals())

In [4]: # locals() get local variables


# globals() get global variables

In [5]: print(globals().keys())

dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__builtin__', '__bui

In [7]: print(globals()['s'])

This is a global variable

In [8]: func()

{}

In [14]: # In python everything is an object,


# functions are objects.
def hello(name='Jose'):
return ("Hello " + name)

In [15]: hello()

Out[15]: 'Hello Jose'

In [16]: greet = hello

In [17]: greet

Out[17]: <function __main__.hello>

2
In [18]: greet()

Out[18]: 'Hello Jose'

In [20]: del hello

In [21]: hello()

---------------------------------------------------------------------------

NameError Traceback (most recent call last)

<ipython-input-21-a803225a2f97> in <module>()
----> 1 hello()

NameError: name 'hello' is not defined

In [22]: greet()

Out[22]: 'Hello Jose'

In [23]: # FUNCTIONS WITHIN FUNCTIONS

In [24]: def hello(name = 'Jose'):


print('The hello() function has been executed')

def greet():
return ("\t This is inside the greet() function")

def welcome():
return("\t This is inside the welcome() function")

print(greet())
print(welcome())
print("Now we are back inside the hello() function")

In [25]: hello()

The hello() function has been executed


This is inside the greet() function
This is inside the welcome() function
Now we are back inside the hello() function

In [26]: welcome()

3
---------------------------------------------------------------------------

NameError Traceback (most recent call last)

<ipython-input-26-efaf77b113fd> in <module>()
----> 1 welcome()

NameError: name 'welcome' is not defined

In [29]: def hello(name = 'Jose'):

def greet():
return ("\t This is inside the greet() function")

def welcome():
return("\t This is inside the welcome() function")

if( name == 'Jose'):


return greet #returning the function
else:
return welcome #returning the function

In [30]: x = hello()

In [31]: x

Out[31]: <function __main__.hello.<locals>.greet>

In [32]: print(x())

This is inside the greet() function

In [33]: # FUNCTION AS ARGUMENTS

In [35]: def hello():


return ("Hi Jose")

In [36]: def other(func):


print("Other Code Goes Here!")
print(func())

In [39]: other(hello)

Other Code Goes Here!


Hi Jose

4
In [48]: def new_decorator(func):
def wrap_func():
print('Code here, before executing the func()')

func()

print('Code here, will execute after the func()')

return wrap_func

In [49]: def func_needs_decorator():


print("This function needs a decorator")

In [50]: func_needs_decorator = new_decorator(func_needs_decorator)

In [51]: func_needs_decorator()

Code here, before executing the func()


This function needs a decorator
Code here, will execute after the func()

In [52]: @new_decorator
def func_needs_decorator():
print("This function needs a decorator")

In [53]: func_needs_decorator()

Code here, before executing the func()


This function needs a decorator
Code here, will execute after the func()

In [54]: def gencubes(n):


for num in range(n):
yield num ** 3

In [55]: for x in gencubes(10):


print(x)

0
1
8
27
64
125
216
343
512
729

5
In [56]: def genfibon(n):
a = 1
b = 1

for i in range(n):
yield a
a,b = b, a+b

In [57]: for num in genfibon(10):


print(num)

1
1
2
3
5
8
13
21
34
55

In [58]: def simple_gen():


for x in range(3):
yield x

In [59]: g = simple_gen()

In [60]: print(next(g))

In [61]: print(next(g))

In [62]: print(next(g))

In [65]: s = 'hello'

for x in s:
print(x)

6
h
e
l
l
o

In [67]: # String is "Iterable" but is not Iterator


next(s)

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

<ipython-input-67-8f0cf519e20b> in <module>()
1 # String is "Iterable" but is not Iterator
----> 2 next(s)

TypeError: 'str' object is not an iterator

In [68]: s_iter = iter(s)

In [70]: next(s_iter)

Out[70]: 'h'

In [71]: next(s_iter)

Out[71]: 'e'

In [72]: next(s_iter)

Out[72]: 'l'

In [73]: next(s_iter)

Out[73]: 'l'

In [74]: next(s_iter)

Out[74]: 'o'

In [75]: # Its how to convert objects that are


# iterables to iterators.

In [76]: # Everything you can use "for... in..." on is an iterable; lists, strings, files...
# These iterables are handy because you can read them as much as you wish,
# but you store all the values in memory and
# this is not always what you want when you have a lot of values.

7
In [77]: # Generators are iterators, a kind of iterable you can only iterate over once.
# Generators do not store all the values in memory, they generate the values on the fly

In [79]: # When you call the function, the code you have written in the function body does not r
# The function only returns the generator object, this is a bit tricky
# Then, your code will be run each time the for uses the generator.

def mygenerator():
for x in range(3):
yield x

In [81]: answer = mygenerator()

In [82]: for x in answer:


print(x)

0
1
2

In [84]: # The first time the for calls the generator object created from your function,
# it will run the code in your function from the beginning until it hits yield,
# then it'll return the first value of the loop.
# Then, each other call will run the loop you have written in the function one more tim
# and return the next value, until there is no value to return.

In [85]: # ADVANCED PYTHON MODULES

In [86]: # COLLECTIONS MODULE

In [87]: #Counter
from collections import Counter

In [90]: l = [1,1,1,2,4,4,5,1,2,3,12,3,2,1,2,3,1,2,3,4,2,1,2,3]
Counter(l)

Out[90]: Counter({1: 7, 2: 7, 3: 5, 4: 3, 5: 1, 12: 1})

In [91]: s = 'asssvacbvvaasssdjessses'
Counter(s)

Out[91]: Counter({'a': 4, 'b': 1, 'c': 1, 'd': 1, 'e': 2, 'j': 1, 's': 10, 'v': 3})

In [93]: s = 'How many times does each word show up in this sentence word word show up in the'

In [95]: words = s.split()


Counter(words)

8
Out[95]: Counter({'How': 1,
'does': 1,
'each': 1,
'in': 2,
'many': 1,
'sentence': 1,
'show': 2,
'the': 1,
'this': 1,
'times': 1,
'up': 2,
'word': 3})

In [96]: def my_function():


"""Demonstrate docstrings and does nothing really."""
pass

In [98]: my_function.__doc__

Out[98]: 'Demonstrate docstrings and does nothing really.'

In [99]: dir(Counter)

Out[99]: ['__add__',
'__and__',
'__class__',
'__contains__',
'__delattr__',
'__delitem__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__gt__',
'__hash__',
'__iadd__',
'__iand__',
'__init__',
'__init_subclass__',
'__ior__',
'__isub__',
'__iter__',
'__le__',
'__len__',
'__lt__',

9
'__missing__',
'__module__',
'__ne__',
'__neg__',
'__new__',
'__or__',
'__pos__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__setitem__',
'__sizeof__',
'__str__',
'__sub__',
'__subclasshook__',
'__weakref__',
'_keep_positive',
'clear',
'copy',
'elements',
'fromkeys',
'get',
'items',
'keys',
'most_common',
'pop',
'popitem',
'setdefault',
'subtract',
'update',
'values']

In [103]: dir(book)

Out[103]: ['__class__',
'__del__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__hello__',

10
'__init__',
'__init_subclass__',
'__le__',
'__len__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'author',
'pages',
'title']

In [104]: book.__class__

Out[104]: __main__.Book

In [105]: book.__dir__

Out[105]: <function Book.__dir__>

In [106]: dir(list)

Out[106]: ['__add__',
'__class__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__gt__',
'__hash__',
'__iadd__',
'__imul__',
'__init__',
'__init_subclass__',
'__iter__',

11
'__le__',
'__len__',
'__lt__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__reversed__',
'__rmul__',
'__setattr__',
'__setitem__',
'__sizeof__',
'__str__',
'__subclasshook__',
'append',
'clear',
'copy',
'count',
'extend',
'index',
'insert',
'pop',
'remove',
'reverse',
'sort']

In [108]: class Duck(object):


def quack(self):
print ("Quack")

class Mallard(object):
def quack(self):
print ("Quack Quack")

def shoot(bird):
bird.quack()

for target in [Duck(), Mallard()]:


shoot(target)

Quack
Quack Quack

In [109]: def calc(a,b):


return a+b

12
In [110]: calc(2,3)

Out[110]: 5

In [111]: calc('2','3')

Out[111]: '23'

In [112]: calc([2],[3])

Out[112]: [2, 3]

In [113]: import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

In [4]: class Foo:


def __init__(self):
print("Hello")

a = Foo()
b = Foo()
print(a)
print(b)

Hello
Hello
<__main__.Foo object at 0x7f03b0b19c18>
<__main__.Foo object at 0x7f03b0b19be0>

13
In [7]: name = 'Subash'

a = []
a.append(name)

b = a
b.append('Basnet')
name = 'Nabin'

print (name, a, b)

Nabin ['Subash', 'Basnet'] ['Subash', 'Basnet']

In [12]: first_names = ['Fred', 'George', 'Bill']


last_names = ['Smith', 'Jones', 'Williams']
name_tuple = (first_names, last_names)

first_names.append('Igor')

In [13]: print(name_tuple)

(['Fred', 'George', 'Bill', 'Igor'], ['Smith', 'Jones', 'Williams'])

In [14]: A = ['x','y','z']
B = ['X', 'Y', 'Z']
AB = (A,B)
A.append('w')
print(AB)

(['x', 'y', 'z', 'w'], ['X', 'Y', 'Z'])

In [17]: def foo(bar):


bar.append(42)
print(bar)

answer_list = []
foo(answer_list)
print(answer_list)

[42]
[42]

In [18]: def foo(bar):


bar = 'new value'
print (bar)

14
answer_list = 'old value'
foo(answer_list)
print(answer_list)

new value
old value

In [25]: class Test:


def __init__(self,x,y):
self.x = x
self.y = y
def __repr__(self):
return ('Point(%r, %r)' % (self.x, self.y))

In [26]: import copy


a = Test(23,42)
b = copy.copy(a)

In [27]: a

Out[27]: Point(23, 42)

In [28]: b

Out[28]: Point(23, 42)

In [29]: a is b

Out[29]: False

In [30]: # Because our test object uses immutable types (ints) for its coordinates,
# theres no difference between a shallow and
# a deep copy in this case.

In [31]: class Rectangle:


def __init__(self, topleft, bottomright):
self.topleft = topleft
self.bottomright = bottomright

def __repr__(self):
return (f'Rectangle({self.topleft!r}, '
f'{self.bottomright!r})')

In [32]: rect = Rectangle(Test(0,1), Test(5,6))


srect = copy.copy(rect)

In [33]: rect

15
Out[33]: Rectangle(Point(0, 1), Point(5, 6))

In [34]: srect

Out[34]: Rectangle(Point(0, 1), Point(5, 6))

In [35]: rect is srect

Out[35]: False

In [36]: rect.topleft.x = 999

In [37]: rect

Out[37]: Rectangle(Point(999, 1), Point(5, 6))

In [38]: srect

Out[38]: Rectangle(Point(999, 1), Point(5, 6))

In [39]: drect = copy.deepcopy(srect)

In [40]: drect.topleft.x = 222

In [41]: drect

Out[41]: Rectangle(Point(222, 1), Point(5, 6))

In [42]: rect

Out[42]: Rectangle(Point(999, 1), Point(5, 6))

In [43]: srect

Out[43]: Rectangle(Point(999, 1), Point(5, 6))

In [ ]:

16

You might also like