Giter VIP home page Giter VIP logo

demystifying-python's Introduction

Demystifying Python

A Quick(ish) tutorial for Rubyists curious about learning Python.

##More similarities than differences

Both languages are high-level, dynamically typed, and object-oriented. Overall, they are pretty similar. But, they evolved in slightly different directions.

The same basic rules apply:

In [1]: 1 + 1
Out[1]: 2

In [2]: print "Hello World"
Hello World

In [3]: x = "some stuff"

In [4]: print x
some stuff

The syntax is different in a few small places:

  • manditory parenthesis
  • no implicit returns
  • whitespace instead of end statements
  • colons
  • different elsif
  • different && and || opperators
  • uppercase true and false
In [5]: def func(x,y,z):
   ...:     return x+y+z
   ...: 

In [6]: func(1,2,3)
Out[6]: 6

In [7]: def func2(a):
   ...:     if a <= 0:
   ...:         return "negative"
   ...:     elif a>0 and a<5:
   ...:         return "less than 5"
   ...:     else:
   ...:         return "more than 5"
   ...:     


In [8]: func2(3)
Out[8]: 'less than 5'

In [9]: func2(-1)
Out[9]: 'negative'

In [10]: func2(10)
Out[10]: 'more than 5'

In [11]: True or False
Out[11]: True

In [12]: True and False
Out[12]: False

But the idea is the same:

  • Do as much as possible in as few words as possible
  • Emphasize readability and programmer productivity

###Syntactical tricks

Most syntactical tricks you can do in Ruby, you can also do in Python.Here is a good list of stuff you CAN do in Python

Datatypes

  • No symbols
  • Lists == Arrays (with better slicing)
  • Dictionaries == Hashes (with JSON-y syntax, rather than hash rockets)
  • Tuples
  • Sets

Lists

In [13]: list = [0,1,2,3,4,5,6,7,8,9,'string', True, None]

In [14]: list[0]
Out[14]: 0

In [15]: list[3:8]
Out[15]: [3, 4, 5, 6, 7]

In [16]: list[:8]
Out[16]: [0, 1, 2, 3, 4, 5, 6, 7]

In [17]: list[8:]
Out[17]: [8, 9, 'string', True, None]

In [18]: list[-1]
#it didn't return anything

In [19]: list[-2]
Out[19]: True

In [20]: list[::2]
Out[20]: [0, 2, 4, 6, 8, 'string', None]

In [21]: list[::-2]
Out[21]: [None, 'string', 8, 6, 4, 2, 0]

###Dictionaries

In [22]: dict = {"a":1, "b":2, "c":[4,5,6]}

In [23]: dict["a"]
Out[23]: 1

In [24]: dict["c"]
Out[24]: [4, 5, 6]

In [25]: dict = {"a":1, "b":2, [1,2,3]:[4,5,6]}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-e5bc73dbf621> in <module>()
----> 1 dict = {"a":1, "b":2, [1,2,3]:[4,5,6]}

TypeError: unhashable type: 'list'

###Tuples

  • The same as lists, but immutable.
  • Tuples are good for passing series of arguments around without fear of the order being mutated.
  • They also have faster load time because they are immutable.
In [26]: tup = (1,2,3)

In [27]: tup[0]
Out[27]: 1

In [28]: tup.pop()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-29-9e226b0399e4> in <module>()
----> 1 tup.pop()

AttributeError: 'tuple' object has no attribute 'pop'

In [29]: del list
# Python is less strict about reserved words. Deleting the list variable here so we can use the list function.
# You can't delete variables in Ruby...You have to set the variable equal to nil, and wait for the garbage collector to pick it up.

In [30]: list(tup)
Out[30]: [1, 2, 3]

In [31]: tup
Out[31]: (1, 2, 3)

In [32]: list(tup).pop()
Out[32]: 3

###Sets

Sets are unordered collections of unique values. They also have faster load times than lists.

