Python
· Programming language · Computer programming · Python Sink ·
TOC
Datasheet
Paradigm | Multi-paradigm: object-oriented, procedural (imperative), functional, structured, reflective |
Designed by | Guido van Rossum |
Developer | Python Software Foundation |
First appeared | 20 February 1991; 30 years ago[2] |
Stable release | 3.10.2 14 January 2022; 13 days ago |
Preview release | 3.11.0a4 Edit this on Wikidata / 14 January 2022; 13 days ago |
Typing discipline | Duck, dynamic, strong typing; gradual (since 3.5, but ignored in CPython) |
OS | Windows, Linux/UNIX, macOS and more |
License | Python Software Foundation License |
Filename extensions | .py, .pyi, .pyc, .pyd, .pyo (prior to 3.5), .pyw, .pyz (since 3.5) |
Major implementations | CPython, PyPy, Stackless Python, MicroPython, CircuitPython, IronPython, Jython |
Dialects | Cython, RPython, Starlark |
Influenced by | ABC, Ada, ALGOL 68, APL, C, C++, CLU, Dylan, Haskell, Icon, Java, Lisp, Modula-3, Perl, Standard ML |
Influenced | Apache Groovy, Boo, Cobra, CoffeeScript, D, F#, Genie, Go, JavaScript, Julia, Nim, Ring, Ruby, Swift |
Description
Python is an interpreted high-level general-purpose programming language. Its design philosophy emphasizes code readability with its use of significant indentation. Its language constructs as well as its object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects.
Python is dynamically-typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly, procedural), object-oriented and functional programming. It is often described as a "batteries included" language due to its comprehensive standard library.
Guido van Rossum began working on Python in the late 1980s, as a successor to the ABC programming language, and first released it in 1991 as Python 0.9.0. Python 2.0 was released in 2000 and introduced new features, such as list comprehensions and a cycle-detecting garbage collection system (in addition to reference counting). Python 3.0 was released in 2008 and was a major revision of the language that is not completely backward-compatible. Python 2 was discontinued with version 2.7.18 in 2020.
Python consistently ranks as one of the most popular programming languages.
Books
Mark Lutz - Learning Python 5th edition
[[Mark Lutz - Learning Python, 5th Edition - 2013.pdf]]
Building Skills in Python
[[Building.Skills.in.Python.Steven.F.Lott.2010.pdf]]
Data types
Strings
String formatting
Inline
this method raise exception if the string does not contain %s
name = "Shaun"
age = 32
string = "Hi, %s!" % name
string = "Hi, %s!" % "Paul"
string = "%s is %d years old" %(name, age)
x = "dance"
print("Nancy can %s and %s too" %(x, "sing"))
print("The value of e is %5.2f" %(24553.718281)) # rounding
Format() method
print("Welcome to {} world!".format("Python"))
#positional
print("Let's {0} and {1}".format("dance", "chant"))
print("{} called {} to inform that {} will be absent.".format("Luke", "Diana", "John"))
print("{2} called {0} to inform that {1} will be absent.".format("Luke", "Diana", "John"))
print("We {a}, {b} and {c} with you".format(a = "learn", b = "upskill", c = "grow"))
String literals, f-strings
tutorial = "Python"
subject = "programming"
place = "internet"
print(f"Let's learn {tutorial} {subject} from {place}")
n1 = 60
n2 = 12
print (f"The product of {n1} and {n2} is {n1 * n2}")
num = 55
print(f"Is number {num} even? {True if num%2==0 else False}")
# alignment with spaces/zeros, < left, > right
fnum = 2.1239810
name = 'Anthony Crueger Jr.'
print(f'Name: {name:<20}') #
print(f'{fnum:<015.05f}') # leading and trailing zeros rounding to 3 digits
print(f'{fnum:>15.02f}')
# allows operations like this to fit both name title and name to 20 chars
print(f'{"Name:" + name:<20}')
# or
print(f'{"Name":>10} {":":>5} {name:>25}') # both aligned with spaces on the left
# formatting numbers
print(f'{fnum:.3f}') # float with 3 positions aligned with leading spaces
print(f'{fnum:.010f}') # float with 3 pos with aligned trailing zeros
print(f'{fnum:10}') # 10 positions aligned with leading spaces
print(f'{fnum:010}') # 10 positions aligned with leading zeros
print(f'{fnum:010.03f}') # 10 positions with leading and trailing zeros rounding to 3 digits
print(f'{fnum:<010.03f}') # 10 positions with leading and trailing zeros aligned to left rounding to 3 digits
print(f'{fnum:>010.03f}') #
String template class
# template symbol $
from string import Template
a1 = "Python"
a2 = "Internet"
n = Template("Hello, welcome to $n1 programming on the $n2")
print(n.substitute(n1 = a1, n2 = a2))
stu_name = "Long Johnson"
s = Template("Hey, $name! How are you?")
print(s.substitute(name = stu_name))
String operations
s = "the quick brown fox /jumps 8 over / a lazy dog"
print(s.split(" ")) # list of words
print(s[-10:]) # last 10 symbols
print(' '.join(s.rsplit(' ', 10)[-10:])) # get string of last 10 words
print(s.replace('/','\\'))
before, delim, after = s.partition("/")
import re
print(re.split(r'\d', s))
Lists, tuples, sets, dictionaries
Lists
Mutable arrays
courses = ["History", "Math", "Physics", "CompSci"]
print(len(courses)) # =4
print(courses[0]) # item by index
print(courses[-1]) # last item
print(courses[4]) # exception
print(courses[0:2]) # items from 0 (including) and 2 (excluding)
print(courses[2:]) # items from 2 (including) to the end
courses.append("Art") # add item to the end
courses.insert(0, "Art") # insert at position 0
courses2 = ["Art", "Education"] # new list
courses.insert(0, courses2) # inserts the list courses2 as a single list element
courses.extend(courses2) # adds the courses2's values to the end
courses.remove("math") # removes Math item
popped = courses.pop() # removes the last value and returns it
popped = courses.pop(0) # removes the first value and returns it
courses.reverse() # reverses the item order
courses.sort() # sorts alphabetically (if strings) or ascending (if numbers)
courses.sort(reverse=True) # sorts in revered order
sorted_courses = sorted(courses) # returnes a sorted version without altering the source list
nums = [1, 2, 3, 4, 5]
min(nums)
max(nums)
sum(nums)
courses.index("compSci") # finds the item's index
"Art" in courses # presence check
for item in courses: # order preserved
print(item)
for index, course is enumerate(courses):
print(index, course)
for index, course is enumerate(courses, start=1):
print(index, course)
courses = ["History", "Math", "Physics", "CompSci"]
course_str = ';'.join(courses)
new_list = course.split(";")
# swap elements
courses[0], courses[1] = courses[1], courses[0]
# slice object, also works for strings
rev: slice = slice(None, None, -1) # equal to [::-1]
numbers = list(range(1, 11))
text = 'Hello, world!'
print(numbers[rev])
print(text[rev])
Tuples
Immutable arrays
# Mutable
list1 = ["History", "Math", "Physics", "CompSci"]
list2 = list1
list1[0] = "Art" # will change the first item for BOTH of list
# Immutable
list1 = ("History", "Math", "Physics", "CompSci")
list2 = list1
list1[0] = "Art" # exception, no item assignment/change allowed
Sets
much more performant than lists and tubles
cs_courses = {"History", "Math", "Physics", "CompSci"}
print(cs_courses) # returns the items in random order
cs_courses = {"History", "Math", "Physics", "CompSci", "Math"}
print(cs_courses) # returns the set, but Math is present just once
print("Math" in cs_courses) # True
art_courses = {"History", "Math", "Art", "Design"}
print(cs_cources.intersection(art_courses)) # common items
print(cs_cources.difference(art_courses)) # uncommon items
print(cs_cources.union(art_courses)) # items from both sets, no repeats
set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}
print(set_a | set_b) # Union
print(set_a - set_b) # Subtraction
print(set_a & set_b) # Intersection (shared elements)
print(set_a ^ set_b) # Symmetric difference (unique elements only)
# Empty lists
empty_list = []
empty_list = list()
# Empty tuples
empty_tuple = ()
empty_tuple = tuple()
# Empty sets
empty_set = set() # the only way to create an empty set
empty_dictionary = {} # this creates an empty dictionary instead
Dictionaries
index names are immutable
student = {"name": "John", "age": 25, "courses": ["Math", "CompSci"]}
print(student["name"]) # returns "John"
print(student["phone"]) # throws exception
print(student.get("phone")) # returns None
print(student.get("phone", "Not found")) # returns Not found instead
student["phone"] = "555-5555"
student.update({"name": "jane", "age": 26, "phone": "555-5555"}) # a few chanes in one shot
del student["age"]
popped = student.pop("age")
student = {"name": "John", "age": 25, "courses": ["Math", "CompSci"]}
print(len(student))
print(student.keys()) # returns dict_keys(["name", "age", "courses"])
print(student.values()) # returns dict_values(["John", 25], ["Math", "ComSci"]])
print(student.items()) # returns dict_tems([("name", "John"), ("age", 25), ...])
for key, value in student.items():
print(key, value)
# sort dictionary
print(dict(sorted(student.items())))
Scopes
LEGB rule
Local >> Enclosing >> Global >> Built-in
Global and local
x = "global x"
def test():
x = "local x"
print(x) # local x
test()
print(x) # still global x
def test():
global x # x may not be defined earlier, not used often
x = "local x"
print(x) # local x
test()
print(x) # global x overwritten to local
def test(z): # z is in local scope
x = "local x"
print(z) # local x
test("local z")
print(z) # exception! no such global var
Built-in
import builtins
print(dir(builtins))
Enclosing
nested functions
def outer():
x = "outer x"
def inner():
x = "inner x"
print(x) # prints inner x
inner()
print(x) # prints outer x
outer()
print(x) # exception!
Conditionals
# if
language = "Python"
if language == "Python":
print("Language is Python")
elif language == "java":
print("Language is Java")
elif language == "Pascal":
print("Language is Pascal")
else:
print("No match")
# is
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True
print(a is b) # False, not the same object
print(id(a))
print(id(b)) # ids are different
b = a
print(a is b) # true, now the same object
Evaluation to True/False
Evaluates to False
- False
- None
- Zero of any numeric type
- Any empty sequence. For example,
'', (), []
- Any empty mapping. For example,
{}
Everything else evaluates toTrue
Inline if
a = 4
b = "yes" if a == 4 else "no"
Loops
nums = [1, 2, 3, 4, 5]
for num in nums:
if num == 3:
print("Found")
break
print(num)
nums = [1, 2, 3, 4, 5, 3]
for num in nums:
if num == 3:
print("Found")
continue
print(num)
for i in range(10): # not including 10
print(i)
for i in range(1, 11): # start, end, step
print(i)
x = 0
while x < 10:
print(x)
x += 1
Functions
def hello_func(greeting, name = "You"): # You is the default value, keyword argument
return "{}, {}". format(greeting, name)
print(hello_func("Hi"))
print(hello_func("Hi", name="Keerah"))
def student_info(*args, **kwargs): # arbitrary number of arguments
print(args)
print(kwargs)
student_info("Math", "Art", name="John", age=22)
# returns
# ("Math", "Art")
# {"name": "John", "age": 22}
courses = ["Math", "Art"]
info = {"name": "John", "age": 22}
student_info(courses, info)
# not equal to the previous use, it returns:
# (["Math", "Art"], {"name": "John", "age": 22})
# {}
student_info(*courses, **info)
# now it upacks the arguments and passes them
First-class functions
First-class functions are treated as first-class citizens.
First-class citizen (aka first-class object) is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, and assigned as a variable.
Higher order functions are the functions that can receive functions as arguments and return functions.
def square(x):
return x * x
f = square(5)
print(square) # returns <function ssquare at 0x...>
print(f) # returns 25
f = square # asign function to a variable
print(f) # now returns same <function ssquare at 0x...>
print(f(5)) # returns 25
def square(x):
return x * x
def my_map(func, arg_list)
result = []
for in in arg_list:
result.append(func(i))
return result
squares = my_map(square, [1, 2, 3, 4, 5]) # the square function name without (), cause parenthesis execute the fucntion, but we have to pass it as an argument
def logger(msg):
def log_message():
print("Log:", msg)
return log_message # returning the function, no parenthesis = no execution
log_hi = logger("hi!")
print(log_hi.__name__) # name of the returned function
log_hi()
html tag wrapping example
def html_tag(tag):
def wrap_text(msg):
print("<{0}>{1}<{0}>".format(tag, msg))
return wrap_text
print_h1 = html_tag("h1")
print_h1("Test headline!")
print_p = htma_tag("p")
print_p("Test paragraph")
Closures
A closure is a record storing a function together with an environment: a mapping associating each free variable of the function with the value or storage location to which the name was bound when the closure was created.
A closure, unlike a plain function, allows the function to access those captured variables through the closure's reference to them, even when the function is invoked outside their scope.
#import logging
#logging.basicConfig(filename="c:\temp\example.log", level=logging.INFO)
def logger(func):
def log_func(*args):
#logging.info("Running ""{}"" with arguments {}".format(func.__name__, args))
print("fake log:", "Running \"{}\" with arguments {}".format(func.__name__, args))
print(func(*args))
return log_func
def add(x, y):
return x+y
def sub(x, y):
return x-y
add_logger = logger(add) # this is a closure
sub_logger = logger(sub)
add_logger(3, 3)
sub_logger(10, 5)
Decorators
def decorator_function(original_function):
def wrapper_function():
return original_function()
return wrapper_function
def display():
print("display function ran")
decorated_display = decorator_function(display)
decorated_display()
# or another more common syntax
@decorator_function
def display():
print("display function ran")
display()
and more confusion with class decorators and multiple decorators at https://youtu.be/FsAPt_9Bf3U
Classes
Instance variables
contain data that is unique to each instance of the class
# empty class
class Employee:
pass
emp1 = Employee()
emp2 = Employee()
emp1.first = "Corey"
emp1.last = "Schafer"
emp1.email = "Corey.Schafer@company.com"
print(emp1.email)
# the next usage is equal but creates variables at init
class Employee:
# class constructor
# self is an instance of the class
def __init__(self, first, last, pay):
# there's no need to give them same names, but it's more consistent
self.first = first
self.last = last
self.pay = pay
self.email = first + "." + last + "@company.com"
# additional method to return full name
def fullname(self):
return "{} {}".format(self.first, self.last)
# self variable is passed automatically
# init method is run automatically
emp1 = Employee("Corey", "Schafer", 50000)
print(emp1.email)
# parenthesis used for methods, otherwise it's an attribute
# instance argument self is passed automatically
print(emp1.fullname())
# in backfround it get trsnsform into the next call
# self instance is explicit in this one
print(Employee.fullname(emp1))
Class variables
are the same in each instance
class Employee:
# Class variable
raise_amount = 1.04
def __init__(self, first, last, pay):
# there's no need to give them same names, but it's more consistent
self.first = first
self.last = last
self.pay = pay
self.email = first + "." + last + "@company.com"
def fullname(self):
return "{} {}".format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * Employee.raise_amount)
# or instance also can access it
self.pay = int(self.pay * self.raise_amount)
emp1 = Employee("Corey", "Schafer", 50000)
emp1.apply_raise()
print(emp1.pay)
# both class and instance contain this variable
print(Employee.raise_amount)
print(emp1.raise_amount)
# get the namespace of emp1
print(emp1.__dict__)
# the result is {"first": "Corey", "pay: 50000", "email": "Corey.Schafer@company.com", "last": "Schafer"}
# no raise anount in this list
# this one contains raise_amount
print(Employee.__dict__)
# this creates a new variable for this instance only
# now emp1 namespace contains its own raise_amount
emp1.raise_amount = 1.05
# now the following ones will work differently
self.pay = int(self.pay * Employee.raise_amount) # uses the class attribute
self.pay = int(self.pay * self.raise_amount) # uses raise_amount from the emp1 namespace
class Employee:
numof = 0
def __init__(self, name):
# runs everytime a new employee added
Employee.numof += 1
emp1 = Employee("Corey Schafer")
emp2 = Employee("Test User")
print(Employee.numof)
# returns 2
Methods
Class methods
class Employee:
numof = 0
raise_amount = 1.04
def __init__(self, name, pay):
self.pay = pay
Employee.numof += 1 # runs everytime a new employee added
# inside this we work with the class instead of the instance
# and using cls instead of self
@classmethod # class method decorator
def set_raise_amount(cls, amount):
cls.raise_amount = amount
emp1 = Employee("Corey Schafer", 50000)
emp2 = Employee("Test User", 50000)
# no self variable is passed, cause it addresses the class method
Employee.set_raise_amount(1.05)
# equals to
Employee.raise_amount = 1.05
# calling this method from an instance still changes the class attribute for the class and for the instances
emp1.set_raise_amount(1.05)
Class methods as alternative constructors
mostly for various methods of creating instances
class Employee:
numof = 0
raise_amount = 1.04
def __init__(self, name, pay):
self.pay = pay
Employee.numof += 1
@classmethod
def set_raise_amount(cls, amount):
cls.raise_amount = amount
# as alternative constructor
@classmethod
def from_string(cls.employee_str):
name, pay = employee_str.split(";")
return cls(name, pay) # calls the actual constructor and returns a new instance
emp1 = Employee("Corey Schafer", 50000)
emp2 = Employee.from_string("Test User;50000")
Static methods
regular functions, they do not pass class cls
or instance self
variables
class Employee:
numof = 0
raise_amount = 1.04
def __init__(self, name, pay):
self.pay = pay
Employee.numof += 1
@classmethod
def set_raise_amount(cls, amount):
cls.raise_amount = amount
@classmethod
def from_string(cls.employee_str):
name, pay = employee_str.split(";")
return cls(name, pay)
# static method to establish a workday
@staticmethod
def is_workday(day)
if day.weekday() == 5 or day.weekday() == 6:
return False
return True
import datetime
my_date = datetime.date(2016, 7, 11)
print(Employee.is_workday(my_date))
Inheritance
Inherit attributes
class Emloyee:
raise_amt = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + "." + last + "@email.com"
self.pay = pay
def fullname(self):
return "{ } { }".format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
# new class inherits the Employee class
class Developer(Employee):
#overriding the value for the subclass
raise_amt = 1.10
dev1 = Employee('Corey', 'Schafer', 50000)
# using the iherited
dev2 = Developer('Test' ,' Employee', 60000)
print(help(Developer)) # to see the method resolution order
dev2.apply_raise() # the amount is from the developer class
Inherit methods
class Emloyee:
raise_amt = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + "." + last + "@email.com"
self.pay = pay
def fullname(self):
return "{ } { }".format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
# new class inherits the Employee class
class Developer(Employee):
#overriding the value for the subclass
raise_amt = 1.10
def __init__(self, first, last, pay, prog_lang):
super().__init__(first, last, pay) # letting the parent class handle these
# or
Employee.__init__(self, first, last, pay) # more suitable for multiple inheritance
self.prog_lang = prog_lang
dev1 = Developer('Corey', 'Schafer', 50000, "Python")
dev2 = Developer('Test' ,' Employee', 60000", "Java")
print(dev1.email)
print(dev1.prog_lang)
class Emloyee:
raise_amt = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + "." + last + "@email.com"
self.pay = pay
def fullname(self):
return "{ } { }".format(self.first, self.last)
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
# new class inherits the Employee class
class Developer(Employee):
#overriding the value for the subclass
raise_amt = 1.10
def __init__(self, first, last, pay, prog_lang):
super().__init__(first, last, pay) # letting the parent class handle these
self.prog_lang = prog_lang
class Manager(Employee):
def __init__(self, first, last, pay, employees=None):
super().__init__(first, last, pay)
if employees is None:
self.employees = []
else:
self.employees = employees
def add_emp(self, emp):
if emp is not in self.employees:
self.employees.append(emp)
def remove_emp(self, emp):
if emp is not in self.employees:
self.employees.remove(emp)
def print_emp(self):
for emp in self.employees:
print("-->", emp.fullname())
dev1 = Developer('Corey', 'Schafer', 50000, "Python")
dev2 = Developer('Test' ,' Employee', 60000", "Java")
mgr1 = Manager("Sue", "Smith", 90000, [dev1])
print(mgr1.email)
mgr1.print_emps() # returns --> Corey Schafer
mgr1.add_emp(dev2)
mgr1.remove_emp(dev1)
mgr1.print_emps() # returns --> Test Employee
print(isinstance(mgr1, Manager) # True cause mgr1 is an instance of class Manager
print(isinstance(mgr1, Employee) # True cause mgr1 is an instance of class Employee
print(isinstance(mgr1, Developer) # False cause mgr1 is an instance of (sub)class Employee >> Manager not of subclass Developer
print(issubclass(Developer, Employee)) # True
print(issubclass(manager, Developer)) # False