Hello dear reader. This book is design for newcomers to Python with little knowledge on basic programming. Instructors can used it as a guide for teaching Python.
Read text below quickly:
Python has a deisgn philozophy which emphasizes code readebility (notably using whitespece indentetion to delimit code blucks rather than curly brackats or keywords), and a syntax which allows programmers to express concepts in fewer lines of code than might be used in langoages such as C++ or Java. The langoage provides constructs intended to enable writing clear progrems on both a small and large scale.
There are many mistakes in the above text. Did you saw them? All of them?! If yes, good for you! If no, that is the most important difference between a computer and a human brain. Humans know how to solve problems automatically, we almost never see many of details, but computers are just fast and studpid calculators!
Remember every thing that computer is doing should be told to it. Computers doesn't have a brain, they are just very advanced calculators.
Python is an interpreted, general purposed language that is designed in 1991 by Guido Van Rossum. Python can be used for object oriented and functional programming. The language usage areas are tremendous. Some of them are, system programming, graphical user interfaces, game development, developing back-end, machine learning, visualizing data, media processing, scientific programming and many many more.
Python have very readable and simple syntax to write code. Nowadays python frameworks and libraries are every where.
There is only one drawback with python, runtime speed. But even this problem is almost solved by PyPy project and many libararies like numpy and numby.
An integrated development environment like pycharm isn't exactly what need for most of this course. We are dealing with watching effects of each line of code we write. Jupyter is what we use here.
Python is a scripting language. This is an advantage that we will get familiar through the course.
There is a tradition in learning programming languages, called Hello World program. We are going to respect the traditions and do it.
print('Hello Mickey Mouse!')
Everything after a sharp will be commented out and will not considered by interpreter.
You can even use strings as a comment.
# A whole line commented out.
"This is a comment too!"
"""This is a comment with a little diference.
This one is a multiline comment.
Remember this strings are not saved in any variable!
"""
print('Hello Mickey Mouse!') # Every thing after it will be commented, not before it.
print('Hello Mickey Mouse!') # Your code should be separated from sharps with two space characters.
Python enhancement proposal documents are about everything that apply to python. New features, community input on an issue, ... . Probably the most famous and useful one for us, is PEP8.
Development is not single handed anymore, it's a social effort. Groups of many people try to write a code for a same software. PEP8 helps for readability of code for reusing by other people.
Most imporant ones:
# Yes: easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
# This is good
import os
import sys
# This is bad
import os, sys
# This is good
spam(ham[1], {eggs: 2})
foo = (0,)
if x == 4: print x, y; x, y = y, x
# This is bad
spam( ham[ 1 ], { eggs: 2 } )
bar = (0, )
if x == 4 : print x , y ; x , y = y , x
# This is good
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
# This is bad
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
self
and cls
.Variables are labeled places that you can store data in them. We use all lowercase underscore separated alphabets and numbers for variables. Never start a variable with a number. Python names are case sensitive. Variable names can't have spaces.
famous_disney_character = "Donald Duck"
print(famous_disney_character)
Python have infinite integers, booleans, floating points, ... for representing numbers.
print (2 ** 4096)
These operators are use for arithmetic purposes. Some of them have more priority upon others.
# Arithmetic operators. Upper operators group have higher priority.
# Operators under a group have same priority.
print("2 ** 4: {}".format(2 ** 4)) # Power operator
print("5 * 6: {}".format(5 * 6)) # Multiplication operator
print("4 / 5: {}".format(4 / 5)) # Real division operator
print("10 // 3: {}".format(10 // 3)) # Integer division operator
print("25 % 7: {}".format(25 % 7)) # Reminder operator
print("23 + 45: {}".format(23 + 45)) # Addition operator
print("21 - 8: {}".format(21 - 108)) # Subtraction operator
number = 12 # Assignment operator
print("number: {}".format(number))
# By using parentheses you can change order of operators.
num_1 = 24 + 2 * 12
num_2 = (24 + 2) * 12
print("num_1: {}, num_2: {}".format(num_1, num_2))
# Operators with same priority, get calculated from left to right.
num_3 = 2 * 3 / 4
num_4 = 2 / 3 * 4
print("num_3: {}, num_4: {}".format(num_3, num_4))
When you are doing arithmetic operations on numbers, the result is the type that have more precision.
print("35 / 5 = {}".format(35 / 5)) # Result is a floating point number
print("35.0 / 5 = {}".format(35 / 5)) # Result is a floating point number
print("35 / 5.0 = {}".format(35 / 5)) # Result is a floating point number
print("35.0 / 5.0 = {}".format(35 / 5)) # Result is a floating point number
print()
print("23 // 7 = {}".format(23 // 7)) # Result is an integer number
print("23.0 // 7 = {}".format(23.0 // 7)) # Result is a floating point number
print("23 // 7.0 = {}".format(23 // 7.0)) # Result is a floating point number
print("23.0 // 7.0 = {}".format(23.0 // 7.0)) # Result is a floating point number
print()
print("12 * 3 = {}".format(12 * 3)) # Result is an integer number
print("12.0 * 3 = {}".format(12.0 * 3)) # Result is an integer number
print("12 * 3.0 = {}".format(12 * 3.0)) # Result is an integer number
print("12.0 * 3.0 = {}".format(12.0 * 3.0)) # Result is an integer number
Each variable need a name.
They have special meaning for python, so never use reserved words for variable, function, class, ... names!
from import as
True False
None
and or not
try except finally else raise assert
while for break continue else in
class
def lambda return yield
global nonlocal
del
if elif else pass is in
with
There is a technique called mnemonic. It's about variables name that you choose. They should be short, simple and sensible. Variables names doesn't have any influence on how python execute your code, but it is very important for who wants to read your code!
These three codes are the same thing to python. Python doesn't care about the meaning of your variables (Remember the fast, stupid calculator). It is just doing what we tell to do.
# VERY VERY bad code!
hqpowiefhpqowi = 35.0
poiwhgpoiqf = 12.5
poiwhzpoiqf = hqpowiefhpqowi * poiwhgpoiqf
print(poiwhzpoiqf)
# Better, but it is not mnemonic.
a = 35.0
b = 12.5
c = a * b
print(c)
# Mnemonic code. You can understand it's purpose.
worked_hours = 35.0
pay_rate = 12.5
pay = worked_hours * pay_rate
print(pay)
String are a sequence of characters that surrounded with single/double quote.
book_author = 'Amir Khazaie'
book_title = "Python for You and Me"
Multiline string are possible with triple single/double quote.
book_description = '''
This book is about Python programming language.
I started this book as a guideline for instructors
and who wants to learn python not as a first programming language.'''
book_license = """
This book is under Creative Common license.
so feel free to contribute to it and read it."""
third_way = 'This is a\
very very long\
string'
print(book_description)
print(book_license)
print(third_way)
Attaching two strings is easy as sum them up.
greetings = "Hello" + 'Goofy' + "!"
print("Greetings: " + greetings)
firstname = "Felonius"
lastname = "Gru"
fullname = firstname + ' ' + lastname
print('Full Name: {}, greeting: {}'.format(fullname, greetings))
Each string have a length that is count of characters in the string.
print(len(greetings))
print(len('')) # Empty String
As you have seen, there is a format
method for string objects that will try to make a new string. This method try to put arguments passed to it in {}
.
This is somehow like printf
structure in C/C++
.
print('You can put numbers: {},\
strings: {} in it to print.'\
.format(125, '#TEST#'))
As we explain later, any object can get passed to a format method and get a string representation.
print('List: {}, Dictionary: {} and Set {}.'\
.format([110, 'Python'], {'a': 'hi', 'b': 12},\
{1, 2, 3, 2, 5, 1}))
name = 'ali'
print(name[1])
print('Second arg: {1}, First arg: {0},\
Second arg again: {1}'.format('FIRST', 'SECOND'))
print('First Name: {firstname}, Last Name: {lastname}'\
.format(firstname='Mike', lastname='Wazowski'))
You can define a variable and put your template string in it and then get a proper string with format
method.
book_template = '''
{title}
Author: {author}
{description}
Book License: {license}
'''
print(book_template.format(author=book_author,
title=book_title,
description=book_description,
license=book_license))
Python interpret '
as start or end for a strings. What happen if want to use it as a apostrophe? You use an escape sequence. They have a special meaning to Python.
Escape Sequence | Meaning |
---|---|
\\ |
Backslash (\) |
\' |
Single quote (') |
\" |
Double quote (") |
\a |
ASCII Bell (BEL) |
\b |
ASCII Backspace (BS) |
\f |
ASCII Formfeed (FF) |
\n |
ASCII Linefeed (LF) |
\r |
ASCII Carriage Return (CR) |
\t |
ASCII Horizontal Tab (TAB) |
\v |
ASCII Vertical Tab (VT) |
print("Baymax: \"On a scale of one to ten, how would you rate your pain?\"\nHiro Hamada: \"What?!\"")
print('Student Results:\n\nName\tMark\nJodi\t80\nFrank\t30\nBob\t67')
# Above and below have the same output.
# In code below we use backsplash to tell python interpreter to ingnore newlines.
print('Student Results:\n\
\n\
Name\tMark\n\
Jodi\t80\n\
Frank\t30\n\
Bob\t67')
print('''Student Results:
Name\tMark
Jodi\t80
''')
For terminal applications, there is a built-in function called input
that you can prompt user to get data.
string = input() # The output is always a string.
print(string)
name = input("What is your name? ") # You can ask user for special kind of information.
print("Hello {}.".format(name))
Ask name and age of user and printing something nice for them.
We said strings are created from characters. You can select characters in a string in very customize way.
the_string = 'Hi. This is a simple string that we will use as an example.'
First of all you can access each character. Remember in Python counting starts from 0
.
print('1st character is: {}\n5th character is: {}'\
.format(the_string[0], the_string[4]))
You want last character? No problem. Use negative index!
print('Last character is: {}\n5th character from end of the string is: {}'
.format(the_string[-1], the_string[-5]))
String indexes are like:
H | e | l | l | o | W | o | r | l | d | ! | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
-12 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
It's almost like indexing, but instead of selecting a single character, you can select mutiple characters. There is three things you use in here Start
, Stop
, Step
and sperate them by a colon. In any of them missing, Python use their default value.
Variable | Default Value |
---|---|
Start | Beginning of the string |
Stop | End of the string |
Step | Equals to 1 |
print(the_string)
print(the_string[14:20]) # [start stop)
print(the_string[33:])
print(the_string[:27])
print(the_string[12:30:6])
print(the_string[::-1])
Use message below and generate word gun with slice operator.
message = '"Simplicity is about subtracting the obvious and adding the meaningful." - John Maeda'
in
Operator¶Checks for existence of string in another string.
the_string_contains_hi = 'hi' in the_string
print(the_string_contains_hi)
greeting_contains_hi = 'hi' in 'Hi, how are you?'.lower()
print(greeting_contains_hi)
This is a special object that represent Nothing
in Python.
print(None)
Boolean types are actually intergers 0
and 1
. Boolean keywords are True
, False
.
print("What you see when you print\
true: {} and false: {}".format(True, False))
We will see, any object can have a boolean equal. But for now look at code below.
print("Numbers: bool(814): {}, bool(-38): {}, bool(1.5): {}, bool(0): {}"
.format(bool(814), bool(-38), bool(1.5), bool(0)))
print("Strings: bool('A'): {},\
bool('Python is a wonderful language.'): {},\
bool(''): {}"
.format(bool('A'),\
bool('Python is a wonderful language.'),\
bool('')))
All empty python datasets are evaluated to false.
print("0: {}, '': {}, []: {}, {}: {}, set(): {}"
.format(bool(0), bool(''), bool([]), '{}', bool({}), bool(set())))
A list is very similar to an array. You can put any data in a list and access them with indexing and slicing.
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
print("3rd Prime Number: {}".format(primes[2]))
print("Even indexed Prime Numbers: {}".format(primes[::2]))
print("Last Prime Number: {}".format(primes[-1]))
Lists can have multiple data types at the same time! A list can have integers, floats, string and even other lists!
stuff = ['Apple', 12, [1, 2, 3], {'a': 'Alice', 'b': 'Bob'}]
The len
function return length of our list.
print(len(stuff))
The difference between normal arrays and Python lists is that Python lists can change!
Add new values with append
method and remove value with del
keyword.
primes.append(27)
print(primes)
primes.extend([9, 31, 37])
print(primes)
del primes[9:11]
print(primes)
primes.insert(9, 29)
print(primes)
With method pop
you can get the last element of list and remove it.
print(primes.pop())
print(primes)
remove
/index
methods get an object, search for it in the list and remove it/return index of it.
primes.remove(2)
print(primes)
print(primes.index(17))
Add to lists together and get new one.
positive_nums = [1, 2, 3, 4, 5]
negative_nums = [-1, -2, -3, -4, -5]
integers = negative_nums + [0] + positive_nums
print(integers)
sample_list = [1, 2, 'hi', 2, 'hello', 9, 3.14, 2]
print(sample_list.count(2))
We have in
operators for lists too.
[1, 2, 3] in integers
2 in integers
These two concepts come with sequences. Unpacking means separate each part of a sequence and packing means gather them together in a place.
In Python we have multiple assignments.
name, age, color = 'Bugs Bunny', 77, 'White' # Mixed types. No type limitations.
print(name, age, color)
You can even put multiple objects in a single variable.
character = 'Spongebob Squarepants', 18, 'Yellow'
print(character) # Character is a Tuple actually.
If you have a long list and you wanted to get head and tail of it, you can use this syntax.
students = ['Emma', 'Noah', 'Olivia', 'Liam', 'Sophia', 'Mason', 'Ava', 'Jacob', 'William', 'Isabella']
head, *students, tail = students
print('Head: {}, Students: {}, Tail: {}'.format(head, students, tail))
If you use a star before variable name in a left side of assignment, it means packing variables.
print(students)
print(*students) # Is equal to print('Noah', 'Olivia', ...)
Packing and unpacking won't happen magically, you have to tell what python should do.
[1, 2, 3] == (1, 2, 3) # No packing or unpacking. No usage here.
There is a faster and better way to define a list.
squared = [num**2 for num in range(100)]
print(squared)
A little more complex comprehension, let your code decide to add a value or not to add the value there.
squared_cubed = [num for num in squared if round(num ** (1/3)) ** 3 == num]
print(squared_cubed)
Consider list below:
countries = ['USA', 'Germany', 'France', 'Iran']
Write piece of code that change upper list to:
countries = ['UK', 'USA', 'France', 'Iran', 'Canada']
countries = ['USA', 'Germany', 'France', 'Iran']
countries.insert(0, 'UK')
# countries = ['UK'] + countries
del countries[2]
# countries.remove('Germany')
countries.append('Canada')
print(countries)
Tuples are frozen Lists. It means when you define a tuple you can't add to it or remove an object from it. Even you can't change a object at specific index! It is freezed for ever.
alvin_marks = (12, 10, 17, 14)
Now is you pass alvin marks to any function or method or ... they can only read it and not changing it!
Tuples are much like lists. You can add two tuples and get new tuple. You can search for an object in it. You can do anything lists can do with tuples as long as what you want to do won't change it.
woody_marks = (19, 18.5, 20, 17)
alvin_woody_marks = alvin_marks + woody_marks
print(alvin_woody_marks)
alvin_got_12 = 12 in alvin_marks
print("Alving got 12? {}".format(alvin_got_12))
If you wanted to define a tupe with only one value in it, you should write it like:
a = (12,)
print(a)
They are like lists with a little differences. One of differences is that programmer defines indexes here! Indexes are called keys. A dictionary is like a rope, you can connect a keys to its value.
per2eng_dict = {
'سلام': 'Hello',
'خوبی؟': 'How are you?',
'پایتون': 'Python',
}
print(per2eng_dict['خوبی؟'])
Adding new value to a dictionary is so simple. Choose your key and value.
Using the same syntax for adding, you can update value of keys that exist.
per2eng_dict['خدانگهدار'] = 'Goodbye'
print(per2eng_dict['خدانگهدار'])
per2eng_dict['خوبی؟'] = 'How is it going?'
print(per2eng_dict['خوبی؟'])
You can use any immutable datatype as a key for your dictionary.
per2eng_dict[1396] = 2017
per2eng_dict[('شنبه', 'یک شنبه', 'دو شنبه')] = \
('saturday', 'sunday', 'monday')
print(per2eng_dict)
You can get all keys and values of a dictionary.
print(per2eng_dict.keys())
print(per2eng_dict.values())
Another example for dictionaries is in simple cryptography algorithm called substitution.
ciphertext = '''QRNE NYVPR,
V NZ N FCL NAQ JBEXVAT SBE HAVGRQ XVATQBZ. NF GUR SVEFG QNL V FGNEG ZL JBEX URER LBH JNF NYJNLF XVAQ GB ZR.
V JVYY ARIRE SBETRG LBHE URYCF.
FVAPRERYL, FNEN.'''
print(list(zip('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'NOPQRSTUVWXYZABCDEFGHIJKLM')))
# Generate a dictionary with comprehension
subs = {i: j for i, j in zip('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'NOPQRSTUVWXYZABCDEFGHIJKLM')}
#
# subs = {'J': 'W', 'Y': 'L', 'I': 'V',
# 'A': 'N', 'B': 'O', 'C': 'P',
# 'D': 'Q', 'W': 'J', 'F': 'S',
# 'U': 'H', 'Q': 'D', 'M': 'Z',
# 'P': 'C', 'E': 'R', 'K': 'X',
# 'Z': 'M', 'S': 'F', 'O': 'B',
# 'R': 'E', 'H': 'U', 'X': 'K',
# 'N': 'A', 'L': 'Y', 'G': 'T',
# 'V': 'I', 'T': 'G'}
# Characters that is not encrypted.
subs.update({'.': '.', ' ': ' ',
',': ',', '\n': '\n'})
plaintext = ''.join([subs[char] for char in ciphertext])
print(plaintext)
There is a better way to define dictionaries.
length_of_names = {name: len(name) for name in students}
print(length_of_names)
Just like lists, here we can use if
statement too.
long_names = {name: len(name) for name in students if len(name) >= 6}
print(long_names)
You can put unique object in a set.
numbers = {1, 5, 4, 1, 3, 2, 5, 2, 3, 5, 1}
print(numbers)
numbers.add(10)
print(numbers)
numbers.remove(3)
print(numbers)
even_nums = {2, 4, 6, 8, 10}
print(numbers & even_nums) #
print(numbers | even_nums)
print(numbers - even_nums)
Faster way to define a set.
all_marks = {mark for mark in alvin_woody_marks}
print(all_marks)
Again, you can use an if
.
high_marks = {mark*5 for mark in alvin_woody_marks if mark >= 16} # We change marks scale from 0 -> 20 to 0 -> 100
print(high_marks)
There are function with same name as datatypes that try to convert you data from any datatype, to that you want.
print(int(3.14))
print(int('-45'))
print(float(18))
print(float('3.14'))
print(list({1, 2, 3, 1, 4, 1, 2}))
print(set((10, 1, 5, 2, 9, 10, 4, 2, 1, 5)))
set2str = str({1, 2, 3, 1, 4, 1, 2})
print(type([]))
Some of datatypes are mutable and some of them are immutable.
If a datatype is mutable, if you change a copy of mutable datatype, you will change original variable.
shop_list = ['Egg', 'Milk']
new_shop_list = shop_list
new_shop_list.append('Sugar')
print(shop_list)
But if a datatype is immutable, if you change a copy of immutable datatype, you will not change original variable.
book_title = "Python for You and Me"
new_book_title = book_title
new_book_title = "Python 4 U & Me"
print(book_title)
Until now we had all of our code running sequentially. Now we need to group them, decide when to run what part of our code, or which part is going to be reused.
To define sub-code, we use indentation. Indentation is a set of whitespaces before the actual code begins.
password = input('Enter your password: ')
if password == 'blah blah blah':
print('Correct.')
Considering PEP8 it if better to use 4 spaces for each step of indentation.
There will be a time that you need to make a decision about age of a user, or correctness of a password, or information on a website and ... .
Decision making in Python is done with this three statements:
if CONDITION:
elif CONDITION:
else:
secret_knock = input('What is the secret knock? ')
if secret_knock == '1 2 1 3':
print('Correct, you can get in.') # This is indented code. This code is sub-code of if statement.
if secret_knock != '1 2 1 3':
print('Incorrect!')
number = int(input('Enter an odd number: '))
if number % 2 == 1:
print('That is correct.')
else:
print('This is an even number!')
age = int(input('How old are you? ')) # input function return datatype is always a string.
where_to_go = ''
if age < 7:
where_to_go = 'kindergarten'
elif age < 18:
where_to_go = 'school'
elif age < 120:
where_to_go = 'college'
else:
where_to_go = 'grave'
print('Then probabely you are going to {}.'.format(where_to_go))
In above code, if user input is 5, then all of the conditions evaluate to true, but the first one will be used for printing. When you use if
, else
or if
, elif
, else
there is a priority for conditions. If one of upper conditions comes true, none of conditions below the true one will be check or executed.
Python have six comparison operators:
Operator | Meaning |
---|---|
a < b |
a is smaller than b |
a <= b |
a is smaller than or equal to b |
a > b |
a is bigger than b |
a >= b |
a is bigger than or equal to b |
a == b |
a is equal to b |
a != b |
a is not equal to b |
You can combine conditions together with and
and or
operators.
Condition | Result |
---|---|
True and True | True |
True and False | False |
False and True | False |
False and False | False |
ali_age, ali_school, ali_friend = \
17, 'high school', {'mohammad', 'mahdi'}
amir_age, amir_school, amir_friend = \
16, 'high school', {'reza', 'mohammad'}
if ali_school == amir_school and ali_friend & amir_friend:
print('Ali and Amir are friends.')
Condition | Result |
---|---|
True or True | True |
True or False | True |
False or True | True |
False or False | False |
if ali_school == amir_school or ali_friend & amir_friend:
print('Ali and Amir are probabely friends.')
There is a very useful feature named Chained comparsion. In most programming languages like C, C++, Java, PHP, Drupal, ... this is not possible.
if 5 >= 2 <= 3:
print('Correct!')
if -1 <= (ali_age - amir_age) <= 1:
print('Ali and Amir have same age range.')
It like any operators, it have operands and a result.
age = int(input('How old are you? '))
print('You are {} young.'\
.format('so' if age < 25 else 'not so'))
If you were going to check if a variable is None
or True
or False
, considering PEP8, you have to use is
operator.
information_about_bermuda_triangle = None
if information_about_bermuda_triangle is None:
print('We don\'t know anything.')
rainy_day = False
if rainy_day is not False:
print('Use an umbrella.')
There is another operator that you can use in condition.
name = input('What is your name? ') # Mason
if name in students:
print('Your name is on the list.')
else:
print('Your name is not on the list!')
Any object is evaluated to True
or False
. In Python all empty built-in datatypes are evaluated to False
and in other situations evaluated to True
. As we told, bool
function get the object and return evaluated result.
if [False]:
print('List is true.')
else:
print('List is false.')
shop_list = ['Egg', 'Milk', '...']
for item in shop_list:
print('We should buy {}.'.format(item))
print('This is another print function.')
number = int(input('Which factorial number to calculate? '))
total = 1
for i in range(1, number + 1):
total *= i
print('Factorial of {} is {}.'.format(number, total))
biggest_num = int(input('Enter a number to get all prime numbers less than that: '))
primes = []
for num in range(2, biggest_num):
is_prime = True
for prime in primes:
if num % prime == 0:
is_prime = False
if is_prime:
primes.append(num)
print(primes)
total = 1
for prime in primes:
total *= prime
print(total)
for key in per2eng_dict:
print('With key {} we get value {}.'\
.format(key, per2eng_dict[key]))
This loop is used for situation when we don't know exactly when we should stop.
from random import randint
selected_num = randint(1, 1000)
num = 1
while num != selected_num:
num = int(input('Guess a number: '))
if num < selected_num:
print('Smaller!')
elif num > selected_num:
print('Bigger!')
else:
print('BINGO')
password = input('Enter the password: ')
while password != 'wonder woman':
password = input('Password was incorrect!\nPlease enter correct password: ')
Maybe you have complex situation that need to be checked and if they come true, the loop should stop. For this purpose you can use break
inside a loop.
print('Enter numbers to get total, finish with 0.')
total = 0
while True:
num = int(input('Enter your number: '))
if num == 0:
break
total += num
print('Total: {}'.format(total))
If you wanted to skip rest of a loop execution, you can use continue
.
print('Enter number to get total, finish with 0. Negative numbers are ignored.')
total = 0
while True:
num = int(input('Enter your number: '))
if num < 0:
continue
elif num == 0:
break
total += num
print('Total: {}'.format(total))
To create some of built-in datatypes you need use loops. For example to get students names and put them in a list.
students = []
for i in range(5):
student_name = input('Enter a name: ')
students.append(student_name)
print(students)
students = [input('Enter a name: ') for i in range(5)] # Shorter and faster way to do it.
print(students)
Get a number from user and create a list with comprehension that contains square of numbers in range [1, user_number).
user_num = int(input('Enter a number: '))
numbers = [num**2 for num in range(1, user_num + 1)]
print(numbers)
You can use comprehension with lists, set and dictionary.
num_count = int(input('How many number you have? '))
unique_nums = {int(input('Enter a number: '))\
for i in range(num_count)}
print(unique_nums)
num_of_words = int(input('How many words you want in your dictionary? '))
eng2per = {input('Enter english word: '):\
input('Enter persian translation of your word: ')\
for i in range(num_of_words)}
print(eng2per)
Maybe you don't want all of the thing you have, get into your list, set or dictionary, well you can use if statement for that in comprehensions.
words = ["Trip", "restaurant", "GOOD", "reviews", "expectations", "hIGh","service", "SLOW", "full"]
lowercase_words = [word for word in words if word.islower()]
print(lowercase_words)
You can nest loops for complex results like multiplication table.
from pprint import pprint
size = int(input('Enter size of multiplication table: '))
mul_tab = []
for row in range(1, size + 1):
mul_tab.append([])
for column in range(1, size + 1):
mul_tab[-1].append(row * column)
pprint(mul_tab)
print()
You can nest loops with comprehension too!
from pprint import pprint # Better way
size = int(input('Enter size of multiplication table: '))
mul_tab = [[row*col for col in range(1, size + 1)] for row in range(1, size + 1)]
pprint(mul_tab)
Another way of nesting
mul_tab_nums = {num for row in mul_tab for num in row}
print(mul_tab_nums)
1 1 2 3 5 8 13 21 34 55 ...
*
**
***
****
*****
******
fibo_num = [1, 1]
fibo_count = int(input('Enter fibo count: '))
# for i in range(2, fibo_count):
# fibo_num.append(fibo_num[-1] + fibo_num[-2])
nothing = [fibo_num.append(fibo_num[-1] + fibo_num[-2])
for i in range(2, fibo_count)]
print(nothing)
print(fibo_num)
num1, num2 = int(input('Enter first number: ')), int(input('Enter second number: '))
while num1 % num2 != 0:
num1, num2 = num2, num1 % num2
print(num2)
names = ['Jodi', 'Alice', 'Bob']
print(''.join(names))
size = int(input('Size of triangle: '))
for row in range(1, size + 1): # [start, stop)
stars = []
for i in range(row):
stars.append('*')
# print(stars)
print(''.join(stars))
# print('*' * row)
# print('\n'.join(['*' * row
# for row in range(1, size + 1)]))
Some parts of code is going to be reused again and again and again, we can't write them again and again and again! Remember don't repeat yourself (DRY). Function are reusable block of code that make coding easy. We can split tasks to function and use them any where. Functions improve readability and hide complexity.
Generally in Python function take zero, one or more input (called arguments) and return exactly one output (called returned value).
def greeting(name):
print('Hello {}'.format(name))
greeting('Alice')
print(greeting('Jack'))
Each function should have a very specific job and should not alter anything that is not necessary. So we can rewrite above code.
def greeting(name):
return 'Hello {}'.format(name)
print(greeting('Alice'))
print(greeting('Bob'))
A function can take multiple arguments.
from pprint import pprint
def multiplication_table(row, column):
return [[i*j for j in range(1, column + 1)] for i in range(1, row + 1)]
pprint(multiplication_table(4, 8))
Mutable and immutable values behave different when passed to a function. Mutable variables is pass by refrence but immutable variables is pass by value.
def add_one(a_list): # Mutable
a_list.append(1)
num_list = [3, 2, 9]
add_one(num_list)
print(num_list)
def add_one(a_string): # Immutable
a_string += 'ONE'
print('a_string is "{}"'.format(a_string))
numbers = 'TWO, '
add_one(numbers)
print(numbers)
You can explicitly pass an argument by using name of that argument.
pprint(multiplication_table(column=10, row=3))
Function arguments can have default values. Remember all arguments with default values should be last arguments.
def multiplication_table(row=10, column=10):
return [[i*j for j in range(1, column + 1)] for i in range(1, row + 1)]
pprint(multiplication_table()) # Use default values
pprint(multiplication_table(5, 5)) # Don't use default values
pprint(multiplication_table(3)) # Pass first argument and use second default value
pprint(multiplication_table(column=3)) # Even pass second value and use first default value
Function can get arguments of any count.
def total_of(*numbers):
total = 0
for number in numbers:
total += number
return total
print(total_of(1, 2, 3))
print(total_of(9018, 8712, 2376, 19, 268, 683, 1912875))
print(total_of())
Function arguments even can be labeled.
def keywords(**kwargs):
return(kwargs)
print(keywords(hi='سلام', hello='سلام', goodbye='خداحافظ'))
Function arguments have order to use, positional args, list args, keyword args.
def function(first, second, third=3, fourth='four', *fifth, **sixth):
print('First: {}'.format(first))
print('Second: {}'.format(second))
print('Third: {}'.format(third))
print('Fourth: {}'.format(fourth))
print('Fifth: {}'.format(fifth))
print('Sixth: {}'.format(sixth))
function(1, 2, 3, 4, 5, 6, test=123)
function(1, 2)
function(1, 2, test='keyword')
Each function always return exactly one data even when you don't return anything!
def no_return():
pass
result = no_return()
print(result)
With Python function you can simply return multiple values. They will be pack as a single tuple and returned.
def multiple_value_returned():
return 'Lightning McQueen', 11, ['Mater', 'Chick Hicks', 'Sally Carrera', 'Doc Hudson']
values = multiple_value_returned() # Single variable to store.
name, age, partners = multiple_value_returned() # Multiple variables to store.
print("Values: {}".format(values))
print("Name: {}, Age: {}, Partners: {}".format(name, age, partners))
Each function have its one scope of variables.
name = 'Tonto'
if name == 'Tonto':
age = 12
print('Age: {}'.format(age))
while age == 12:
age += 1
shopping_list = ['Milk', 'Egg']
print('Shopping List: {}'.format(shopping_list))
def func():
print('Func -> Age: {}'.format(age))
# print('Func -> Shopping List: {}'.format(shop_list))
var_in_func = 'Function'
func()
print(var_in_func)
def multiplication_table(row=10, column=10):
"""Get number of rows and columns of multiplication table then generate and returns a two dimensions list."""
return [[i*j for j in range(1, column + 1)] for i in range(1, row + 1)]
help(multiplication_table)
We can define nameless functions.
square = lambda x: x**2
print(square(10))
Everything about function arguments works with lambda functions too.
print((lambda a, b=3, **kwargs: (a, b, kwargs))('Hello', keyword='argument'))
If you call a function inside itself, it is called recursive function. Recursive function are useful for solving some complex algorithmic problems.
def factorial(n):
if n == 0:
return 1
return factorial(n - 1) * n
print(factorial(10))
def move_disks3(n, source, intermediate, destination):
"""Print movee needed to move disks in source tower to destination tower.
@param int n: Number of disk in source tower.
@param int source: Name of the source tower.
@param int intermediate: Name of intermediate tower.
@param int destination: Name of destination tower.
@rtype: None
"""
if n > 1:
move_disks3(n - 1, source, destination, intermediate)
move_disks3(1, source, intermediate, destination)
move_disks3(n - 1, intermediate, source, destination)
else:
print("{} -> {}".format(source,destination))
move_disks3(3, 'First', 'Second', 'Third')
def gcd(num1, num2):
"""This function calculate GCD of two numbers with recursive function."""
return gcd(num2, num1%num2) if num2 else num1
print(gcd(45, 33))
def prime_range(start, stop):
primes = []
for number in range(start, stop):
is_prime = True
for i in range(2, number):
if number % i == 0:
is_prime = False
break
if is_prime:
primes.append(number)
return primes
for prime in prime_range(1000, 1050):
print('{} is a prime number.'.format(prime))
# (stop)
# (start, stop)
# (start, stop, step)
def prime_range(start=None, stop=None, step=None):
"""A function with parameter that is completely like
range function but instead of generate natural numbers
here the function generates prime numbers."""
# Evaluate start, stop and step by what user of this
# function code want it to do.
if start is None:
print('WRONG')
return
elif stop is None:
start, stop, step = 2, start, 1
elif step is None:
step = 1
# A list to store all primes that I found.
primes = []
# Generate all primes from start to stop.
for number in range(start, stop):
is_prime = True
for i in range(2, number):
if number % i == 0:
is_prime = False
break
if is_prime:
primes.append(number)
# Slice primes with step that user wants and return
# it.
return primes[::step]
for prime in prime_range(1000, 1050):
print('{} is a prime number.'.format(prime))
print(prime_range(10))
print(prime_range(5, 15))
print(prime_range(10, 100, 5))
def prime_range(*args):
"""A function with parameter that is completely like
range function but instead of generate natural numbers
here the function generates prime numbers."""
# Evaluate start, stop and step by what user of this
# function code want it to do.
if len(args) == 1:
start, stop, step = 2, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
print('WRONG')
return
# A list to store all primes that I found.
primes = []
# Generate all primes from start to stop.
for number in range(start, stop):
is_prime = True
for i in range(2, number):
if number % i == 0:
is_prime = False
break
if is_prime:
primes.append(number)
# Slice primes with step that user wants and return
# it.
return primes[::step]
Python have built-in functions to help easy coding.
Print a documentation on object passed to it.
help(help)
We work with this one so many times. Print function have 3 labeled arguments:
end
: Specify how to end a printed text.sep
: Specify separation of each argument passed to print
function.file
: Specify a file to print text into that file.print(1, 2, 3)
print(1, 2, 3, sep='\n')
print(1, 2, 3, sep=', ', end='.')
print('Hello')
Get length of an object. You can define length of each object you define. Sequences and collections have length.
Return evaluted boolean value of an object. You can define boolean value of objects you define.
Convert object passed to it, to string representaion.
Convert object passed to it, to integer values.
Convert a sequence passed to it, to a set.
Convert a sequence passed to it, to a list.
Defining a dictionary with keyword argument of a function.
marks = dict(Joe='A', Bob='C', Alice='B+')
print(marks)
Convert a sequence passed to it, to a tuple.
Get three arguments, start
, stop
and step
and return an iterable object that count from start (including) to stop (excluding) with specified steps.
print(list(range(10)))
print(list(range(1, 10)))
print(list(range(1, 10, 3)))
Get any number of sequences and work exactly like a zip!
print(list(zip([1, 2, 3], ('one', 'two','three'))))
print(list(zip(range(1, 5), {'this', 'is', 'a', 'set'})))
print(list(zip([1, 2, 3], ['1', '2', '3'], ['one', 'two', 'three'])))
You should give one function with N
arguments, as the first argument of map
function and then give it N
sequences with same length. It will calculate function with those and save the output as a new sequence.
print(list(map(lambda x, y: x**y, [1, 2, 3], [10, 20, 30])))
As the name says, it is used to filter some of object out. The first parameter would be a function that if returns True
the object stays, it returns false
the object is filtered.
numbers = list(range(1, 20))
print(numbers)
print(list(filter(lambda x: x%2, numbers)))
Get a sequence and compare each of objects inside the sequence with each other and return minimum/maximum value.
greeting ='Hello World!'
print('Min: "{}", Max: "{}"'.format(min(greeting), max(greeting)))
numbers = [12, 6, 47, -52, 158]
print('Min: {}, Max: {}'.format(min(numbers), max(numbers)))
numbers = {12, 6, 47, -52, 158}
print('Min: {}, Max: {}'.format(min(numbers), max(numbers)))
marks = {'John': 18, 'Sophia': 12, 'Sara': 16.7}
print('Min: {}, Max: {}'.format(min(marks), max(marks)))
Default precision should be 5 digits. You have to write proper documentation for your function.
[(2, 2), (3, 1)]
def fraction(frac_num):
result = 1.0
for i in range(1, frac_num + 1):
result *= i/(2*i+1)
return result
def pi(precision=5):
total = 0.0
i = 0
while fraction(i) > 10**-precision:
total += fraction(i)
i += 1
return total * 2
print(pi(8))
def factors(number):
result = []
for prime in prime_range(number + 1):
# Find the right superscript for specific prime number.
superscript = 0
while number % prime == 0:
number /= prime
superscript += 1
# If superscript is bigger that zero, we add it to result
if superscript:
result.append((prime, superscript))
return result
print(factors(2017))
print(factors(21000))
All data we have is store in files. Even this document you are reading is store in a file. Python have very good API and very very libraries to work with files.
# Simple reading from file.
hello = open('hello')
print(hello.read())
print(hello.read())
hello.close()
The open
function first argument is a path to file, second argument is to specify which mode to open the file.
Modes to open a file is:
Character | Meaning |
---|---|
r | open for reading (default) |
w | open for writing, truncating the file first |
x | open for exclusive creation, failing if the file already exists |
a | open for writing, appending to the end of the file if it exists |
b | binary mode |
t | text mode (default) |
+ | open a disk file for updating (reading and writing) |
U | universal newlines mode (deprecated) |
def rwfile_test():
rwfile = open('rwfile', 'a+')
rwfile.seek(0, 0)
data = rwfile.read()
line_count = data.count('\n')
print('Line Number {}.'.format(line_count + 1), file=rwfile)
rwfile.close()
open('rwfile', 'w').close() # Clear file.
for i in range(5):
rwfile_test()
print(open('rwfile').read())
You can even save your objects to a file. pickle
module can do that for you.
# Sample save and load binary object to a file in binary mode.
from pickle import dump, load
shopping_list = ['Milk', 'Egg', 'Bread', 'Sugar']
shopping_list_file = open('shop-list-file', 'wb')
dump(shopping_list, shopping_list_file)
shopping_list_file.close()
shopping_list_file = open('shop-list-file', 'rb')
loaded_shopping_list = load(shopping_list_file)
print(loaded_shopping_list[0])
For each file you should open it to use and then you have to close it. Without closing file, changes that you made to file will be ignored!
It is easier to use with
statement instead of opening and closing a file yourself.
with open('rwfile') as rwfile:
print(rwfile.read())
# Phase 1: Read information and write it to user.
from pathlib import Path
from pickle import load, dump
data = Path("data")
if data.exists():
with open('data', 'rb') as data_file:
print(load(data_file))
# Phase 2: Get information from user and write
# them to file.
num_of_names = int(input('Enter number of names: '))
user_data = [input('Name: ') for i in range(num_of_names)]
with open('data', 'wb') as data_file:
dump(user_data, data_file)
# 1- Open file
# 2- Read all of file
# 3- Convert all file information to a words
# 4- Count each word and create a dictionary
# 5- Return it!
w = "a b c d"
print(w.split())
def frequency_of_words(filename):
with open(filename) as f:
words = f.read().split()
result = {word:words.count(word) for word in set(words) if word.isalpha()}
return result
print(frequency_of_words('todo.md'))
def frequency_of_word(word, fname):
return frequency_of_words(filename=fname)[word]
print(frequency_of_word('section', 'todo.md'))
When your project is getting bigger you have to organize your codes into different files and even different folders. To create new modules just create a file with anyname you want and then import it into your own code when you need it.
import datetime
print(datetime.__file__)
print(datetime.__name__)
from math import pi
print(pi)
import datetime as dt
print(dt.datetime.now())
help(datetime)
from primes import prime_range
print(prime_range(10, 100))
Errors happen. User error or logical error are not always avoidable. Standard way of error happening is called exceptions. An exception occures when an error is occured. You have to always be ready for exceptions and try the code for any exception.
# How to try for exceptions.
try:
print('always execute')
print(int('hi'))
print('end')
except TypeError as e:
print('Type Error occured.')
except ValueError as ve:
print('Value Error occured.')
print('Hello world!')
Different type of exceptions can happen. You can specify different section for each one of them.
# How to trye for different exceptions.
number = input('Enter a number: ') # hi
try:
print('Square of {} is {}.'.format(number, int(number)**2))
print(some_name_that_does_not_exist)
except ValueError as ve:
print(ve)
except NameError as ne:
print(ne)
except SyntaxError as se:
print(se)
print('Running code continues.')
There are other sections:
finally
: That will executed in any condition. If exception happened or didn't, if we try for it or we didn't and ... .else
: That will executed if no exception happens.# How to trye for different exceptions.
number = input('Enter a number: ') # 123
some_name_that_exist = 'No error from this part.'
try:
print('Square of {} is {}.'.format(number, int(number)**2))
print(some_name_that_exist) # Change to some_name_that_does_not_exist
except ValueError as ve:
print(ve)
else:
print('Else Section')
finally:
print('Finally Section')
print('Running code continues.')
num1, num2 = input('num1: '), input('num2: ')
try:
num1, num2 = int(num1), int(num2)
except ValueError as ve:
print('ERROR: You should enter numbers!')
else:
try:
print(num1/num2)
except ZeroDivisionError as zde:
print('ERROR: Second number can not be zero!')
If you wanted to signal an error you can do it by using Exception
object for now. After OOP section, we learn new and more standard way for defining our exception.
def multiplication_table(row=10, column=10):
if (row <= 0 or column <= 0):
raise Exception('{} argument is {}. It should be bigger than zero!'
.format(*(['Row', row] if row <= 0 else ['Column', column])))
return [[i*j for j in range(1, column + 1)] for i in range(1, row + 1)]
# print(multiplication_table(-1, 5))
try:
print(multiplication_table(2, -3))
except Exception as e:
print(e)
You can even make this code smaller and more readable by using assert
. It gets a condition and an exception, if condition is False
, the exception raised.
def multiplication_table(row=10, column=10):
assert row >= 0 and column >= 0, Exception('{} argument is {}. It should be bigger than zero!'
.format(*(['Row', row] if row <= 0 else ['Column', column])))
return [[i*j for j in range(1, column + 1)] for i in range(1, row + 1)]
try:
multiplication_table(-1, 5)
except Exception as e:
print(e)
Good for easy code designing, reusability and readability.
Concepts:
Simplest python class:
# A Class (code blueprints) that represent an Object (real world concepts).
class simple:
pass
# Creating an Instance of the object.
simple_var = simple()
class Car:
"""This is a simple Car class."""
number_of_wheels = 4
# It's defined withing the structures of our class.
print(Car.number_of_wheels)
# Each instance can see it from the class.
simple_car = Car()
print(simple_car.number_of_wheels)
# Class attributes updates through all instances.
Car.number_of_wheels = 3
not_so_simple_car = Car()
print(not_so_simple_car.number_of_wheels)
print(simple_car.number_of_wheels)
Class attribute can have shadows for each object instances.
class Car:
number_of_wheels = 4
strange_car = Car()
# Altering class attribute through instances make specific class attribute values for them.
strange_car.number_of_wheels = 18
print(Car.number_of_wheels)
print(strange_car.number_of_wheels)
# But it will break updated through class structure.
Car.number_of_wheels = 3
print(strange_car.number_of_wheels)
# Don't panic, you can bind your instance to your class again.
del strange_car.number_of_wheels
print(strange_car.number_of_wheels)
class Circle:
radius = 0
a = Circle()
a.radius = 13
b = Circle()
b.radius = 1.3
Each method in a class have a default first argument that is not passed by what we code.
class Person:
name = 'Nobody'
def print_name(self, a, b, c):
print(self.name)
print(a, b, c)
david = Person()
# david.name = 'David'
david.print_name(1, 2, 3)
john = Person()
john.name = 'john'
john.print_name('a', 'b', 'c')
Now that we can access instances, we can define instance attributes.
class Person:
def set(self, name, age):
self.name = name
self.birth_year = 2017 - age
def get(self):
return self.name, self.birth_year
bob = Person()
bob.set(name='Bod', age=25)
print(bob.get())
class Circle:
PI = 3.1415926535
def set(self, radius):
self.radius = radius
def area(self):
return self.PI * self.radius ** 2
c = Circle()
c.set(5)
print("Area of a circle with redius {} is {}".format(c.radius, c.area()))
These methods have specific name and specific job. They have names that starts and ends with __
When we are creating an instance, __init__
is the very first method that get called automatically.
class Person:
def __init__(self, name, age):
self.name, self.age = name, age
def get(self):
return self.name, self.age
alice = Person('Alice', 21)
print(alice.get())
Some of these special methods have is use by built-in operator like len
function. By writing right special methods you can overload those behaviours.
class Person:
def __init__(self, name, age):
self.name, self.age = name, age
def __len__(self):
return self.age
def __bool__(self):
if self.age > 18:
return True
else:
return False
# return True if self.age > 18 else False
# return self.age > 18
def __iter__(self):
return self.name.__iter__()
def __str__(self):
return "{} is {} year{} old.".format(self.name, self.age, 's' if self.age > 1 else '')
me = Person('Amir', 22)
print(len(me))
str(me)
if me:
print('You are allowed to vote.')
else:
print('You are not allowed to vote.')
print(me)
for p in me:
print(p)
You can find all information on special methods in Here.
Normal methods need an instance to do operation on it. Static methods on the other hand get called from class name. In this way you can categorize your function under a title.
class String:
@staticmethod
def reverse(string):
return string[::-1]
@staticmethod
def is_upper_lower(string):
"""Checks for strings like ``, `A`, `a`, `Aa`, `aA`, `AaA`, `aAa`, ... ."""
last_upper, last_lower = False, False
for character in string:
if not last_upper and not last_lower:
if character.isupper():
last_upper = True
else:
last_lower = True
else:
if last_upper and not last_lower and character.islower():
last_upper, last_lower = False, True
elif not last_upper and last_lower and character.isupper():
last_upper, last_lower = True, False
elif (last_upper and last_lower) or (not last_upper and not last_lower):
raise Exception("There is bug in the code! last_upper: {} and last_lower: {}"
.format(last_upper, last_lower))
else:
return False
return True
# part1, part2 = string[::2], string[1::2]
# return part1.islower() and part2.isupper() or part1.isupper() and part2.islower()
@staticmethod
def unique_words(string, case_sensitive=False):
return set([s if case_sensitive else s.lower() for s in string.split()])
print(list(map(String.reverse, ['', 'a', 'ab', 'aa', 'aba', 'abc', 'aaa'])))
print(list(map(String.is_upper_lower, ['', 'a', 'A', 'aA', 'Aa', 'aAa', 'AaA', 'ab', 'AB'])))
print(String.unique_words('What you want is unique word, so you should get what you want!', True))
They are special creators. In some ways they are like __init__
. They create an instance. The first parameter of a class method is always class itself.
class Celsius:
def __init__(self, temperature):
self.temperature = temperature
@classmethod
def from_fahrenheit(cls, fahrenheit):
return cls((fahrenheit - 32) * 5 / 9)
@classmethod
def from_kelvin(cls, kelvin):
return cls(kelvin - 273.15)
def __str__(self):
return "Temperature in celsius is {:0.2f}°C.".format(self.temperature)
print(Celsius(15))
print(Celsius.from_fahrenheit(98.6))
print(Celsius.from_kelvin(300))
If you have mutiple static methods and use some of in other ones, you have to hardcode the class name. This is not so good, we have so make changes as small as possible when class name is going to change. Another usage of class methods are for this problem.
class String:
@classmethod
def is_palindrome(cls, string, case_insensitive=True):
string = cls._strip_string(string)
# For case insensitive comparison, we lower-case string
if case_insensitive:
string = string.lower()
return cls._is_palindrome(string)
@staticmethod
def _strip_string(string):
return ''.join(c for c in string if c.isalnum())
@staticmethod
def _is_palindrome(string):
for c in range(len(string) // 2):
if string[c] != string[-c -1]:
return False
return True
@staticmethod
def get_unique_words(string):
return set(string.split())
print(String.is_palindrome('A nut for a jar of tuna')) # True
print(String.is_palindrome('A nut for a jar of beans')) # False
Main purpose of OOP is reusing existing codes. Inheritance (is-a relationship) and composition (has-a relationship) are two main ways to reuse code.
class Engine: # Base class, Parent class
def start(self):
return 'Engine started.'
def stop(self):
return 'Engine stopped.'
class Car:
def __init__(self):
self.engine = Engine() # Composition
class MechanicalEngine(Engine): # Inheritance
def __init__(self):
self.max_speed = 120
# Above, Engine is called Parent, Base class and MechanicalEngine is called Child, Derived class.
motorcycle_engine = MechanicalEngine()
print(motorcycle_engine.start()) # MechanicalEngine class inherits attributes and methods of Engine class.
c = Car()
print(c.engine.start())
If you overwrite a method of parent class, the functionality of that method from parent class is gone.
class Book:
def __init__(self, title, author):
self.title, self.author = title, author
def get_info(self):
return "Title: {}, Author: {}".format(self.title, self.author)
class EBook(Book):
def __init__(self, title, author, online_id):
# Not so good way! What if Book init changes? What if we didn't know Book init?
self.title, self.author, self.online_id = title, author, online_id
# Better solution BUT, what Book is not the only one we inherits? What if we
# wanted to change Book to Article?
Book.__init__(self, title, author)
self.online_id = online_id
# Best solution!
super().__init__(title, author)
self.online_id = online_id
def get_info(self):
book_info = super().get_info()
return "{}, Online ID: {}".format(book_info, self.online_id)
alice_in_wonderland = EBook('Alice in Wonderlang', 'Lewis Carroll', 1298)
print(alice_in_wonderland.get_info())
class Person:
def __init__(self, name, age, height, x, y):
self.name, self.age, self.height = name, age, height
self.x, self.y = x, y
def walk(self, move_x, move_y):
self.x += move_x
self.y += move_y
def __str__(self):
return '{} at {}, {}'.format(self.name, self.x, self.y)
class Employee(Person):
def __init__(self, name, age, height, x, y, salary):
super().__init__(name, age, height, x, y)
self.salary = salary
def __str__(self):
return "{}, S: {}".format(super().__str__(), self.salary)
ahmad = Person('Ahmad', 16, 180, 4, 18)
print(ahmad)
Sara = Employee('Sara', 25, 180, 10, 5, 100)
print(Sara)
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def move(self, move_x, move_y):
self.x += move_x
self.y += move_y
class Line:
def __init__(self, point_1, point_2):
self.point_1 = point_1
self.point_2 = point_2
def gradient(self):
x1, y1 = self.point_1.x, self.point_1.y
x2, y2 = self.point_2.x, self.point_2.y
if x1 - x2 == 0:
return float('inf')
return (y1 - y2) / (x1 - x2)
l = Line(Point(2, 3), Point(2, 4))
print(l.gradient())
Put names, comma seperated, and know that order of them matters (diamond problem)!
As the following code shows, MRO like our class then parents of our class then grandparents of out class and so on.
import math
class Shape:
tag = 'Shape'
def area():
return 0
class Rectangle(Shape):
tag = 'Rectangle'
def __init__(self, a, b):
self.a, self.b = a, b
def area(self):
return self.a * self.b
class Rhombus(Shape):
tag = 'Rhombus'
def __init__(self, a, theta):
self.a, self.theta = a, theta
def area(self):
return self.a * self.a * math.sin(self.theta)
class Square(Rectangle, Rhombus):
def __init__(self, a):
# For Rectangle
super().__init__(a, a)
# For Rhombus
# super().__init__(a, math.pi / 2)
my_square = Square(10)
print("Area of our square is: {} and Tag is: {}".format(my_square.area(), my_square.tag))
Every thing in python is public! And everyone working with it without a single problem. But there is an agreement on it.
If you use a single underscore at the beginning of names you use, means this is used for the inner workings of your code and it is better not to manipulate it.
If you use two or more underscore at the beginning and at most one underscore at end of names you use, means this is more special name that even child classes should not manipulate it.
class A:
def __init__(self):
self._private = 'Private value from A'
self.__sensitive = 'Sensitive value from A'
class B(A):
def __init__(self):
super().__init__()
self._private = 'Private value from B'
self.__sensitive = 'Sensitive value from B'
def how_to_change_private_values(self):
self.__sensitive = 'Use the name defined value with.'
a, b = A(), B()
print('A Private: {}, Sensitive: {}'.format(a._private, a._A__sensitive))
print('B Private: {}, Sensitive: {}'.format(b._private, b._B__sensitive))
print('B also include A sensitive to A works find.')
print('A sensitive value in B: {}'.format(b._A__sensitive))
When you use two or more underscores at the beginning of a name, the actual name, that is reachable from outside class scope is mangled. Mangling is the process of adding class specific name to beginning of the name you defined. For example __VARNAME
changes to _CLASSNAME__VARNAME
.
isinstance()
to find a varialble origins.isinstance(motorcycle_engine, MechanicalEngine)
issubclass()
to find examine inheritance relation between to classes.issubclass(MechanicalEngine, Engine), issubclass(Engine, MechanicalEngine)
class Person:
def __init__(self, firstname, lastname):
self.__firstname = firstname
self.__lastname = lastname
@property
def full_name(self):
return '{} {}'.format(self.__firstname, self.__lastname)
@full_name.setter
def full_name(self, full_name):
fname_splited = full_name.split()
self.__firstname = fname_splited[0]
self.__lastname = ' '.join(fname_splited[1:])
p = Person('Nikola', 'Tesla')
print(p.full_name)
p.full_name = 'Thomas Alva Edison'
print(p.full_name)
yield
yeild from
Example: Decorator to calculate up execution time.
Homeworks:
Notes to talk:
Python have libraries. pip
is a program that can install and remove your packages. This a fast and safe way to get a python library for our projects.
Consider situations like 2 projects with same dependencies of different versions. Like pip
, virtualenv
helps you manage your packages but in different way.
pip3 install virtualenv
virtualenv .venv # Creating python virtual environment
source .venv/bin/activate # Activating virtual environment
deactivate # Deactivating virtual environment
Python has many many libraries for managing databases. Here we use SQLite3 that comes with Python when you installed it.
import sqlite3
import os
try:
os.remove('simple-db')
except FileNotFoundError as e:
pass
db = sqlite3.connect('simple-db')
# Create a new table.
db.execute('create table music ( title text, artist text, rating int)')
db.commit()
# Insert new data to music table.
db.execute('insert into music (title, artist, rating) values ("Immigrant Sont", "Led Zeppelin",8)')
db.execute('insert into music (title, artist, rating) values ("GOT", "Ramin Djawadi",9)')
db.execute('insert into music (title, artist, rating) values ("The map of reality", "The New Smiths",9)')
db.commit()
# Grab stored data from our database.
table = db.execute('select * from music')
for row in table:
print(row)
We use tkinter
module for this part of our course. To start we need to import the module first.
# 3 ways to import tkinter module
from tkinter import * # This is very bad way. We import everything in the module to global scope.
# There is chance that we overwrite something imported from or to tkinter module.
import tkinter # This way is not best but works fine. The name is long to type.
import tkinter as tk # This is best way to do this.
If you have problem with importing tkinter
you might want to check the installation of it. Read the error completely there should be some clue to how to fix it or search the error online for more clues.
Three components for GUI programming:
Every thing is part of a big tree structure.
With tkinter
there is no need to start from scratch and invent the wheel! The root window
is what you have to start with. A window that have basic functionality that you need.
The concept of a mainloop
is like a manager. You are the one who write standards an law, but someone else know better how to execute those.
root = tk.Tk() # root variable is filled with a basic root window object.
root.mainloop()
Widgets are building blocks of a GUI application. They work together to serve an application goal. Labels, text inputs, button, sliders, image viewer, tabs and even root window are widgets.
root = tk.Tk()
greetings_label = tk.Label(root, text='Hello User!') # Attaching a label to root with some text.
greetings_label.pack() # This method should be called to define a position for our new widget.
root.mainloop()
Remember all widgets are objects that is created and returned by a specific functions like Label
. Then you configure them as you like them to behave (OOP) and then call the manager to do dirty works!
root = tk.Tk()
username_frame = tk.Frame(root)
tk.Label(username_frame, text='Username').pack(side=tk.LEFT)
username_entry = tk.Entry(username_frame)
username_entry.pack(side=tk.LEFT)
username_frame.pack(fill=tk.X)
password_frame = tk.Frame(root)
tk.Label(password_frame, text='Password').pack(side=tk.LEFT)
password_entry = tk.Entry(password_frame, show='*')
password_entry.pack(side=tk.LEFT)
password_frame.pack(fill=tk.X)
def login():
username = username_entry.get()
password = password_entry.get()
if username == 'username' and password == 'password':
print('Correct username and password.')
else:
print('ERROR: Wrong username and password. username: {}, password: {}'.format(username, password))
button_frame = tk.Frame(root)
login_button = tk.Button(button_frame, text='Login', command=login)
login_button.pack(fill=tk.X)
button_frame.pack(fill=tk.X)
root.mainloop()
Three ways to manage:
root = tk.Tk()
frame = tk.Frame(root)
tk.Label(frame, text='Pack demo of side and fill.').pack()
tk.Button(frame, text='A').pack(side=tk.LEFT, fill=tk.Y)
tk.Button(frame, text='B').pack(side=tk.TOP, fill=tk.X)
tk.Button(frame, text='C').pack(side=tk.RIGHT, fill=tk.NONE)
tk.Button(frame, text='D').pack(side=tk.TOP, fill=tk.BOTH)
frame.pack()
tk.Label(root, text='Pack demo of expand.').pack()
tk.Button(root, text='I do not expand').pack()
tk.Button(root, text='I do not fill X but I expand').pack(expand=True)
tk.Button(root, text='I fill X and expand.').pack(expand=True, fill=tk.X)
root.mainloop()
Now learning python syntax and usages is finished. Let us start our big project!
The project is simple client chat. This applicatoin have 3 main parts.