In [33]: a_set = {1,2,3,4,4,4,4,4,4,4}

In [34]: a_set
Out[34]: {1, 2, 3, 4}

In [35]: a_set.add(5)

In [36]: a_set
Out[36]: {1, 2, 3, 4, 5}

In [37]: b_set = set([3,3,3,3,3,3,3,3,3,3, "aaaaaaaaaa"])

In [38]: b_set
Out[38]: {3, 'aaaaaaaaaa'}

In [39]: a_set.union(b_set)
#or... a_set | b_set
Out[39]: {1, 2, 3, 4, 5, 'aaaaaaaaaa'}

In [40]: a_set.difference(b_set)
#or... a_set - b_set
Out[40]: {1, 2, 4, 5}

In [41]: a_set.intersection(b_set)
#or... a_set & b_set
Out[41]: {3}

##Living without blocks One undeniable drawback of Python: no blocks. You're forced to loop through things the old fashion way...with for/while loops:

In [42]: my_list = [1,2,3,4,5,6,7,8,9,10]

In [43]: for number in my_list:
   ....:     print number*number
   ....:     
1
4
9
16
25
36
49
64
81
100

In [44]: i = 0

In [45]: while i < my_list[-1]:
   ....:     print i
   ....:     i +=1
   ....:     
0
1
2
3
4
5
6
7
8
9

Keep in mind that, like Ruby, Python has a fantastic library to help you do what you want to do...it just might not be as obvious.

For example, array.each_with_index{|v,i| ...} == for i,v in enumerate(list): ...

This can make things a pain in the ass sometimes, but can also make things easier to reason about when reading nested loops.

In addition, Python has some "special sauce" to help make things easier.

###Comprehensions Syntactic sugar for making complicated lists/dictionaries/sets on the fly.

In [49]: [x for x in range(10)]
Out[49]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [50]: [x for x in range(5,10)]
Out[50]: [5, 6, 7, 8, 9]

In [51]: [x for x in range(2,10,2)]
Out[51]: [2, 4, 6, 8]

In [52]: comp = [x for x in range(2,13,2) if x%3==0]

In [53]: comp
Out[53]: [6,12]

In [54]: [x**x for x in range(2,10,2)]
Out[54]: [4, 256, 46656, 16777216]

In [55]: suits = ['Hearts','Spades','Clubs','Diamonds']

In [56]: values = ['Ace',2,3,4,5,6,7,8,9,10,'Jack','Queen','King']

In [57]: [(value, suit) for suit in suits for value in values]
Out[57]: 
[('Ace', 'Hearts'),
 (2, 'Hearts'),
 (3, 'Hearts'),
 (4, 'Hearts'),
 (5, 'Hearts'),
 (6, 'Hearts'),
 (7, 'Hearts'),
 (8, 'Hearts'),
 (9, 'Hearts'),
 (10, 'Hearts'),
 ('Jack', 'Hearts'),
 ('Queen', 'Hearts'),
 ('King', 'Hearts'),
 ('Ace', 'Spades'),
 (2, 'Spades'),
 (3, 'Spades'),

# and so forth...
]

In [58]: dict = {"key1":"val1", "key2":"val2", "key3":"val3"}

In [59]: {val:key for key, val in dict.items()}
Out[59]: {'val1': 'key1', 'val2': 'key2', 'val3': 'key3'}

###Generators

Generators are weird, and I don't understand them well. Here's what the docs say:

When you call a function, it gets a private namespace where its local variables are created. When the function reaches a return statement, the local variables are destroyed and the value is returned to the caller. A later call to the same function creates a new private namespace and a fresh set of local variables. But, what if the local variables weren’t thrown away on exiting a function? What if you could later resume the function where it left off? This is what generators provide; they can be thought of as resumable functions.

In other words, generators are single-use iteration tools that remember where you left off, and can pick up in that place whenever you need it.

In [60]: def generate_sq(n):
   ....:     for i in range(n):
   ....:         yield i**2
   ....:         

