I take notes here as I learn Python (Phase 1).
!!! warning Fragmented Content
This post content may appear fragmented or disjointed for I only noted down things I think I need to look at a second time. Therefore, this post is primarily meant for personal consumption.
Start Python
- Type
python.exe
to start Python interpreter, which turns the current terminal into a Python terminal.start python.exe
to open a Python terminal it in a new window. For method of starting exe, check my other post on CMD Commands.- If
python.exe
does not work and PATH is correct, trypy
,python
,python3
. - Use
-O
or-OO
switches on the Python command to reduce the size of a compiled module. The-O
switch removesassert
statements, the-OO
switch removes bothassert
statements and__doc__
strings.
- Type an end-of-file character (Ctrl-Z on Windows) or
quit()
at the primary prompt causes the interpreter to exit with a zero exit status. - Run a Python command after starting an interpreter by
python -c command [arg] ...
- Run any source code file with
python <file-name.py>
; Yes, run directly from the source code.- Use
-i
to run the script and after that open an interactive shell, which is usually what we want. That is,python -i <file-name.py>
. - Use
-v
to show howdoctest
are run verbosely, which is usually what we want. That ispython -m doctest -v <file-name.py>
. (They will run automatically and silently. Usually, if nothing shows up, all tests are passed.) - Python is an interpreted language. For difference between compiler and interpreted languages, see here.
- Use
- By default, Python source files are treated as encoded in UTF-8. The standard library only uses ASCII characters for identifiers.
- That means you can choose to use characters beyond ASCII - but do not do that! It is a convention that any identifiers should use ASCII characters.
- Change the encoding method by adding this as the first line of the source code file:
# -*- coding: xxx -*-
. (UNIX âshebangâ line should come even before that, if any)
# This works fine - but not recommended.
# Use ASCII only.
ă«ă»ăă = 0
æçæ°ç» = ['à€à€',ă«ă»ăă,4.99,("Ï","α")]
for ĐČĐ”ŃŃ in æçæ°ç»: print(ĐČĐ”ŃŃ)
- Besides the
print
calls, the Interactive Shell always prints the final returned value of each expression (not including evaluated results halfway; If the final returned value is a string, enclose them in quotes). If that value isNone
then nothing showed - but if youprint(None)
then aNone
is printed.
Behind the Hood
- Python evaluates a call expression with three steps:
- Evaluate the operator (operator are also expression that needs evaluation)
- Evaluate the operands (left to right)
- Apply the operator (a function) to the evaluated operands (arguments)
add(add(6, mul(4,6)), mul(3,5))
- add(add(6,24),mul(3,5))
- add(30,mul(3,5))
- add(30,15)
- 45
Python evaluates an assignment statement with two steps:
- Evaluate the expression at RHS and get its value
- Check to see if the variable name at LHS currently exists in the current frame.
- If it does, then do re-bind the variable to the new value.
- Otherwise, create a new variable in this frame, and set it to the given value.
Python interprets a function call in three steps:
- Create a new frame(local frame) in the environment
- Bind the argument passed in to the parametersâ names, which live on the new frame
- Execute the body of the function, in the new frame
Python handles a for loop:
for <name> in <expression>:
<suite>
Evaluate
<expression>
, which must returns a iterable value.For each element in that iterable, do:
- Bind
<name>
to that element in the current frame (no new frame!) - Run
<suite>
- Bind
Scope
A function is always evaluated in the same environment it was defined in, not where it is called.
- The function remembers the original frame it was defined in.
- Upon running and encountering a name, Python searches in the local frame first, and if not found it goes to search the parent frame. The parent frame is the frame it is defined but not where it is called.
- In other words, it searches names in the current environment. An environment is a sequence of frame, starting from the local frame propagating up along its parent frame chain, all the way up to the global frame.
- In terms of nested function definitions, this evaluation method allows the inner functions to see variables defined by the enclosing function.
x = 10 def f1(): x = 5 f2() def f2(): print(x) def f3(): x = 5 def f4(): print(x) f2() f1() # 10 f3() # 5
global
: access/create global variables (or even functions, classes or other objects) in a local context.nonlocal
: access/create a variable of local scope from inside a nested function.
# global
x = 50
z = 50
def bar():
global x,y
x = 10
y = 10
z = 10
return (x,y,z)
x1,y1,z1 = bar()
print(x1,y1,z1) #10 10 10
print(x,y,z) #10 10 50
# non-local
def some_fun():
my_var1 = 10
my_var2 = 20
def some_nested_fun():
nonlocal my_var2
my_var1 = 30
my_var2 = 40
print(my_var1, my_var2) # 10, 40
- Python searches for identifier in this order:
- Search in the local frame, and if not found
- Search in the parent frame, and if not found, search a higher level parent frame
- Until Python reaches the global frame. If the identifier is still not found, throw
NameError
.
Style
Naming
- Variables and functions are named with all lowercase letters, separated with underscores
_
. (name
,test
,do_this
).- Lead one underscore for internal methods and instance variables (
_my_protected_method
). - Lead one underscore for private methods variables (
__my_private_method
).
- Lead one underscore for internal methods and instance variables (
- Functions that perform comparisons and return boolean values typically begin with is, not followed by an underscore (
isfinite
,isdigit
,isinstance
). - Class and Error names should normally use the CapWords convention. (
MyClass
,Error
) - Module names should be all lowercase, without underscores. (
mymath.py
)
Imports
import
statement should be at the front of the file, just behind any module comment and docstring, and before module globals and constants.- Separate different module on different lines.
- Keep names in the same module at the same line.
import sys
import os
from types import StringType, ListType
- Order imported module declaration using this order, and separate each with a blank line.
- Standard lib imports
- Related major package imports
- Application-specific imports
Line Break
- One line break:
- Separate functions in a class
- Separate logical sections in a function (rarely)
- Two line breaks
- Separate groups of related functions
- No line breaks:
- Between a bunch of related one-liners.
- Between a bunch of c
White Spaces
- Yes:
- After commas, semicolons or colons
- Around an arithmetic/boolean/comparison or assignment operator (
+
,=
,<
,and
,not
,:=
)
- No:
- Before commas, semicolons or colons
- Inside parentheses, brackets or braces
- Do not surround
=
with white space when it is indicating keyword arguments or default parameter values.
Line, Indentation
- 4 spaces, or 2 spaces, or Tab?
- Max 72 characters (never exceed 79 characters) for one line.
- Use line break
\
.
- Use line break
- No two expression on the same line
Comments, Docstring, Doctest
- Block comments
- Should start with
#
(# and a space). - Have the same indentation level.
- Paragraphing is allowed in block comments. (Just put a
#
without other stuff for a line.) - Surround block comments by a blank line above and below.
- Should start with
- In-line comments
- Should start with
#
(# and a space). - Should be separated by at least two spaces from the statement they apply to.
- Should start with
- Docstring
- Write docstrings for all public modules, functions, classes, and methods.
- Docstring is not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the âdefâ line.
- One-line Docstring:
- The opening and closing âââ are on the same line.
- There is no blank line either before or after the docstring.
- Describes the function or methodâs effect as a command (âDo thisâ, âReturn thatâ), not as a description.
- Multi-line Docstring:
- The
"""
that ends a multiline docstring should be on a line by itself. - Script: The docstring for a script should be usable as its âusageâ message. It should document the scriptâs function, the command line syntax, and the environment variables.
- Module: The docstring for a module should generally list the classes, exceptions and functions (and any other objects) that are exported by the module, with a one-line summary of each.
- Class:
- The docstring for a class should summarize its behavior and list the public methods and instance variables.
- If the class is intended to be subclassed, and has an additional interface for subclasses, this interface should be listed separately.
- If a class subclasses another class and its behavior is mostly inherited from that class, its docstring should mention this and summarize the differences.
- The class constructor should be documented in the docstring for its
__init__
method. - Insert a blank line before and after any docstring that document a class.
- Function or method:
- The docstring should summarizes its behavior and document its arguments, return value, side effects, exceptions raised, and restrictions on when it can be called.
- Optional arguments should be indicated.
- Use the verb âoverrideâ to indicate that a subclass method replaces a superclass method and does not call the superclass method; use the verb âextendâ to indicate that a subclass method calls the superclass method.
- The docstring should contain a summary line, followed by a blank line, followed by a more elaborate description.
- The
Data Types
Numericals, Booleans
- Quick facts
- Python does not have
double
orchar
. Thefloat
is in double precision. /
division returns a float;//
floor division returns an integer. Use the**
operator to calculate powers.- In interactive mode, the last printed expression is assigned to the variable
_
. - Boolean literals are
True
andFalse
(instead oftrue
false
). - Logical operations are
and
or
andnot
instead of&&
||
and!
- BUT âdoes not equal toâ is still!=
. and
andor
does not returnTrue
orFalse
; it returns the first operand that determines the result;not
, however, always returns a boolean value.- Any object can be tested for truth value (put as an
if
condition). The following are consideredFalse
:None
, any numerical zero, any empty sequence/mapping ("",[]
etc), and any instances of user-defined class when__len__()
is defined and returns0
(integer), or when__nonzero__()
is defined and returnsFalse
. Any other values are true. - Bitwise operators are still
&
|
~
and^
. - Use
**
for exponents instead of^
. - No
char
orlong
. - Use
#
to lead a line comment, triple quotes to surround a multi-line comment. - Chain comparison operators like this:
1<x<5<x**2
- Ternary (
exp ? a : b
in many other languages) isa if exp else b
in Python. - Conversion can be done easily:
float('12')
,bool([])
,str(12312)
etc. - All numerical types support
max()
,min()
,abs()
,round()
andpow()
. Forsqrt()
, you have to importmath
. - The
operator
module has functions that corresponds to an operator.(e.g.add
mul
truediv
floordiv
);pow
is the only one already available. - Use
bin()
to get bit-string for numbers. For example,bin(3)
returns'0b11'
- Python does not have
- Complex: Python support natively
complex
type.
my_complex = 4.22 + 20j
my_complex = complex(4.22, 20)
- Fraction: Python has a library from fractions.
from fractions import Fraction
half = Fraction(1,2)
Strings
- String supports slicing,
+
,*
, and membership test. Two or more string literals (not variables or expressions) next to each other are automatically concatenated.
p = 'Py' 'thon'
print(p) # Python
print(p + p) #PythonPython
print(p * 3) #PythonPythonPython
longstr = ('Put several strings within parentheses '
'to have them joined together.')
print("234" in "12345") #True
- String supports indexing and slicing. Attempting to use an index that is too large will result in an error, but out of range slice indexes are handled gracefully (with modulo operation) when used for slicing.
- There is no
char
. Indexer returns a string.
- There is no
- Use
r
to lead non-escaped raw string literals (like@
in C#, e.g.,r'New lines are marked by \n'
). - Use
u
to lead a Unicode string literal (e.g.,u'Hello\u0020World !'
isHello World !
). - Use
""" """
(or''' '''
) to surround a string that spans multiple lines. - Strings are immutable; all methods that seem to modify string do not actually modify the original strings but return a new string.
eval
andexec
myStr = "012345abcde_WASD"
print (myStr.startswith('01') and myStr.endswith('D')) # prints True
print ('W' in myStr or 'w' not in myStr) # prints True
print (myStr.find('a')) # prints 6;
# the find() method can take in two optional arguments for start and end index to search
# same as Java/C# IndexOf; returns -1 if not found
# there is also a index() method which behaves the same but throw an error when not found
str2 = "this is string example....wow!!! this is really string"
print str2.replace("is", "was") # thwas was string example....wow!!! thwas was really string
print str2.replace("is", "was", 3) # thwas was string example....wow!!! thwas is really string
# the third optional argument limits the number of replacement
delimiter = '*-*'
myList = ['Me','My','I']
print(delimiter.join(myList)) # prints Me*-*My*-*I
txt = "Google#Taobao#Facebook"
print(txt.split("#")) # ['Google', 'Taobao', Facebook']
print(txt.split("#",1)) # ['Google', 'Taobao#Facebook']
# the second optional argument limits the number of split
# for multiple delimiters: import re and use regex
print ("My name is %s and weight is %d kg!" % ('Zara', 21))
print ("My name is {0} and weight is {1} kg!".format('Zara', 21))
# Both prints "My name is Zara and weight is 21 kg!"
# String literals can be used as variables directly.
print(len(my_string)) # Returns the length of a string
print(ord("c")) # 99 # Returns a Unicode of a character
print(chr(ord("c"))) # c # Returns Converted the Unicode to a character
String Formatting
Formatted string literals(âf-stringâ): String literals that have embedded expressions.
- f strings uses
str()
to evaluate each expression
from fractions import Fraction print(f"half = {Fraction(1,2)}") # half = 1/2
- expressions are evaluated in current environment
s = -3 x = f"abs(s)={abs(s)}" print(x) #abs(s)=3 abs = float print(f"abs(s)={abs(s)}") #abs(s)=-3.0 print(x) #abs(s)=3
- Remember this may have side-effects.
s = [1,2,3] print(f"last element is {s.pop()}") # now s only have two elements left
- f strings uses
Format String Syntax: Information about string formatting with
str.format()
.printf
-style String Formatting: The old formatting operations invoked when strings are the left operand of the % operator are described in more detail here.Style-making methods (stripping empty spaces, capitalization, justification and padding)
strip()
,upper()
,lower()
,swapcase()
. All these return a modified string without changing the original string
Encoding
from unicodedata import name, lookup
print(name('A')) # LATIN CAPITAL LETTER A
lookup('WHITE SMILING FACE') #âș
print('A'.encode()) #b'A'
print(lookup('BABY').encode()) #b'\xed\xa0\xbd\xed\xb1\xb6'
Related Built-in functions
# type() Returns the class_name of an object.
a = "What?"
print(type(a)) # str
b = 1+1j
print(type(b)) # complex
# isinstance() Checks if a object is an instance of a particular class. Returns True/False.
a = 23
print(isinstance(a, int)) # True
print(isinstance(a, float)) # False
print(isinstance(a, str)) # False
# id() Returns object id (int) of a object.
my_float = 50.0
# object id wil differ each time with program
print(id(my_float)) # 1875526208176
# is Keyword
# == tests for value equality. Use it when you would like to know if two objects have the same value.
## == does as what you think it does for strings and lists
## == uses the __eq__() method to compare two objects (if not defined, == check memory location equality)
## implementing __eq__() automatically makes instances of your class unhashable, which means they can't be stored in sets and dicts.
# is tests for reference equality. Use it when you would like to know if two references refer to the same object.
a, b = list, list[:]
print(a == b) #True
print(a is b) #False
Basic Syntax
Quick facts
- Python is a dynamically typed (type safety is checked at runtime). Variables are not declared as a certain type but are instead given a value directly.
- Do not need any semicolon at the end of the expression (you can add one if you want); but if multiple expressions are on the same line, separate them with a semicolon (this is a bad practice).
- Multiple assigment at the same time:
x, y = y, x + y
.This order of events â evaluating everything on the right of
=
before updating any bindings on the left â is essential for correctness⊠- De-construct assignment
(a, (b, c)) = (1, (2, 3))
first, *middles, last = range(5)
foo,bar = bar,foo
- Use
\<newline>
to split an expression to multiple lines. For example:
i = \
5
Flow Control
- Python use indentation instead of
{}
to mark code blocks. (This is because Python is an interpreter language?)- Python doesnât mind whether you use two spaces or four spaces (or any other number of spaces) as long as you are consistent.
- Inconsistent indentation results in
IndentationError: unindent does not match any outer indentation level
.
if
andwhile
use:
instead of()
.for
iterates over the items of any iterable in the order that they appear in the iterable. (Somewhat like theforeach
in C#).- Unlike
foreach
, code that modifies a collection while iterating over that same collection is allowed but error-prone. It is usually more straight-forward to loop over a copy of the collection or to create a new collection.
- Unlike
pass
is used as a placeholder to empty expression. It is used when a statement is required syntactically but you do not want any command or code to execute. Think ofpass
as an empty block.for
andwhile
can be followed by anelse
(the codes inelse
will be run after thefor
is finished, unless the program runs into abreak
).
#if
if guess == number:
print("Yes")
elif guess < number:
print("Too small")
else:
print("Too large")
if (guess == number): print("Ok") # in one line only
# Faster ways to test numbers
if 3 <= x < 9:
pass
if x in tange(3,8):
pass
#while
while True:
print("True")
while chunk := fp.read(200): # Assignment expressions using the walrus operator := assign a variable in an expression
print(chunk)
total = 0
while k <= n: # Compress the code
total, k = total + k, k + 1
#for
for i in range(1,5):
print(i) # 1,2,3,4
for _ in range(1,3): # When index is not important, use _
print("Hi!")
for j in reversed(range(1,6,2)):
print(j) # 5,3,1
for num in numbers: # numbers is a tuple or list
print(num)
for i, item in enumerate(iterable): # To access index, use enumerate()
print i, item
for x,y in [[1,2],[4,5],[0,9]]: # Unpack list in for
print(x, y)
# Here is another example:
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):#To loop two lists simultaneously, use zip()
print('What is your {0}? It is {1}.'.format(q, a))
#for and else
for i in foo:
if i == 0:
break
else:
print("i was never 0")
#pass
if 3 > 5: pass
else:
print("Why?")
Define a function
Return type is not specified at signature; by default
return None
.Return multiple values by passing out a tuple.
Keyword arguments
- Must not be followed by positional arguments (positionally accessed arguments). Never.
- Limit the ways that functions can be called with
/
and*
. This feature cannot be used together with variadic parameters.
def standard_arg(arg):
print(arg)
def pos_only_arg(arg, /):
print(arg)
def kywd_only_arg(*, arg):
print(arg)
def combined_example(pos_only, /, standard, *, kywd_only):
print(pos_only, standard, kywd_only)
- Parameters with default value
- Must not be followed by non-default parameters, except variadic positional parameters.
- Mutable default arguments are dangerous!
def foo(a, b = 1): # b as standard parameter
pass
def foo2(a, *, b = 1): # b as keyword-only parameter
pass
def foo3(a, b = 1, /): # b as positional-only parameter
pass
foo(1,2) # ok
foo(1,b=2) # ok
foo2(1,2) # TypeError: foo2() takes 1 positional argument but 2 were given
foo2(1,b=2) # ok
foo3(1,2) # ok
foo3(1,b=2) # TypeError: foo3() got some positional-only arguments passed as keyword arguments: 'b'
## Mutable default arguments are dangerous!
def f(s = []):
s.append(5)
return len(s)
f() # 1
f() # 2
f() # 3
- Variadic parameter: use
*args
or**kwargs
.- Any formal parameters which occur after the
*args
parameter are considered âkeyword-onlyâ arguments - these arguments have to be passed in by keywords. - Cannot have more than one
*
or more than one**
. **kwargs
must not be followed by any parameter.
- Any formal parameters which occur after the
def total(a = 1, *numbers, **phonebook):
print('a = {0}'.format(a))
total(100,1,2,3,4,Jack=123,Mike=357,b=15) # a = 100
- Variadic arguments will be last in the list of formal parameters.
- You cannot have any formal argument following a variadic dictionary argument.
def sumPow(*nums,pow=2):
sum = 0
for num in nums:
sum += num ** pow
return sum
print(sumPow(3,4,5)) #50 = 9+16+25
print(sumPow(3,4,pow=5)) #1267 =3^5+4^5
def bar(arg, kywd_arg = 0, *pos_vari_arg, **kywd_vari_arg):
print(arg, kywd_arg, pos_vari_arg, kywd_vari_arg)
bar(0,1,2,3,4,foo=5, test=6)
#0 1 (2, 3, 4) {'foo': 5, 'test': 6}
bar(0,1,2,3,4,foo=5, test=6, default_arg = 99)
#TypeError: bar() got multiple values for argument 'default_arg'
def bar2(arg, *vari_arg, default_arg = 99, **vari_kwarg):
print(arg, default_arg, vari_arg, vari_kwarg)
bar2(0,1,2,3,4,foo=5, test=6)
#0 99 (1, 2, 3, 4) {'foo': 5, 'test': 6}
- De-construct arguments
def showPow(x, y):
print(x**y)
point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}
showPow(*point_foo) # 81
showPow(**point_bar) # 8
- Anonymous functions
- If using Lambda: syntactically restricted to a single expression.
def
is a statement, whilelambda
is an expression. Evaluating adef
statement will have a side-effect, namely it creates a new function binding in the current environment. On the other hand, evaluating alambda
expression will not change the environment unless we do something with the function created by thelambda
. For instance, we could assign it to a variable or pass it as a function argument (most of the time we actually do those).
# Lambda: only a single expression as body (no statement!), and it will return that expression
cube = lambda x: x ** 3
print(cube(3)) # 125
print(len) # <built-in function len>
print(cube) # <function <lambda>>
# It does not have a intrinsic name! "cube" is just a way to refer to this func object
def make_repeater(n):
return lambda x: x * n
twice = make_repeater(2)
print(twice('word'))
print(twice(5))
- Function Annotations: You can annotate the parameters and return type and access it with the functionâs
__annotation__
field. This is completely optional and has no effect on functionâs functionality.
def f(ham: str, eggs: str = 'eggs') -> str:
return 'breakfast!'
print(f.__annotations__)
# This prints {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
- Rebinding functions
Just define a second function with the same signature below will override the first function/originally imported function. You can also assign value to a already existing function name.
from operator import add
print(pow(2,3)) # 8
def pow(x,y):
return "H!"
print(pow(2,3)) # H!
pow = add
print(pow(2,3)) # 5
Note that functions are also objects. Assignment and identity test are done on a reference level.
f = max # both max and f points to the built-in max object
max = min # the built-in max object has its no. of handle -1, because 'max' now points to the built-in min object
print(max(3,4)) # prints 3
print(f(3,4)) # prints 4
print(max == min) # prints True
print(max == f) # prints False (max now references the built-in min object)
print(min == f) # prints False
- Decorator
def trace(fn):
def wrapped(x):
print('{} is called on argument {}'.format(fn,x))
return fn(x)
return wrapped
def cube(x):
return x ** 3
cube = trace(cube)
@trace # This achieves basically the same effect as above
def square(x):
return x * x
Define a Class
- Constructors work the same way as in C#/Java. If a user-defined constructor is supplied, the parameterless argument will not be added by the compiler.
self
in Python is likethis
in C#/Java. You need aself
as the first argument for every class method.- All variables are public. Inside a class, use â_â underscore for protected, and â**â double underscore for private (e.g.,
**name
). - Refer here for special method names.
class Person: # Define a class
"""Represents a person"""
population = 0 # Static variable for the class
# Variables declared inside the class definition, but not inside a method are class or static variables
# Note that you can access this from instance level, too. So print(jack.population) works.
# You could have another "population" variable defined in instances as well!!!
@classmethod
def report_population(cls):
return cls.population
@staticmethod
def report_population_2():
return Person.population
# Both class method can static method can be called by instance or from class level
# The difference is class method takes in the current class as an argument
def __init__(self, name, age = 18): # Constructor
self.name = name # Instance variable - no need to declare beforehand
self.age = age
Person.population += 1 # Access the static variable
print(name + ' joined!')
def sayHi(self): # Any method defined in class should have the first argument: self
"""Give a greeting"""
print('Hello, I am {0}. How are you?'.format(self.name))
def __del__(self): # Destructor; Called when an instance is garbage collected
Person.population -= 1
if Person.population == 0:
print('I am the last one.')
else:
print(self.name + ' left!',end=" ")
print('There are still %d people left.' % Person.population)
def __str__ (self):
return 'I am a simple person named ' + self.name
# You can overwrite repr() too
def __lt__(self,other): # implement < operator
return self.name < other.name
# The other operators to implement: le, gt, ge, eq, ne, pos, neg, add/radd, sub/rsub, pow, mod, divmod, mul, and, or, xor, iadd(+=), isub(-=), lshift, rshift
# you can do __radd__ = __add__ if the add is commutative
def __bool__(self):
return self.age >= 18
# This is invoke when you try to convert a person object to bool.
# It is also used when you pass this object into if condition
# The other conversion methods can be: float, int
def __getitem__(self, key): # indexer
return name[key]
# you can have a __setitem__(self, key, value), __missing__(nonexistent_key) too
def __len__(self) # override len
return len(self.name)
def __call__(self, k):
print(k)
# If __call__ is defined, you could use this object like a function. It becomes callable.
Mike = Person('Mike') # Mike joined!
Sarah = Person("Sarah") # Sarah joined!
Sarah.sayHi() # Hello, I am Sarah. How are you?
Sarah = Mike # Sarah left! There are still 1 people left.
class StudyMixin: # Another class, acts as interface
pass
class Student(Person, StudyMixin): # Define a child class that inherits two parents
"""Represents a student; a child class of Person"""
def __init__(self, name, age, sch):
super().__init(name, age) # Use super()
self.school = sch
- Documentation Strings
- Declared using triple quotes just below the class, method declaration, or at the beginning of each source file(module), before the
import
statements. - Accessed by
__doc__
orhelp()
. - The doc string line should begin with a capital letter and end with a period.
- The first line should be a short description.
- If there are more lines in the documentation string, the second line should be blank; the following lines should be one or more paragraphs describing the objectâs calling conventions, its side effects, etc.
- There are various formats to write doc strings. Check here.
- The lines that begin with
>>>
are called doctest. Recall that when using the Python interpreter, you write Python expressions next to>>>
and the output is printed below that line. Doctests explain what the function does by showing actual Python code.
- Declared using triple quotes just below the class, method declaration, or at the beginning of each source file(module), before the
class SimpleClass:
"""Class docstrings go here."""
def say_hello(self, name: str):
"""Class method docstrings go here.
This method says hello!
>>> say_hello("Mike")
Hello Mike
"""
print(f'Hello {name}')
print(demo.__doc__)
help(demo)
- Enum
from enum import Enum
class Color(Enum):
RED = 1 #member values can be anything
GREEN = 2
BLUE = 3
FF0000 = 1 #Values can be repeated; they act as aliases
print(Color(1) == Color['RED']) # True
print(Color.RED is Color.RED) #True
print(Color.RED.name) # RED
print(Color.RED.value) # 1
print(Color.RED) # this gives Color.RED
type(Color.RED) # <enum 'Color'>
isinstance(Color.GREEN, Color) # True
for col in Color: print(col) # prints all colors
#Flag enum
class Perm(IntFlag):
R = 4
W = 2
X = 1
RWX = 7
- Related built-in functions
# string representation
# For most object types, eval(repr(statement)) = calling the statement
# In interactive shell, the result is print(repr(result))
>>> 12e12
1200000000000.0
>>> print(repr(12e12))
1200000000000.0
>>> s = "Hello"
>>> s
'Hello'
>>> repr(s)
"'Hello'"
>>> print(repr(s))
'Hello'
>>> print(s)
Hello
>>> str(s)
'Hello'
>>> print(str(s))
Hello
>>> eval(repr(s))
'Hello'
>>> repr(repr(repr(s)))
'\'"\\\'Hello\\\'"\''
>>> eval(s)
Error
# str(), repr() can only be called at a class level; any instance calling is still the same as calling at the class level. This means: str(a) is actually type(a).__str__(a) and not a.__str__()
# calling str() when there is no __str__ would use __repr__
class Bear:
def __init__(self):
self.__repr__ = lambda: 'override repr'
self.__str__ = lambda: 'override str'
def __repr__(self):
return 'repr'
def __str__(self):
return 'str'
>>> oski = Bear()
>>> oski
repr
>>> print(oski)
str
>>> str(oski)
'str'
>>> repr(oski)
'repr'
>>> str(Bear)
"<class '__main__.Bear'>"
>>> repr(Bear)
"<class '__main__.Bear'>"
>>> oski.__repr__()
'override repr'
>>> oski.__str__()
'override str'
# dot operator
hasattr()
setattr()
Iterator/Generator
- An iterable (Like
IEnumerable
in C#) is an object where we can go through its elements one at a time (e.g., can be iterated withfor
). Specifically, we define an iterable as any object where calling the built-initer
function on it returns an iterator; It must have an__iter__()
method that returns an iterator object (likeGetEnumerator()
in C# that returns anIEnumerator
object), or defines a__getitem__()
method that can take sequential indexes starting from zero (and raises anIndexError
when the indexes are no longer valid). - An iterator (Like
IEnumerator
in C#) is another type of object which can iterate over an iterable by keeping track of which element is next in the iterable. You can also calliter
on the iterator itself, which will just return the same iterator without changing its state. This class can be thought as a helper class which should implement__next()__
(and raisesStopIteration
upon running out). In C#, a helperIEnumerator
class which hasCurrent
MoveNext()
Reset()
must be implemented.- Usually, we let the
__iter__()
returnthis
and write the__next__()
method inside the same class, instead of making two classes.
- Usually, we let the
- Analogy: An iterable is like a book (one can flip through the pages) and an iterator for a book would be a bookmark (saves the position and can locate the next page). Calling
iter
on a book gives you a new bookmark independent of other bookmarks, but callingiter
on a bookmark gives you the bookmark itself, without changing its position at all. Callingnext
on the bookmark moves it to the next page, but does not change the pages in the book. Callingnext
on the book wouldnât make sense semantically. We can also have multiple bookmarks, all independent of each other.
# Call iter() on a iterable to get an iterator
iterator = iter(iterable)
try:
while True:
elem = next(iterator)
# do something
except StopIteration:
pass
# This would return itself, with the state unchanged.
iter_copy = iter(iterator)
# This returns a new, fresh iterator
another_iter = iter(iterable)
# Iterator can be used as an iterable in for
list_iter = iter([4, 3, 2, 1])
for e in list_iter:
print(e)
# but you cannot do for a second time on this
# This is because iterator is exhausted
list(another_iter)
# This would also exhaust an iterator
# Generally, any attempt to "list" the iterator exhaust it
# Calling for loop on an exhausted iterator won't throw an error; it just does nothing
# iterator of an iterable always tracks the latest update of the iterable
li = [3,4]
it = iter(li)
print(next(it)) # 3
print(next(it)) # 4
li.append(5)
print(next(it)) # 5
print(next(it)) # StopIteration
# For dictionary however, adding/removing elements makes all its iterator invalid
# You don't have this problem with list
dict = {"one":1, "two":2, "three":3}
it = iter(dict) # same as iter(dict.keys); for values/kvp, use `values`/`items`
print(next(it))
dict["four"] = 4 # RuntimeError: dictionary changed size during iteration
print(next(it))
# use hasattr to check if some object has some particular function
print(hasattr(my_iter, "__iter__")) # True
print(hasattr(list, '__iter__')) # True
print(hasattr(tuple, '__iter__')) # True
# Make a class both an iterable and an iterator
class SquareIterator: # Both an iterable and an iterator
"""SquareIterator takes items and returns item's square upon call"""
def __init__(self, *args):
self.args = args
self.iter_len = len(args)-1
def __iter__(self):
"""This method is used to initialize a iterator, it returns an iterator object."""
self.idx = -1 # we initialize index
return self
def __next__(self):
"""This method is used to fetch next value, it can be called or loops do call it automatically."""
self.idx += 1
if self.idx > self.iter_len:
raise StopIteration
return self.args[self.idx]**2
my_iter = SquareIterator(10,20,30,40,50)
my_iter = iter(my_iter) # initialize iterator
print(next(my_iter)) # 100
print(my_iter.__next__()) # 400
for v in my_iter: print(v) # for loop call __iter__ and __next__ functions on a iterable
- Generator: A method has at least one
yield
statement and returns a generator object when we call it, without evaluating the body of the generator function itself. When we first callnext
on the returned generator, then we will begin evaluating the body of the generator function until an element is yielded or the function otherwise stops (such as if wereturn
). The generator remembers where we stopped, and will continue evaluating from that stopping point on thenext
time we call next.
def count_three():# generator without loop
yield 1
yield 2
yield 3
for a in count_three():
print(a)
def countdown(n):
print("Beginning countdown!")
while n >= 0:
yield n
n -= 1
print("Blastoff!")
c1, c2 = countdown(3), countdown(3) # Does not print anything
print(c1 is iter(c1)) # True, a generator object is an iterator
print(c1 is c2) # Not the same iterator
next(c1) # Beginning countdown! 2
def my_generator(*args): # generator with a loop
for a in args:
yield a
generator = my_generator(10,20,30,40,50)
generator = (a for a in [10,20,30,40,50]) # or using comprehension
print(type(generator)) # <class 'generator'>
print(next(generator)) # 10
for a in generator:
print(a) # loop over all values
def gen_list(lst): # You can do yield from an iterator or iterable.
yield from lst
def gen_list2(lst):
yield from iter(lst)
# mix return and yield
def f(x):
yield x
yield 1
return x + 1 # not yielded using next(); returned value lost
yield 2 # never reached
def h(x):
y = yield from f(x)
yield y # in this way you can get the returned value
# Recursive generator
def primes_gen(n):
"""Generates primes in decreasing order.
>>> pg = primes_gen(7)
>>> list(pg)
[7, 5, 3, 2]
"""
if n < 2:
return
if is_prime(n):
yield n
yield from primes_gen(n-1)
# Do not just do yield primes_gen(n-1)
# Some examples:
def prefixed(s):
"""Generates prefixes of a word.
>>> list(prefixes('both'))
['b','bo','bot','both]
"""
if s:
yield from prefixes(s[:-1])
yield s
def substrings(s):
if s:
yield from prefixes(s)
yield from substrings(s[1:])
Code Structure
import
- If you
import math
you can usemath.sqrt()
. - You can also do
from math import *
or evenfrom math import *
to call this method by justsqrt()
(A bit like the static import in Java and C# - not a good practice). - If a built-in module like
sys
(they are compiled) is imported, Python knows where to get it; if not, Python searches from thesys.path
directory, and once found will run those imported codes at run time, before running codes in this file.- sys.path is initialized from these locations: The directory containing the input script (or the current directory when no file is specified).PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH). The installation-dependent default.
.pyc
files (Byte-Compiled files) are created by the Python interpreter when a .py file is imported. They contain the âcompiled bytecodeâ of the imported module/program so that the âtranslationâ from source code to bytecode (which only needs to be done once) can be skipped on subsequent imports if the .pyc is newer than the corresponding .py file, thus speeding startup a little. But itâs still interpreted. Once the _.pyc file is generated, there is no need of _.py file, unless you edit it.- Easter Egg:
import this
,import antigravity
,from __future__ import braces
.
- If you
- A module is just a Python source file. It has these attributes:
__doc__
: discussed above. The functionhelp()
has the same effect.__file__
: holds the name and path of the module file from which it is loaded.__name__
: ttribute: By default, is the file name (if imported by another module) or__main__
(if invoked directly), unless this string is changed in file by code (e.g.,__name__
=my own name
). Hence, we use the following codes to check whether a module is imported:
if __name__ == "__main__":
print ("Executed when invoked directly")
else:
print ("Executed when imported")
__dict__
: return a dictionary object of module attributes, functions and other definitions and their respective values. The functiondir()
has the same effect.A package is a collection of Python modules: while a module is a single Python file, a package is a directory of Python modules containing an additional
__init__.py
file at the root folder (The__init__.py
distinguishes a package from a directory that just happens to contain a bunch of Python scripts.).- A package can contain other package folder (sub-packages), too (in those folder there will be separate
__init__.py
files). Packages can be nested to any depth. - The distinction between module and package seems to hold just at the file system level. When you import a module or a package, the corresponding object created by Python is always of type module.
- When you import a package, only variables/functions/classes in the
__init__.py
file of that package are directly visible, not sub-packages or modules. To access variables/functions/classes in its sub-package/contained modules, call it behind the package name, e.g.,math.sqrt()
afterimport math
; Directly callingsqrt()
will not work.- You could import and give an alias to a package at the same time using
import cPickle as p
.
- You could import and give an alias to a package at the same time using
- A package can contain other package folder (sub-packages), too (in those folder there will be separate
Sequences
Sequences in general
- Common Operations
## All sequences support indexing operations
myList = [1, 2, 4]
myTuple = ('tom','jerry')
phonebook = {
'john': '88888888' # keys can only be immutable types (numbers, strings, tuples)
'mike': '99999999'
}
print(myList[0])
print(phonebook['mike'])
# This is a syntax suger of operator.getitem
from operator import getitem
print(getitem(myList, 0))
## All sequences support slicing operations
print(myList[0:-1])
print(myList[::-1]) # get a reversed string or list like this
print(myList[99:999]) # you can do things out of bound; it will not show errors but simply give you [] or ""
# Slicing does create a new iterable, but it is only a shallow copy. It simply bundle reference to each element
# together and put them in a new iterable.
b = a[:]
print(a is b) # False
print[a[0] is b[0]) # True
## All sequences are iterable
for number in myList:
print(number)
## All sequences support membership test
print(3 in [1, 2, 3]) # True
if 'mike' in phonebook:
print(phonebook['mike'])
# in will only search for the first layer element
# [1,2] in [1,2,3] -- False
# 1 in [[1,2],3] -- False
## Sequences may support len
print(len(myList)) # prints 3
## Sequences can be converted to one another
my_list = list((1,2,3,4,5)) # tuple to list
my_list = list({1,2,3,4,5}) # set to list
my_list = list(rang(1,6)) # range to list
my_tuple = tuple([1,2,3,4,5]) # list to tuple
my_tuple = tuple({1,2,3,4,5}) # set to tuple
myDict = dict.fromkeys(myList) #myDict is {1:None, 2:None, 4:None}
myDict = dict.fromkeys(myTuple,10) # myDict is {'tom':10, 'jerry':10}
myDict = dict([(3, 9), (4, 16), (5, 25)]) #myDict is {3: 9, 4: 16, 5: 25}
my_set = set(my_list) # this unpacks items from list to set
# also if my_list contained a list inside it, TypeError: unhashable type: 'list' is raised
my_set = set((1,2,3,4,5)) # tuple to set
keys = [1,2]
values = [2,3]
my_dict = dict([keys, values]) # list to dict
my_dict = dict(((1,2), (2,3))) # tuple to dict
## Sequence objects typically may be compared using lexicographical ordering
print([1,2,3] == [1,2,3]) # True
print([1,2,3,4] < [1,2,4]) # True
print((1, 2, 3) == (1.0, 2.0, 3.0)) # True
print('ABC' < 'C' < 'Pascal' < 'Python') # True
print((1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)) # True
## List, tuples, strings support count()
my_list.count(2) # returns number of appearance of 2
## You can print sequences nicely without a foreach
Related built-in functions:
nums = [-1, 0, 1, 2, 3, 4, 5]
# range(start, stop[, step])
# Returns a sequence of consecutive integers separated by step.
# Follows a similar syntax to that of slicing
range(4) # 0, 1, 2, 3
range(1,5) # 1, 2, 3, 4
range(1,5,2) # 1, 3
# range is immutable
# sum(iterable,/,start=0)
# start from start, applies "+" to each element in the iterable and the previous result, returns the final result
# sum takes at most 2 arguments!
sum(nums)
# returns 14
sum(nums, [3, 4]) #[3, 4] is the start parameter
# returns [3, 4, -1, 0, 1, 2, 3, 4, 5]
sum(["hi","I","ih"])
# TypeError: unsupported operand type(s) for + : 'int' and 'str'
sum(["hi","I","ih"],[])
# TypeError: can only concatenate list (not "str") to list
sum([["hi","I","ih"]],[])
# ['hi', 'I', 'ih']
sum([['2','3'],['5'],['9']],[])
# ['2','3','5','9']
sum([[1,["3"],(3,4),2,3],["!",[4]],[]],[])
# [1, ['3'], (3, 4), 2, 3, '!', [4]]
sum([(1,),(2,),[3,]],())
# TypeError: can only concatenate list (not "list") to list
# max(), min()
# max(iterable, *[, key, default])
# max(arg1, arg2, *args[, key])
# key and default are keyword-only argument; The key argument specifies a one-argument ordering function like that used for list.sort(), and The default argument specifies an object to return if the provided iterable is empty. If the iterable is empty and default is not provided, a ValueError is raised.
# If multiple items are maximal, the function returns the first one encountered.
max(range(5)) # If one positional argument is provided, it should be an iterable.
max(0, 2, 4) # cannot mix up iterables and values
max(range(10), key = lambda x: 7 - (x - 4) * (x - 2)) # Get max point on a parabola, returns 3
max(range(0), default = "!") # return "!"
max(range(0)) # ValueError: max() arg is an empty sequence
# Strings are compared lexicographically
max("Hi","There","a")
# 'a'
# any(), all()
# any() returns True if any element of the iterable is true. If the iterable is empty, returns False.
# all() returns True if all elements of the iterable are true (or if the iterable is empty).
any(True, 0, '') # True
all(range(5)) # False (there is a zero)
all([x < 5 for x in range(5)>]) # True, because all elements in the list are true
# sorted(iterable)
# Creates a sorted list containing all the elements in the input iterable
# It is the same as calling li = list(iterable) and then sort it by li.sort()
Lazy evaluation functions: acts like an iterator
# enumerate(iterable, start=0)
# Takes in an iterable and returns an iterator that yield a tuple each time. Each tuple consists of an index and its actual content.
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
it = enumerate(seasons)
next(it) # (0, Spring)
list(enumerate(seasons, start=1))
# [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
# zip(iterable*)
# Returns a zip object, which is an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
# Any extra element from any iterable would simply be ignored
a = ("John", "Charles", "Mike")
b = ("Jenny", "Christy", "Monica", "Vicky")
for each in zip(a, b):
print(each)
# Result:
# ('John', 'Jenny')
# ('Charles', 'Christy')
# ('Mike', 'Monica')
# filter(f, iterable)
# Creates iterator over x for each x in iterable if f(x).
# Take note of the double lazy evaluation
def double(x):
print("{} is doubled".format(x))
return x + x
t = filter(lambda x: x > 10, map(double, range(0,10)))
next(t)
# You will see:
# 0 is doubled
# 1 is doubled
# 2 is doubled
# 3 is doubled
# 4 is doubled
# 5 is doubled
# 6 is doubled
# Because it keeps evaluating until it hits the first element that doubles to be more than 10
# map(f, iterable*)
# Takes in a function and a (or many) iterable(s), apply the function to every element and return an iterator
# map() returns an iterator (a map object), and you need list() to convert that to a list
this_is_iter = map(lambda x: x + 10, range(5))
next(this_is_iter) # 10
# map() is written in C and is highly optimized, its internal implied loop can be more efficient than a regular Python for loop
# In some cases, computing a list of the values in this iterable will give us the same result as [func(x) for x in iterable]. However, it's important to keep in mind that iterators can potentially have infinite values because they are evaluated lazily, while lists cannot have infinite elements.
nums_abs = list(map(int,["1","2","3"])) # let int() parses every element to an int
the_powers = [4, 5, 6, 7]
pow_list = list(map(pow, nums_abs, the_powers)) # [1, 32, 729]
# reversed(iterable)
# Creates iterator over all the elements in the input iterable in reverse order.
# Take not its lazy evaluation!
mynum = [1,2,3,2,1]
rev = reversed(mynum)
print(rev == mynum) # False! An iterator != a list
# import reduce from functools
# reduce(f, iterable)
# Apply function of two arguments f cumulatively to the items of iterable, from left to right, so as to reduce the sequence to a single value.
List
- Lists are mutable. List operations, such as
remove
,pop
,sort
, does not return a sorted list; instead, change the original list.
shopping = ['apple','rice','washboard','rice','triangle','house',3,4,5] # elements can have different types
shopping.append('milk') # append; return None[1]
shopping.insert(3,'porridge')
shopping.extend(['egg','bread','potato']) # append the whole list
copy = shopping.copy() # or use Slicing to copy a list, see below
copy2 = shopping[:] # or [::]; they are the same
del copy2[2:4] # delete by continuous index
print(shopping.count('rice')) # (this prints 2) get number of appearances of a certain value in list
del shopping[0], shopping[2] # remove by index (it removes one by one! The second item to remove may have its positive moved)
shopping.remove('rice') # remove by value; will remove the first occurence only; returns None
# Use filter or list comprehension to remove all occurence
del copy, copy2
print(shopping.pop()) # pop the last element - stack behaviour
print(shopping.popleft()) # pop the last element - queue behaviour
shopping.pop(1) # pop by index, would return the removed element
shopping.sort()
shopping.reverse() # this changes the original list
revCopy = shopping[::-1] # this returns a reversed list (works for strings too)
myList = [1, 2, 3, 4]
print(max(myList)) # prints 4 (note that all elements must be of the same type for comparison)
addList = [1, 2, 3] + [4, 5, 6]
addList += [7]
mulList = ['Hi'] * 4 # mulList is ['Hi','Hi','Hi','Hi']
# The same effect can bes achieved by add(), mul() method
# But there is no subtraction or division defined for list
# More dimension
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
for a, b, c, d in matrix:# unpacking
print(a, b, c, d)
# List comprehension
# Take a existing sequence and build a new list from it.
# LIST = [function(ITEM) for ITEM in ITERABLE_OBJ if condition(ITEM)]
# This picks all ITEM that satisfies condition from ITERABLE_OBJ, and apply a function to them, and put them into LIST
list1 = [0 for _ in range(10)] # ten zeros in the list
list2 = [2*i for i in (2,3,4) if i > 2]
list3 = [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
list4 = [str(round(pi, i)) for i in range(1, 6)] # ['3.1', '3.14', '3.142', '3.1416', '3.14159']
# Take note the reference
child = [1,2]
parent = [child, child[:], list(child) 3]
child.append(3)
print(parent) # [[1, 2, 3], [1, 2], [1, 2], 3]
# This is because:
print(child is child[:]) # False
print(child is list(child)) # False
# List seems to be passed by reference!
# In env diag they point to the same object, too.
def pops(orig):
orig.pop()
list = [1,2,3,4]
pops(list)
print(list) # [1,2,3]
- Very elegant explanation on nested list comprehension.
Set
Set represents a mathematical set (with no repeated element). Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.
basket = {'orange', 'apple', 'banana'}
empty = set() # cannot do {}; this is a dict
a = set('abracadabra')
b = {'a','l','a','c','m','a','z'}
c = set(['orange', 'apple', 'banana'])
d = {x for x in 'abracadabra' if x not in 'abc'}
print(b - a) # in b but not in a: {'l', 'm', 'z'}
print(b | a) # union: {'d', 'z', 'l', 'b', 'c', 'm', 'a', 'r'}
print(b & a) # intersect: {'a', 'c'}
print(a ^ b) # xor:{'d', 'm', 'z', 'l', 'b', 'r'}
## Some methods of sets
my_set1 = {3,5,7,1,8}
my_set2 = {1,2,3,4,5}
# find intersection, or similar to 'my_set1 & my_set2'
print(my_set1.intersection(my_set2)) # {1, 3, 5}
# to find union, or similar to 'my_set1 | my_set2'
print(my_set1.union(my_set2)) # {1, 2, 3, 4, 5, 7, 8}
my_copy = my_set1.copy() # returns a copy of a set
my_copy.clear() # removes all members of set
# checks if my_set2 is a subset of my_set1
print(my_set1.issubset(my_set2)) # False
# checks if my_set2 is a superset of my_set1
print(my_set1.issuperset(my_set2)) # False
Tuple
Tuples are immutable. Cannot use append
or del
.
zoo = ('python','elephant','zebra') # Brackets are optional - but please use them
parent_zoo = ('monkey','tiger',zoo) # Element does not have to be the same type
singleton = (2,) # you need this comma
empty_tuple = ()
print(zoo[0], parent_zoo[2][0]) # indexer access
del zoo # you cannot delete an element, but you can delete the whole tuple
print(max(zoo)) # prints zebra (note that all elements must be of the same type for comparison)
mulTuple = ('Hi!',) * 4 # mulTuple is ('Hi','Hi','Hi','Hi')
Dictionary
The class is dict
. The object that uses {}
to initialize is a dict
object.
The order of items in dictionary is the order in which they were added (Python 3.6+). Historically items appeared in an arbitrary order.
phonebook = {
'john': '88888888', # keys can only be immutable types (numbers, strings, tuples); it must be hashable; if a tuple contains a list it becommes unhashable, too. This is because an immutable sequence might still change if it contains a mutable value.
'mike': '99999999',
333 : '11111111', # keys can values can have different types
'mike': 100 # if keys repeated, the one that comes later is used
}
my_dict = {i: i * i for i in range(3,6)} # faster setup for {3: 9, 4: 16, 5: 25}
print(phonebook['mike']) # prints 100
phonebook['fido'] = '00000000'
copy = phonebook.copy() # shallow copy
del phonebook['john']
copy.clear() # clean the dict
del copy # delete the dict
phonebook2 = {
'nancy': '44444444',
'john': '22222222'
}
phonebook.update(phonebook2) # include the second dict into the first, override any repeated key
print(phonebook.pop('john')) # prints 22222222; 'john' is then deleted
phonebook.pop('mary','Not Found') # prints 'Not Found'
phonebook.popitem() # returns a tuple (containing the last key-value pair)
phonebook.keys() # returns a fake list containing all keys)
list(phonebook) # returns a list of keys; same as calling keys()
iter(phonebook)
iter(phonebook.keys) # these two lines are the same; both return an iterator of dictionary keys
iter(phonebook.values)
iter(phonebook.items) # Kvp
phonebook.values() # returns a fake list of values
for name, number in phonebook:
print('call {} at {}'.format(name,number))
if 'mike' in phonebook:
print(phonebook['mike'])
print(phonebook.get('tom','No such person')) # try get, if no such key then return the value specified as the second argument (None by default)
print(phonebook.setdefault('jerry','00000000')) # try get, if no such key then create a key with the value specified as the second argument (None by default)
IO
print()
- It appends a
\n
by default. To print without changing line, useprint(...,end='')
.
- It appends a
pprint()
input()
- The type of the value stored is always string.
raw_input()
is only used in Python 2.x.
val1 = input("Enter the name: ")
print(type(val1)) # <class 'str'>
print(val1)
val2 = input("Enter the number: ")
print(type(val2)) # <class 'str'>
val2 = int(val2)
print(type(val2))
print(val2)
file
Class- There are many other modes like
a
For appending. Usehelp(file)
to learn more.
- There are many other modes like
poem = '''
Programming is fun
When the work is done
if you wanna make your work also fun:
use Python!
'''
f = file('poem.txt', 'w') # open for 'w'riting
f.write(poem) # write text to file
f.close() # close the file
f = file('poem.txt')
# if no mode is specified, 'r'ead mode is assumed by default
while True:
line = f.readline()
if len(line) == 0: # Zero length indicates EOF
break
print(line, end="") # Print a line includes printing the linefeed at the end of the line; hence, avoid adding a line break automatically
f.close() # close the file
# with keyword in Python is like using keyword in C#
with open("poem.txt") as f:
for line in f:
print(line, end="")
cPickle
ClasscPickle
is done by C and thus is 100 times faster thanpickle
and they have the same functionalities.- They can serialize any Python objects.
import cPickle as p
#import pickle as p (this is slower)
shoplistfile = 'shoplist.data'
# the name of the file where we will store the object
shoplist = ['apple', 'mango', 'carrot']
# Write to the file
f = file(shoplistfile, 'w')
p.dump(shoplist, f) # dump the object to a file
f.close()
del shoplist # remove the shoplist
# Read back from the storage
f = file(shoplistfile)
storedlist = p.load(f)
print storedlist
BytesIO
andStringIO
import io
Error, Testing
Exception
Like in many OOP languages, exceptions are objects. They have classes with contructors and you can define customized exceptions.
They enable non-lcoal contintuations of control: If f calls g and g calls h, exceptions can shift control from h directly back to f without waiting for g to return.
However, exception handling tends to be slow.
# Custom Exception
class MyOwnError(Exception):
"""My own error."""
pass
# throw an error
raise MyOwnError('HiThere')
# try, catch, finally block
try:
s = int(input('Enter a number'))
except (RuntimeError, TypeError, NameError):
pass
except ValueError:
sys.exit() # exit the program
except myOwnError as err:
print(err)
except:
print('\nSome error/exception occurred.')
# here, we are not exiting the program
finally: # the finally block always runs
print("Do clean-ups here!")
Common types of errors:
- TypeError: wrong number/type of argument(s) passed into a function
- NameError: a name wasnât found
- KeyError: a key wasnât found in dict
- RuntimeError: catch-all for troubles during interpretation
Debugging tips:
- IndentationError/TabError: Check indentation inconsistency/misalignment; Check mixing tabs and spaces.
- UnboundLocalError: This means a variable that is local to a frame used before assigned. Check whether you are using a using a variable from a parent frame.
Assertion
Assertion in Python are designed pretty much the same as in other OOP languages. They usually serves as sanity checks during development and helps to rule out very basic errors in earlier stages.
# assert (if assertion fails, an AssertionError is thrown)
mylist = ['item']
assert len(mylist) >= 1
# You can customize error message to be shown, too.
# assert <expression>, <string>
mylist.pop()
assert len(mylist) >= 2, 'Length of myList should >= 2!'
# AssertionError: Length of myList should >= 2!
If Python is run with the -O
flag (O for optimize) then all assertion checks are ignored. They can also be gloablly turned on or off by toggling the bool __debug__
, and running python with -O
will toggle the __debug__
to be False
.
Run Doctest
To test a specific function:
from doctest import run_docstring_examples
run_docstring_examples(func_to_test, globals(), True)
Or
from doctest import testmod
testmod(name ='func_to_test', verbose = True)
When writing Python in files, all doctests in a file can be run by starting Python with the doctest command line option: python3 -m doctest _mytest.txt
.
- Yes. The file can just be a
.txt
that has all doctest with no python codes. Of course it can also be a.py
file. - You can also add a
-v
for verbose.
Testing in Practice
When writing Python in files, rather than directly into the interpreter, tests are typically written in the same file or a neighboring file with the suffix _test.py.
Other Built-in Modules
operator
- This module has operators for you to import so you can perform operations like a function call.
from operator import add, sub, mul, truediv, floordiv, mod
sub(pow(3, truediv(365, 52)), 1) # same as (3 ** (365/52)) - 1
mod(floordiv(25, 4), truediv(25, 4)) # Q9: (25 // 4) % (25 / 4)
sys
- This module provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter. You have to
import sys
to use the following: sys.exit()
: Exit from Python.- Can pass in an optional argument as the exit status (0 by default, considered âsuccessful terminationâ).
- Most systems require it to be in the range 0â127, and produce undefined results otherwise. Some systems have a convention for assigning specific meanings to specific exit codes, but these are generally underdeveloped; Unix programs generally use 2 for command line syntax errors and 1 for all other kind of errors.
- This raises the SystemExit exception - it can be caught and intercepted at an outer level. It will only exit the process when called from the main thread, and the exception is not intercepted.
- Changed in version 3.6: If an error occurs in the cleanup after the Python interpreter has caught SystemExit (such as an error flushing buffered data in the standard streams), the exit status is changed to 120.
- Can pass in an optional argument as the exit status (0 by default, considered âsuccessful terminationâ).
sys.argv
: The list of command line arguments passed to a Python script.argv[0]
is the script name (it is operating system dependent whether this is a full pathname or not).- If the command was executed using the
-c
command line option to the interpreter,argv[0]
is set to the string'-c'
. - If no script name was passed to the Python interpreter,
argv[0]
is the empty string.
sys.stdxxx
: Read this and this.
os
and os.path
- This module represents generic operating system functionality. This module is especially important if you want to make your programs platform-independent (Linux and Windows).
os.name
string specifies which platform you are using, such as'nt'
for Windows and'posix'
for Linux/Unix users.os.getcwd()
: gets the current working directory i.e. the path of the directory from which the curent Python script is working.os.getenv()
andos.putenv()
: get and set environment variables respectively.os.listdir()
: returns the name of all files and directories in the specified directory.os.remove()
: delete a file.os.system()
: run a shell command.os.linesep
string gives the line terminator used in the current platform (e.g., Windows uses'\r\n'
, Linux uses'\n'
and Mac uses'\r'
.)os.sep
: The character used by the operating system to separate pathname components (e.g.'/'
for POSIX and'\\'
for Windows). This is not sufficient to parse or concatenate pathnames; use the following two:os.path.split()
: returns the directory name and file name of the path.os.path.join()
: joins the folder names with the correct separator in the current platform.os.path.exists()
: checks whether a path exists.os.path.isfile()
andos.path.isdir()
: check whether the path is a file or a directory.
Reference
- Post link: https://reimirno.github.io/2021/08/12/Python-Collections/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
GitHub Discussions