Python Cert Notes
Python Cert Notes
print(12 / -4.5)
print(12 // -4.5)
print(12 % -4.5)
print (12-(4.5*(-3.0)))
-2.6666666666666665
-3.0
-1.5
-1.5
print(9 % 6 % 2)=1 as This operator has left-sided binding
** the exponentiation operator uses right-sided binding.
Priority Operator
1 +, - unary
2 **
3 *, /, //, %
4 +, - binary
5 <, <=, >, >=
6 ==, !=
If you want to give a name to a variable, you must follow some strict rules:
Take a look at the list of words that play a very special role in every Python
program.
They are called keywords or (more precisely) reserved keywords. They are reserved
because you mustn't use them as names: neither for your variables, nor functions,
nor any other named entities you want to create.
The meaning of the reserved word is predefined, and mustn't be changed in any way.
Fortunately, due to the fact that Python is case-sensitive, you can modify any of
these words by changing the case of any letter, thus creating a new word, which is
not reserved anymore.
import
You mustn't have a variable named in such a way - it is prohibited. But you can do
this instead:
Import
Note: the range() function accepts only integers as its arguments, and generates
sequences of integers.
The loop's else branch is always executed once, regardless of whether the loop has
entered its body or not.
bitwise operators.
Here are all of them:
Priority Operator
1 ~, +, - unary
2 **
3 *, /, //, %
4 +, - binary
5 <<, >>
6 <, <=, >, >=
7 ==, !=
8 &
9 |
10 =, +=, -=, *=, /=, %=, &=, ^=, |=, >>=, <<=
& does a bitwise and, e.g., x & y = 0, which is 0000 0000 in binary,
| does a bitwise or, e.g., x | y = 31, which is 0001 1111 in binary,
˜ does a bitwise not, e.g., ˜ x = 240*, which is 1111 0000 in binary,
^ does a bitwise xor, e.g., x ^ y = 31, which is 0001 1111 in binary,
>> does a bitwise right shift, e.g., y >> 1 = 8, which is 0000 1000 in binary,
<< does a bitwise left shift, e.g., y << 3 = , which is 1000 0000 in binary,
The new_list list will have end - start (3 - 1 = 2) elements - the ones with
indices equal to 1 and 2 (but not 3).
my_list = [10, 8, 6, 4, 2]
del my_list
print(my_list)
The del instruction will delete the list itself, not its content.
Three-dimensional arrays
rooms = [[[False for r in range(20)] for f in range(15)] for t in range(3)]
In Python, the else block after a for or while loop runs only if the loop completes
without encountering a break.
Think of it like:
“Did the loop finish cleanly? If yes → run the else block.”
f[start:stop]
It returns a new list that includes elements from index start up to but not
including index stop.
k=[[1,2],[3,4]]
print(k[0][0])
1
print(k[0][1])
2
print(k[1][1])
4
print(k[1][0])
3
list.insert(index, element)
s=[1,2,3]
for v in range(len(s)):
s.insert(1,s[v])
print(s)
positional argument passing in which the order of arguments passed matters (Ex. 1),
keyword (named) argument passing in which the order of arguments passed doesn't
matter
def my_function():
#global var
var = 2
print("Do I know that variable?", var)
global var
var = 1
my_function()
print(var)
This also means that a function receives the argument's value, not the argument
itself. This is true for scalars.
Mutable data can be freely updated at any time. Immutable data cannot be modified
in this way.
A tuple is an immutable sequence type. It can behave like a list, but it mustn't be
modified. Each tuple element may be of a different type (floating-point, integer,
or any other not-as-yet-introduced kind of data).
dictionaries are not lists - they don't preserve the order of their data, as the
order is completely meaningless (unlike in real, paper dictionaries). The order in
which a dictionary stores its data is completely out of your control, and your
expectations. That's normal. (*)(*) In Python 3.6x dictionaries have become ordered
collections by default. Your results may vary depending on what Python version
you're using.
And now the most important news: you mustn't use a non-existent key.
1. Tuples are ordered and unchangeable (immutable) collections of data. They can be
thought of as immutable lists. They are written in round brackets:
The default except branch must be the last except branch. Always!
d={'a':'a11','b':'b11','c':'c11'}
v=d['a']
print(v)
#for i in range(len(d)):
# v=d[v]
#print(v)
ls=['a','b','c','d','e']
def l(l1):
del l1[3]
l1[3]='e'
print(l(ls))
dd={'1':'0','0':'1','2':'3'}
v=dd['2']
v=dd['4']
print(v)
print(len(dd))
print(sin(pi/2))
def sin(x):
if 2 * x == pi:
return 0.99999999
else:
return None
print(sin(pi/2))
Note: after successful execution of an aliased import, the original module name
becomes inaccessible and must not be used.
dir(). The function returns an alphabetically sorted list containing all entities'
names available in the module identified by a name passed to the function as an
argument:
During the very first import of the actual module, Python translates its source
code into the semi-compiled format stored inside the pyc files, and deploys these
files into the __pycache__ directory located in the module's home directory.
The repository (or repo for short) we mentioned before is named PyPI (it's short
for Python Package Index) and it's maintained by a workgroup named the Packaging
Working Group, a part of the Python Software Foundation, whose main task is to
support Python developers in efficient code dissemination.
“all Python roads lead to PyPl”. PyPI is completely free, and you can just pick a
code and use it.
Is every user doomed to visit hell in order to run the code for the first time?
Fortunately not - pip can do all of this for you. Really. It can discover,
identify, and resolve all dependencies. Moreover, it can do it in the cleverest
way, avoiding any unnecessary downloads and reinstalls.
8
-1
4
1. Some of the methods offered by strings are:
2. String content can be determined using the following methods (all of them return
Boolean values):
It shows a small section of the complete exception tree. Let's begin examining the
tree from the ZeroDivisionError leaf.
BaseException
↑
Exception
↑
ArithmeticError
↑
ZeroDivisionError
https://docs.python.org/3.6/library/exceptions.html
-------------------------------------------------------------------------------
------------------------------------OOP----------------------------------------
-------------------------------------------------------------------------------
if you set a property's value for the very first time (like in the constructor),
you are creating it; from that moment on, the object has got the property and is
ready to use its value;
When any class component has a name starting with two underscores (__), it becomes
private - this means that it can be accessed only from within the class.
You cannot see it from the outside world. This is how Python implements the
encapsulation concept.
Python objects, when created, are gifted with a small set of predefined properties
and methods. Each object has got them, whether you want them or not. One of them is
a variable named __dict__ (it's a dictionary).
class ExampleClass:
varia = 1
def __init__(self, val):
ExampleClass.varia = val
varia = val
self.varia = val
The self parameter is used to obtain access to the object's instance and class
variables.
The self parameter is also used to invoke other object/class methods from inside
the class.
Note: the __name__ attribute is absent from the object - it exists only inside
classes.
__bases__ is a tuple. The tuple contains classes (not class names) which are direct
superclasses for the class.
Key takeaways
1. A method is a function embedded inside a class. The first (or only) parameter of
each method is usually named self, which is designed to identify the object for
which the method is invoked in order to access the object's properties or invoke
its methods.
3. All classes (but not objects) contain a property named __name__, which stores
the name of the class. Additionally, a property named __module__ stores the name of
the module in which the class has been declared, while the property named __bases__
is a tuple containing a class's superclasses.
super() function, which accesses the superclass without needing to know its name:
class Level1:
variable_1 = 100
def __init__(self):
self.var_1 = 101
def fun_1(self):
return 102
class Level2(Level1):
variable_2 = 200
def __init__(self):
super().__init__()
self.var_2 = 201
def fun_2(self):
return 202
class Level3(Level2):
variable_3 = 300
def __init__(self):
super().__init__()
self.var_3 = 301
def fun_3(self):
return 302
obj = Level3()
Note: the situation in which the subclass is able to modify its superclass behavior
(just like in the example) is called polymorphism.
===================================================================================
=============================================================
==OOP==============================================================================
=============================================================
===================================================================================
=============================================================
Key takeaways
class Mouse:
def __init__(self, name):
self.my_name = name
def __str__(self):
return self.my_name
the_mouse = Mouse('mickey')
print(the_mouse) # Prints "mickey".
class Mouse:
pass
class LabMouse(Mouse):
pass
class Mouse:
pass
class LabMouse(Mouse):
pass
mickey = Mouse()
print(isinstance(mickey, Mouse), isinstance(mickey, LabMouse)) # Prints "True
False".
4. A operator called is checks if two variables refer to the same object. For
example:
class Mouse:
pass
mickey = Mouse()
minnie = Mouse()
cloned_mickey = mickey
print(mickey is minnie, mickey is cloned_mickey) # Prints "False True".
5. A parameterless function named super() returns a reference to the nearest
superclass of the class. For example:
class Mouse:
def __str__(self):
return "Mouse"
class LabMouse(Mouse):
def __str__(self):
return "Laboratory " + super().__str__()
doctor_mouse = LabMouse();
print(doctor_mouse) # Prints "Laboratory Mouse".
class Mouse:
Population = 0
def __init__(self, name):
Mouse.Population += 1
self.name = name
def __str__(self):
return "Hi, my name is " + self.name
class LabMouse(Mouse):
pass
class Mouse:
def __init__(self, name):
self.name = name
def __str__(self):
return "My name is " + self.name
class AncientMouse(Mouse):
def __str__(self):
return "Meum nomen est " + self.name
A variable existing outside a function has a scope inside the functions' bodies.
If the argument is a list, then changing the value of the corresponding parameter
doesn't affect the list (remember: variables containing lists are stored in a
different way than scalars),
but if you change a list identified by the parameter (note: the list, not the
parameter!), the list will reflect the change.
+The range() function is, in fact, a generator, which is (in fact, again) an
iterator++
__iter__() which should return the object itself and which is invoked once (it's
needed for Python to successfully start the iteration)
__next__() which is intended to return the next value (first, second, and so on) of
the desired series - it will be invoked by the for/in statements in order to pass
through the next iteration; if there are no more values to provide, the method
should raise the StopIteration exception.
Does it sound strange? Not at all. Look at the example in the editor.
We've built a class able to iterate through the first n values (where n is a
constructor parameter) of the Fibonacci numbers.
Let us remind you - the Fibonacci numbers (Fibi) are defined as follows:
Fib1 = 1
Fib2 = 1
Fibi = Fibi-1 + Fibi-2
In other words:
lines 2 through 6: the class constructor prints a message (we'll use this to trace
the class's behavior), prepares some variables (__n to store the series limit, __i
to track the current Fibonacci number to provide, and __p1 along with __p2 to save
the two previous numbers);
lines 8 through 10: the __iter__ method is obliged to return the iterator object
itself; its purpose may be a bit ambiguous here, but there's no mystery; try to
imagine an object which is not an iterator (e.g., it's a collection of some
entities), but one of its components is an iterator able to scan the collection;
the __iter__ method should extract the iterator and entrust it with the execution
of the iteration protocol; as you can see, the method starts its action by printing
a message;
lines 12 through 21: the __next__ method is responsible for creating the sequence;
it's somewhat wordy, but this should make it more readable; first, it prints a
message, then it updates the number of desired values, and if it reaches the end of
the sequence, the method breaks the iteration by raising the StopIteration
exception; the rest of the code is simple, and it precisely reflects the definition
we showed you earlier;
__init__
__iter__
__next__
1
__next__
1
__next__
2
__next__
3
__next__
5
__next__
8
__next__
13
__next__
21
__next__
34
__next__
55
__next__
output
Look:
The map() function applies the function passed by its first argument to all its
second argument's elements, and returns an iterator delivering all subsequent
function results.
closure is a technique which allows the storing of values in spite of the fact that
the context in which they have been created does not exist anymore.
__iter__() is invoked once when the iterator is created and returns the iterator's
object itself;
__next__() is invoked to provide the next iteration's value and raises the
StopIteration exception when the iteration comes to an end.
2. The yield statement can be used only inside functions. The yield statement
suspends function execution and causes the function to return the yield's argument
as a result. Such a function cannot be invoked in a regular way – its only purpose
is to be used as a generator (i.e. in a context that requires a series of values,
like a for loop.)
outputs True.
outputs 02468.
outputs 3.0.
6. The map(fun, list) function creates a copy of a list argument, and applies the
fun function to all of its elements, returning a generator that provides the new
list content element by element. For example:
8. A closure is a technique which allows the storing of values in spite of the fact
that the context in which they have been created does not exist anymore. For
example:
def tag(tg):
tg2 = tg
tg2 = tg[0] + '/' + tg[1:]
def inner(str):
return tg + str + tg2
return inner
b_tag = tag('<b>')
print(b_tag('Monty Python'))
Opening the file associates it with the stream, which is an abstract representation
of the physical data stored on the media. The way in which the stream is processed
is called open mode. Three open modes exist:
2. Depending on the physical file content, different Python classes can be used to
process files. In general, the BufferedIOBase is able to process any file, while
TextIOBase is a specialized class dedicated to processing text files (i.e. files
containing human-visible texts divided into lines using new-line markers). Thus,
the streams can be divided into binary and text ones.
The invocation creates a stream object and associates it with the file named
file_name, using the specified open_mode and setting the specified text_encoding,
or it raises an exception in the case of an error.
4. Three predefined streams are already open when the program starts:
5. The IOError exception object, created when any file operations fails (including
open operations), contains a property named errno, which contains the completion
code of the failed action. Use this value to diagnose the problem.
Key takeaways
read(number) – reads the number characters/bytes from the file and returns them as
a string; is able to read the whole file at once;
readline() – reads a single line from the text file;
readlines(number) – reads the number lines from the text file; is able to read all
lines at once;
readinto(bytearray) – reads the bytes from the file and fills the bytearray with
them;
2. To write new content into a file, the following stream methods can be used:
3. The open() method returns an iterable object which can be used to iterate
through all the file's lines inside a for loop. For example:
The code copies the file's contents to the console, line by line. Note: the stream
closes itself automatically when it reaches the end of the file.
Module 4:
You are now ready to take the module quiz and attempt the final challenge: Module 4
Test, which will help you gauge what you've learned so far.
===================================================================================
===================================================================================
==
Could you explain Objects __dict__ vs Class's __dict__ in detail with example and
differences?
===================================================================================
===================================================================================
==
Great question! This is one of those Python details that confuses even experienced
developers at first. Let’s break it down carefully.
---
## 1. What is `__dict__`?
---
👉 Example:
```python
class Car:
wheels = 4 # class attribute
print(c1.__dict__)
print(c2.__dict__)
```
**Output:**
```python
{'color': 'red'}
{'color': 'blue'}
```
Notice:
* `wheels` does not appear in the instance `__dict__` because it belongs to the
**class**, not the specific instance.
* Each object only stores what was set via `self` (its unique state).
---
👉 Example (continuing):
```python
print(Car.__dict__)
```
**Output (simplified):**
```python
{
'__module__': '__main__',
'wheels': 4,
'__init__': <function Car.__init__ at 0x...>,
'__dict__': <attribute '__dict__' of 'Car' objects>,
'__weakref__': <attribute '__weakref__' of 'Car' objects>,
'__doc__': None
}
```
Notice:
---
## 4. Key Differences
---
## 5. Demonstrating Interaction
```python
class Person:
species = "Homo sapiens"
p = Person("Alice")
**Output:**
```python
Instance dict: {'name': 'Alice'}
Class dict keys: ['__module__', 'species', '__init__', '__dict__', '__weakref__',
'__doc__']
Homo sapiens
Instance dict after override: {'name': 'Alice', 'species': 'Alien'}
p.species -> Alien
Person.species -> Homo sapiens
```
Explanation:
---
✅ **In short:**
---
===================================================================================
===================================================================================
======
Great question 👍 — **name mangling** in Python often shows up when working with
**double underscores** in class attributes, and it can be confusing at first. Let’s
go step by step.
===================================================================================
===================================================================================
======
---
* **Name mangling** is the process by which Python **changes the name** of a class
attribute that starts with **two leading underscores (`__`)** (but does **not** end
with double underscores).
* Python does this to **prevent accidental access or overriding** of such
attributes in subclasses.
👉 Basically:
`__myvar` inside class `MyClass` becomes `_MyClass__myvar` internally.
---
---
```python
class Test:
def __init__(self):
self.__secret = "hidden"
self.visible = "public"
t = Test()
**Output:**
```
public
hidden
```
Notice:
---
```python
class Parent:
def __init__(self):
self.__data = 42 # becomes _Parent__data
class Child(Parent):
def __init__(self):
super().__init__()
self.__data = 99 # becomes _Child__data
c = Child()
print(c.__dict__)
```
**Output:**
```python
{'_Parent__data': 42, '_Child__data': 99}
```
👉 See? The parent’s `__data` and the child’s `__data` **don’t clash**.
That’s the real purpose of name mangling.
---
## 🔹 5. Important Notes
1. **Only applies** when you use `__var` (double leading underscore) inside a
class.
2. Name mangling is not true privacy — you can still access the variable if you
know the mangled name (`_ClassName__var`).
---
✅ **In short:**
Would you like me to also show you **real-world best practices** (when to use
`_var` vs `__var` vs `__var__`)? That often clears up when name mangling is
actually useful.
Output:
Output:
You cannot use import a.b to import a class, function, or variable—only modules or
packages may be imported this way
import module
import package.submodule
1. Use dir(module)
The built-in dir() function lists all attributes (functions, classes, constants,
variables, submodules, etc.) of a module.
2. Use help(module)
6. Use pydoc
✅ So, in short:
Categorization: inspect
When Python first imports a module, it compiles the module into bytecode and saves
the compiled bytecode in a file with a .pyc extension. The .pyc file contains the
bytecode of the module and is used to speed up subsequent imports of the same
module.
The location where Python deploys the .pyc files depends on the setting of the
__pycache__ directory. The __pycache__ directory is a directory where Python stores
the compiled bytecode files.
For example, if you have a module called my_module.py and you import it for the
first time, Python will compile it into bytecode and save the compiled bytecode in
a file called my_module.cpython-39.pyc. If the __pycache__ directory is located in
the same directory as the source code file, then the .pyc file will be stored in
the same directory. If the __pycache__ directory is located in a subdirectory of
the source code directory, then the .pyc file will be stored in that subdirectory.