In [61]: gen = generate_sq(5)

In [62]: gen
Out[62]: <generator object generate_sq at 0x102a06e10>

In [63]: next(gen)
Out[63]: 0

In [64]: next(gen)
Out[64]: 1

In [65]: next(gen)
Out[65]: 4

In [66]: next(gen)
Out[66]: 9

In [67]: next(gen)
Out[67]: 16

In [68]: next(gen)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-71-8a6233884a6c> in <module>()
----> 1 next(gen)

StopIteration: 

Why would you use this? Something to do with performance. I can't think of any good examples, but probably useful where you'd ordinarily generate a new list for one value several times.

Ruby has "fibers", which are the same thing (I think)

##Functional programmning, "first class" functions...and decorators

In Python, you can pass functions as arguments to other functions.

First class functions exist in Ruby (kind of), and in JavaScript.

Let's say we want to time how long it takes to run a function:

In [69]: import time

In [70]: def time_it(func, args):
   ....:     before = time.time()
   ....:     func(args)
   ....:     after = time.time()
   ....:     print after - before
   ....:     

In [71]: def stuff(n):
   ....:     for x in xrange(n):
   ....:         x**2
   ....:         

In [72]: time_it(stuff, 5)
8.10623168945e-06

In [73]: time_it(stuff, 5000)
0.00114893913269

In [74]: time_it(stuff, 50000)
0.00881218910217

In [75]: time_it(stuff, 5000000)
0.403421878815

Let's say we want to permanently alter stuff's functionality:

In [76]: def time_wrapper(func):
   ....:     def new_func(args):
   ....:        before = time.time()
   ....:        func(args)
   ....:        after = time.time()
   ....:        print after - before
   ....:     return new_func
   ....: 

In [77]: stuff = time_wrapper(stuff)

In [78]: stuff(5)
6.91413879395e-06

Another way to write In [77]:

In [79]: @time_wrapper
   ....: def other_stuff(x):
   ....:     for number in range(x):
   ....:         number * 9 + 8
   ....:         

In [80]: other_stuff(10)
8.10623168945e-06

If you want to get fancy, you can also pass arguments to your decorators:

In [81]: def decorate_with_args(arg1, arg2):
   ....:     def wrap(func):
   ....:         print "inside wrap"
   ....:         print arg1, arg2
   ....:         def wrapped_func(*args):
   ....:             print "inside wrapped_func"
   ....:             print "decorator args:", arg1, arg2
   ....:             func(*args)
   ....:         return wrapped_func
   ....:     return wrap
   ....: 


In [82]: @decorate_with_args("woo", "hoo")
  .....: def more_stuff(a,b,c):
  .....:     print a,b,c
  .....:     
inside wrap
woo hoo

In [83]: more_stuff("yolo", "oloy", "qwerty")
inside wrapped_func
decorator args: woo hoo
yolo oloy qwerty

In [84]: more_stuff(1,2,3)
inside wrapped_func
decorator args: woo hoo
1 2 3

##Classes

A lot of rubyists say that Python is not object oriented. They're wrong.

Syntax is pretty similar, but there are some key differences:

  • No @s. Everything is self.whatever
  • No attr_reader, attr_writer, attr_accessor. Everything is public. Deal with it.
  • Magic methods: Like in Ruby, Python objects inherit behavior from a root object. You can overwrite these, but it looks less pretty.
In [85]: class OrangeTree(object):
   .....:     def __init__(self, n_of_oranges):
   .....:         self.oranges = n_of_oranges
   .....:     def orange_indeces(self):
   .....:         return range(self.oranges)
   .....:     def __str__(self):
   .....:         return "this tree has {0} oranges".format(self.oranges)
   .....:     

In [86]: orange = OrangeTree(5)

In [87]: orange.oranges
Out[87]: 5

In [88]: orange.orange_indeces
Out[88]: <bound method OrangeTree.orange_indeces of <__main__.OrangeTree object at 0x101ebf850>>

