Python
Basics
- Semi-compiled to byte-code, executed by interpreter
- Type is associated with objects, not variables - dynamically typed?
- Python runtimes:Cython, Jython, IronPython, PyPy
- PEP 8: Python Enhancement Proposal No. 8
Data types
String
- In Python 2, a sequence of characters can be represented as
str
- raw 8-bit valuesunicode
- unicode characters
- In Python 3, it is
bytes
- raw 8-bit valuesstr
- unicode characters
List
List Comprehension
- Python provides a compact syntax for deriving one list from another called list comprehension
1 2 3 |
|
- Cons
- More than 2 expressions in list comprehension becomes unreadable
- Not scalable.
Generator Expression
For example, say you want to read a file and return the number of characters on each line. Doing this with list comprehension would require holding the length of every line of the file in memory.
To solve this, Python provides generator expressions, a generalization of list comprehensions and generators. Generator expressions don’t materialize the whole output sequence when they’re run. Instead, generator expressions evaluate to an iterator that yields one item at a time from the expression.
Dict
Dict Comprehension
Set
Set Comprehension
Utilities
Slicing
1 2 3 4 5 6 7 8 9 |
|
Striding
Python has special syntax for the stride of a slice in the form somelist[start:end:stride]
. This lets you take every nth item when slicing a sequence.
1 2 3 4 5 |
|
The problem is that the stride syntax often causes unexpected behavior that can introduce bugs. For example, a common Python trick for reversing a byte string is to slice the string with a stride of -1.
Zip
Functions
Generator Functions
- A generator function is defined like a normal function, but whenever it needs to generate a value, it does so with the
yield
keyword rather than return. - If the body of a def contains
yield
, the function automatically becomes a generator function (even if it also contains areturn
statement) - Generator functions create generator iterators or generators - a special type of iterator.
- To get the next value from a generator, we use the same built-in function as for iterators:
next()
which calls the generator’s__next__()
method. Whennext()
is called on a generator, it callsyield
. yield
is justreturn
(plus a little magic explained below) for generator functions.- When a generator function calls
yield
, the “state” of the generator function is frozen; the values of all variables are saved and the next line of code to be executed is recorded untilnext()
is called again. When it is called again, the generator function simply resumes where it left off. Ifnext()
is never called again, the state recorded during theyield
call is (eventually) discarded. - If a generator function calls
return
or reaches the end its definition, aStopIteration
exception is raised. This signals to whoever was callingnext()
that the generator is exhausted (this is normaliterator
behavior).
1 2 3 4 |
|
Simple ways to use it
1 2 3 4 5 |
|
1 2 3 4 5 6 7 |
|
- Generators have the power
- to yield a value,
- receive a value (via
send()
method), or - both yield a value and receive a (possibly different) value in a single statement.
- A statement of the form
other = yield foo
means, “yield
foo and, when a value is sent to me, setother
to that value.” You can “send” values to a generator using the generator’ssend
method. This way you can set a variable inside a generator to a different value passed in by the user.
Modules
itertools
collections
Object-oriented Programming
- Each Python class definition has an implicit superclass:
object
- The rules for the visibility of Python names are as follows:
- Most names are public.
- Names that start with
_
are somewhat less public. Use them for implementation details that are truly subject to change. - Names that begin and end with
__
are internal to Python.
How Python class instantiation work?
Let’s say you have a class Foo
:
1 2 3 4 |
|
When Foo(1, y=2)
is called, how is it that a new instance of Foo
is returned with x=1, y=2
initialized:
Foo(*args, **kwargs)
is equivalent toFoo.__call__(*args, **kwargs)
.- Since
Foo
is an instance oftype
,Foo.__call__(*args, **kwargs)
callstype.__call__(Foo, *args, **kwargs)
. type.__call__(Foo, *args, **kwargs)
callstype.__new__(Foo, *args, **kwargs)
which returnsobj
.__new__
method is responsible for allocating memory and actual object creation. It should be noted that while__new__
is a static method, you don’t need to declare it with@staticmethod
- it is special-cased by the Python interpreter.obj
is then initialized by callingobj.__init__(*args, **kwargs)
.obj
is returned.
So it goes like
Foo(*args, **kwargs)
-> Foo.__call__(*args, **kwargs)
-> type.__call__(Foo, *args, **kwargs)
-> type.__new__(Foo, *args, **kwargs)
-> obj.__init__(*args, **kwargs)
Singleton pattern
1 2 3 4 5 6 7 |
|
Borg pattern (nonpattern)
Unlike singleton, this creates multiple distinct instances, but the state is shared. There is no real use-case for this pattern.
1 2 3 4 5 6 7 8 9 10 |
|
Naming Notations
- Notations
- Postfix notation (object-oriented):
object.method()
orobject.attribute
. - Prefix notation (procedural programming):
function(object)
. The prefix notationstr(x)
is implemented asx.__str__()
under the hood. - Infix notation:
object+other
. A construct such asa+b
may be implemented asa.__add__(b)
orb.__radd__(a)
depending on the type of compatibility rules that were built into the class definitions for objectsa
andb
.
- Postfix notation (object-oriented):
Categories
- Attribute Access: E.g.,
object.attribute = value
ordel object.attribute
. - Callables: This special method implements what we see as a function that is applied to arguments, much like the built-in
len()
function. - Collections: These special methods implement the numerous features of collections. This involves methods such as
sequence[index]
,mapping[key]
, andset1|set2
. - Numbers: These special methods provide arithmetic operators and comparison operators. We can use these methods to expand the domain of numbers that Python works with.
- Contexts: There are two special methods we’ll use to implement a context manager that works with the
with
statement. - Iterators: There are special methods that define an iterator. This isn’t essential since generator functions handle this feature so elegantly.
Special methods
__str__()
- Theprint()
function will usestr()
to prepare an object for printing.__repr__()
- Theformat()
method of a string can also access these methods. When we use%r
or%s
formatting, we’re requesting__repr__()
or__str__()
, respectively.__bool__()
-__bytes__()
-__del__()
-
Notes
- Docstring. RST (ReStructured Text) markup.
References
- Books
- Learning Python the hard way
- Think Python
- Mastering object-oriented Python