Introducción a Python

About Python

Elegant syntax


OOP with multiple inheritance


Strongly and dynamically typed


Clean error handling


Large standard library


Python 2.x

vs

Python 3.x

http://blog.aitorciki.net/2014/01/12/mejor-si-pero-para-quien/


https://python3wos.appspot.com/

Python interpreter

hello_world.py

#!/usr/bin/env python
print 'hello world!'


$ python hello_world.py
hello world!
$
$ python

Python 2.7.5 (default, May 12 2013, 12:00:47)
[GCC 4.8.0 20130502 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print 'hello world!'
hello world!
>>>

Modules and packages

fifa/
  __init__.py
  match.py
  teams/
    __init__.py
    players.py
    managers.py
    trophies.py
>>> from fifa.teams.players import Zidane
>>> zizou = Zidane()


>>> from fifa import teams
>>> zizou = teams.players.Zidane()


>>> from fifa.teams.trophies import *
>>> la_decima = UefaChampionsLeague()

Virtualenv, virtualenvwrapper & PIP

$ mkvirtualenv -p /usr/bin/python2.7 intro-python

Running virtualenv with interpreter /usr/bin/python2.7
New python executable in intro-python/bin/python2.7
Also creating executable in intro-python/bin/python
Installing setuptools...............................done.
Installing pip...............done.
(intro-python)$


(intro-python)$ exit
$
$ workon intro-python
(intro-python)$

Python Package Index


(intro-python)$ pip install requests

(intro-python)$ pip install -r requirements.txt

The Zen of Python

>>> import this
>>> import this
...
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Readability counts.
Special cases aren't special enough to break the rules.
Errors should never pass silently.
Unless explicitly silenced.
There should be one -- and preferably only one -- obvious way to do it.
Now is better than never.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

PEP-8

code is read much more often than it is written

http://legacy.python.org/dev/peps/pep-0008/
>>> from __future__ import braces
File "< stdin >", line 1
SyntaxError: not a chance

Basic types

Numbers

>>> type(1)
< type 'int' >

>>> type(10000000000000000000)
< type 'long' >
>>> type(1L)
< type 'long' >

>>> type(1.5)
< type 'float' >

>>> 3/2
1
>>> 3/2.
1.5

Bool

>>> type(True)
< type 'bool' >
>>> type(False)
< type 'bool' >

>>> True == 1
True
>>> False == 0

>>> issubclass(bool, int) True

Strings

>>> type('foo')
< type 'str' >
>>> type("Foo's bar")
< type 'str' >

>>> multiline_string = """This is a
... multiline string"""
>>> print multiline_string
This is a
multiline string

>>> print '%s is a str and %i is a int' % ('foo', 1)
foo is a str and 1 is a int
>>> '{0}{1}{0}'.format('abra', 'cad')
'abracadabra'

More about strings

>>> 'Python rocks'.find('rocks')
7
>>> 'Python rocks'.find('PHP')
-1

>>> 'Python rocks'.split()
['Python', 'rocks']
>>> 'Python rocks'.split('o')
['Pyth', 'n r', 'cks']

>>> 'Python rocks'.replace('Python', "Java doesn't")
"Java doesn't rocks"

>>> ','.join(['a', 'b', 'c'])
'a,b,c'

Type conversion

>>> str(2)
'2'

>>> int('2')
2
>>> int('foo')
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
ValueError: invalid literal for int() with base 10: 'foo'

>>> 2 + '2'
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Operators

Numeric

a + b    a - b    a * b    a / b
a % b    a ** b

Comparators

a == b    a != b    a > b    a < b
a >= b    a <= b    a is True    a is not False

Logic

not a    a and b    a or b

Collections

first look

List

>>> even_nums = [2, 4, 6, 8]
>>> even_nums[0]
2

>>> 6 in even_nums
True

>>> even_nums.append(10)
>>> even_nums
[2, 4, 6, 8, 10]

>>> other_list = [1, ['2', 3], 'foo']
>>> even_nums + other_list
[2, 4, 6, 8, 10, 1, ['2', 3], 'foo']

Dictionary

>>> user = {'name': 'javimb', 'is_staff': True}
>>> user['name']
'javimb'

>>> user['password']
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
KeyError: 'password'
>>> user.get('password')
>>> user.get('password', 'foobar')
'foobar'

>>> user['is_authenticated'] = True
>>> user
{'is_staff': True, 'is_authenticated': True, 'name': 'javimb'}

Tuple

>>> even_nums = (2, 4, 6, 8)
>>> type(even_nums) < type 'tuple' >

>>> even_nums[2]
6
>>> 8 in even_nums
True

>>> other_tuple = 2, 'foo', [2, True, 0]
>>> other_tuple
(2, 'foo', [2, True, 0])

Beginner tricks

>>> x = 0
>>> x += 3
>>> x
3
>>> x -= 2

>>> a = 'foo'
>>> b = 'bar'
>>> a, b = b, a
>>> a
'bar'
>>> b
'foo'

>>> a, b, c = 1, 2, 3
>>> a < b and b < c
True
>>> a < b < c
True

Slicing

>>> even_nums = [2, 4, 6, 8]
>>> even_nums[1:3]
[4, 6]
>>> even_nums[:3]
[2, 4, 6]
>>> even_nums[::-1]
even_nums = [2, 4, 6, 8]

>>> 'Python rocks'[::2]
'Pto ok'

Some built-in functions

>>> len('Python rocks')
12

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(2, 5)
[2, 3, 4]

>>> sum(range(10))
45

>>> round(3,141595, 4)
3.1416
>>> map(str, range(10))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

>>> zip([1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]

>>> sorted(range(10)[::-1])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Sentences

if-else

>>> user = {'name': 'javimb', 'is_staf': True}
>>> if user['is_staff']:
...     print 'Hello boss!'
... else:
...     print 'Hello {name}'.format(name=user['name'])
...
Hello boss!

>>> if user['name'] == 'Raul':
...     print 'Hello Raul!'
... elif user['name'] == 'javimb':
...     print 'Hello Javi!'
... else:
...      print 'Hello!'
...
Hello Javi!

while

>>> num = 0
>>> while True:
...     if num == 2:
...         num = 3
...         continue
...     if num == 5:
...         break
...     num += 1
...     print num
...
1
2
4
5

for

>>> users = [{'name': 'javimb', 'is_staf': True}, {'name': 'Raul', 'is_staff': False}]
>>> for user in users:
...      print user['name']
...
javimb
Raul

>>> for num in range(3):
...     print num
...
0
1
2

try-catch

>>> try:
...      2 / 0
... except:
...      print 'Error: division by zero'
...
Error: division by zero

>>> try:
...      2 / range(5)[10]
... except ZeroDivisionError:
...      print 'Error: division by zero'
... except IndexError:
...     print 'Error: out of range'
...      raise
...
Error: out of range
Traceback (most recent call last):
File "< stdin >", line 2, in < module >
IndexError: list index out of range

Functions

>>> def first_function(a, b):
...      return a + b
...

>>> def second_function(x, y):
...      return x ** 2, y ** 2
...
>>> a, b = second_function(2, 4)

>>> def third_function(x=0, y=2):
...      return x ** 2, y ** 2
...
>>> third_function()
(0, 4)

>>> second_function(x=4, y=4)
(16, 16)
>>> def sample_funtion(*args, **kwargs):
...      return args[0] * kwargs.get('mul', 0)
...
>>> sample_function(1)
0
>>> sample_function(2, mul=5)
10

>>> def last_function(x, *args):
...      return None
...
>>> last_function()
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
TypeError: last_function() takes at least 1 argument (0 given)
>>> f = lambda x: x ** 2
>>> f(2)
4

>>> filter(lambda x: not x % 2, range(10))
[0, 2, 4, 6, 8]

Beginner exercises

Exercise 1

Thanks @jorgebastida!

https://github.com/javimb/intro-python/tree/master/exercises/exercise_1

Exercise 2

Thanks @jorgebastida!

https://github.com/javimb/intro-python/tree/master/exercises/exercise_2

Classes

class Car():
    def __init__(self, fuel):
        self.fuel = fuel

    def move(self):
        if not self.fuel:
            print 'No fuel'
        else:
            self.fuel -= 1
            print 'Fuel: %i' % self.fuel

>>> car = Car(5)
>>> car.move()
Fuel: 4

New-style classes

default at Python 3.x

class Car(object):
    @property
    def fuel(self):
        return self._fuel

    @fuel.setter
    def fuel(self, value):
        self._fuel = value + 5

...

>>> car = Car(5)
>>> car.move()
Fuel: 4
class Car(object):
...

    @staticmethod
    def kmh_to_mph(kmh):
        return kmh * 0.621371192

>>> print Car.kmh_to_mph(100)
62.1371192
class Car(object):
    wheels = 4
    axis = 2
...
    @classmethod
    def wheels_per_axis(cls):
        return cls.wheels / cls.axis

>>> car = Car(5)
>>> car.wheels = 6
>>> print car.wheels / car.axis
3
>>> print car.wheels_per_axis()
2
class Vehicle(object):
...

class FuelVehicle(object):
...

class Car(FuelVehicle, Vehicle):
...

List comprehesion

def triple(x):
    return x * 3

def is_even(x):
    return not x % 2

>>> some_even_nums = map(triple, filter(is_even, range(10)))
>>> some_even_nums
[0, 6, 12, 18, 24]
>>> some_even_nums = [x * 3 for x in range(10) if not x % 2]
>>> some_even_nums
[0, 6, 12, 18, 24]

>>> [x * 3 for x in range(10)]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

More exercises

Exercise 3

https://github.com/javimb/intro-python/tree/master/exercises/exercise_3

Exercise 4

https://github.com/javimb/intro-python/tree/master/exercises/exercise_4

Generators

def first_generator(limit):
    i = 0
    while i < limit:
        yield i
        i += 1

>>> gen = first_generator(3)
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
StopIteration
>>> gen = [x for x in range(1000000)] #0.1085 s
>>> gen = (x for x in range(1000000)) #0.0219 s

Decorators

def admin_dashboard(name):
    print "%s's admin dashboard" % name

>>> admin_dashboard('javimb')
javimb's admin dashboard

def admin_dashboard(name):
    if name != 'javimb':
        print '404'
    else:
        print "%s's admin dashboard" % name

>>> admin_dashboard('Raul')
404
def staff_required(original_func):
    def new_func(name):
        if name != 'javimb':
            print '404'
            return
        return original_func(name)
    return new_func

@staff_required
def admin_dashboard(name):
     print "%s's admin dashboard" % name
>>> admin_dashboard
< function new_func at 0x7f8bf1b6c5f0 >

from functools import wraps

def staff_required(original_func):
    @wraps(original_func)     def new_func(name):
        if name != 'javimb':
            print '404'
            return
        return original_func(name)
    return new_func

>>> admin_dashboard
< function admin_dashboard at 0x7f1f06a427d0 >

Decorators

the hard way

Namespaces & scopes

A namespace is a mapping from names to objects


A scope is the textual region where a namespace is accessible

def fibonacci(x):
    if x < 2:
        return x
    return fibonacci(x-1) + fibonacci(x-2)

>>> fibonacci(40) # 49.80 s
CACHE = {}

def fibonacci(x):
    if x < 2:
        return x
    value = CACHE.get(str(x))
    if not value:
        value = fibonacci(x-1) + fibonacci(x-2)
        CACHE[str(x)] = value
    return value

>>> fibonacci(40) # 0.000169 s
CACHE = {}

def fibonacci(x):
    if x < 2:
        return x
    return fibonacci(x-1) + fibonacci(x-2)

original_fibonacci = fibonacci

def fibonacci(x):
    value = CACHE.get(str(x))
    if not value:
        value = original_fibonacci(x)
        CACHE[str(x)] = value
    return value
CACHE = {}

def fibonacci(x):
    if x < 2:
        return x
    return fibonacci(x-1) + fibonacci(x-2)

def cache(func):
    def cached_func(x):
        value = CACHE.get(str(x))
        if not value:
            value = func(x)
            CACHE[str(x)] = value
        return value
    return cached_func

fibonacci = cache(fibonacci)
CACHE = {}

def cache(func):
    def cached_func(x):
        value = CACHE.get(str(x))
        if not value:
            value = func(x)
            CACHE[str(x)] = value
        return value
    return cached_func

@cache
def fibonacci(x):
    if x < 2:
        return x
    return fibonacci(x-1) + fibonacci(x-2)

Other tricks

>>> a = 'foo' if True else 'bar'
>>> dic = {'a': True, 'b': False, 'c': true}
>>> def func(a, b, c):
...
...
>>> func(**dic)

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> _ # only shell mode
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> pow(2, 3)
8
>>> from functools import partial
>>> pow_base_2 = partial(pow, 2)
>>> pow_base_2(10)
1024

Q&A

Thanks!



http://python.javimb.com
https://github.com/javimb/intro-python