In [89]: orange.orange_indeces()
Out[89]: [0, 1, 2, 3, 4]

In [90]: orange
Out[90]: <__main__.OrangeTree at 0x101ebf850>

In [91]: print orange
this tree has 5 oranges

##So, why use Python? For performance (kind of) and Libraries

For the most part, Python is just a marginally uglier version of Ruby. In many cases, solving a problem in Ruby is faster, and ends up being easier on the eyes. However, I generally think that Python looks cleaner and is easier to reason about when solving math-heavy problems.

In any case, Python is fairly popular in data-intensive disciplines (academia, finance, data science). As a result, Pythong has some fantastic data-crunching tools.

###Cython I've never used this, but it looks incredibly useful in some circumstances.

Cython lets you (optionally) statically type variables, and compiles the Python code down to C code.

In some circumstances, you can make your code run about as fast as C code.

# Regular Python
def fib(n):
    a,b = 1,1
    for i in range(n):
        a,b = a+b, a
    return a

#Cython... supposedly 70x faster
def cfib(int n):
    cdef int i, a, b
    a,b = 1,1
    for i in range(n):
        a, b = a+b, a
    return a

Several data-crunching libraries are written largely in Cython, such as Numpy, SciPy, pandas, and scikit-learn

###Numpy & SciPy

If you ever need to use multi-dimensional arrays (in the mathematical sense, not the Ruby sense), Numpy is where it's at.

In [92]: import numpy as np

In [93]: list1 = [0,1,2,3,4,5,6,7,8,9]

In [94]: array1 = np.array(list1)

In [95]: array1
Out[95]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [96]: array1 + 5
Out[96]: array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

In [97]: array1 * 5
Out[97]: array([ 0,  5, 10, 15, 20, 25, 30, 35, 40, 45])

In [98]: array2 = array1 * 5

In [99]: array2 - array1
Out[99]: array([ 0,  4,  8, 12, 16, 20, 24, 28, 32, 36])

This also works for n-dimensional arrays.

The library also comes with a bunch of awesome functions that help with n-d array and linear algebra computations.

Numpy is included in the SciPy environment, which also contains libraries for plotting data (matplotlib), k-means clustering (cluster), machine learning (scikit-learn), and other awesome stuff.

###Pandas

If you're used to conducting data analysis with spreadsheets, pandas should seem pretty intuative. pandas sits on top of the SciPy environment, and groups data into "data frames". This allows you to perform operations on a DB as if they were organized by column and row. You can also do v-lookup-style operations, run multiple regressions. It also makes importing/exporting data and dealing with timestamps a lot easier.

More importly, it's a beast for cleaning up and reshaping data.

A seperate library, pandasql, allows you to make SQL queries on dataframes.

More on Pandas

###NLTK

Natural Language ToolKit. If you ever want to perform language analysis, look no further

Just like the Alchemy API, but free and more dynamic.

More on NLTK Free Book

###IPython IPython is an awesome Python REPL...it is far better than anything Ruby has ever come up with.

Also, it's got this awesome thing.

###Other Cool libraries I'm too lazy to write about

  • Twisted
  • Flask == Sinatra
  • Django == Rails

##Package Management

One undeniable negative of Python: package management sucks. It's often a major pain in the ass to download, install, and use these libraries. Thankfully, there are a few popular distributions out there that simplify the process.

I'm a big fan of Anaconda (free download). Here's a list of all the packages included.

##Version Management

Python 3 is the wave of the future. Unfortunately, Python 3 is not backwards compatable with Python 2. Therefore, a large chunk of Python coders are still using Python 2.7. I still use 2.7 because the Anaconda distribution is better for that version.

In any case, the differences aren't terribly significant, so I feel comfortable mirating to 3.4 whenever.

More info on the differences between Python 2.7 and Python 3.x

##More Resources

demystifying-python's People

Contributors

scyclow avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.