A gentle introduction to Python for C programmers
~ $ python
Python 3.3.0 (default, Dec 15 2012, 17:19:46)
[GCC 4.5.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ian
>>> help(ian)
Enough about me...
Help on module ian:
DESCRIPTION
Ian Cordasco
~~~~~~~~~~~~
- Developer at Bendyworks
- Open Source Lover
* Maintainer of Flake8 (python code-quality tool)
* Maintainer of Requests (python library for HTTP/1.1)
* Author of github3.py (wrapper around the GitHub API)
* Author of betamax (recording your HTTP interactions so the NSA doesn't have to)
* Author of uritemplate.py (implementation of RFC 6570)
* Contributor to lots of other stuff
- Python developer for the last 2 years
- C developer before that (still loves it)
lines 1-23/23 (END)
In C
#include <stdio.h>
int main(char **argv, int argc){
puts("Hello World!\n");
return 0; /* Technically not even necessary anymore in C11 */
}
In Python
print("Hello World!")
Now that we have that out of the way
...
C
int add(int a, int b){
return a + b;
}
int main(void){ /* We're all adults here */
(void)add(1, 2);
(void)add(1, 'c');
(void)add(1, "characters");
return 0;
}
Python
def add(a, b):
return a + b
add(1, 2)
add(1, "foo") # Wait, what?!
What is actually happening here?!
In that code example:
def add(a, b):
return a + b
add(1, 2)
add(1, "foo")
What is actually happening is something along these lines:
The difference is that the second case will raise a TypeError
Let's look at that code example again
def add(a, b):
return a + b
add(1, "string")
We see a TypeError in the console
~ $ python add.py
Traceback (most recent call last):
File "add.py", line 4, in <module>
add(1, "string")
File "add.py", line 2, in add
return a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
We get the line numbers and an explanation of the exception
Our C example, when compiled provides
~ $ gcc add.c -o add
add.c: In function 'main':
add.c:10: warning: passing argument 2 of 'add' makes integer from pointer without a cast
Who said anything about pointers?
This is a common complaint among new programmers
But who said anything about new programmers?
(And who is dog?)
I did. Just now. Did you miss it?
#include <stdio.h>
int main(void){
printf("%d\n", 1 + 2);
return 0;
}
In Pythondef main():
print("%d" % (1 + 2))
Which one prints out 3?
If you run the following instead
def main():
print("%d" % (1 + 2))
main()
or even just
print("%d" % (1 + 2))
you'll see 3 printed to the screen
Why?
def function():
# This function does something
if __name__ == '__main__':
function()
Woah what is this "__name__" thing?!import fake_module
if __name__ == '__main__':
print("From within my_main.py")
print("fake_module.__name__ = %s" % fake_module.__name__)
print("my_main.__name__ = %s" % __name__)
print("From within fake_module.py")
print("fake_module.__name__ = %s" % __name__)
~ $ python my_main.py
From within fake_module.py
fake_module.__name__ = fake_module
From within my_main.py
fake_module.__name__ = fake_module
my_main.__name__ = __main__
Seriously, even integers have methods
~ $ python
Python 3.3.0 (default, Dec 15 2012, 17:19:46)
[GCC 4.5.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> help(1)
Help on int object:
class int(object)
| int(x[, base]) -> integer
|
| Convert a string or number to an integer, if possible. A floating
| point argument will be truncated towards zero (this does not include a
| string representation of a floating point number!) When converting a
| string, use the optional base. It is an error to supply a base when
| converting a non-string.
|
| Methods defined here:
|
| __abs__(...)
| x.__abs__() <==> abs(x)
|
| __add__(...)
| x.__add__(y) <==> x+y
...
So simple integer arithmetic can be written two ways:
1 + 1
1.__add__(1)
In Python, the obvious way is the right way
# |-1| == 1
assert abs(-1) == 1
assert (-1).__abs__() == 1
In many cases there are built-in functions that exist
simply to call methods on built-in objects.
a = [1, 2, 3]
print(a[0]) # prints 1
print(a[-1]) # prints 3
print(a[-3]) # prints 1
a.append(4)
print(a[-1]) # prints 4
a[2] = 10
b = {1: 2, 'key': 'value'}
print(b[1]) # prints 2
print(b['key']) # prints value
b['new key'] = 'new value'
c = {1, 2, 3, 4}
d = {5, 6, 7, 8}
print(c.union(d)) # prints {1, 2, 3, 4, 5, 6, 7, 8}
e = (1, 2, 3)
print(e[0]) # prints 1
print(e[-1]) # prints 3
f = 'string'
print(f[0]) # prints s
print(f[0:3]) # prints str
g = 1 + 1j # Yay for Electrical Engineers -_-
print(1 + g) # prints (2+1j)
print(1j + g) # prints (1+2j)
Rough C:
for (t_node node = list->head; node; node = node->next) {
dump_node(node);
}
Python:
l = [1, 2, 3, 4, 5]
for i in l:
print(i)
for item in [1, 2, 3]: # prints "1 2 3"
print(item, end=' ')
d = {'key0': 'value0', 'key0': 'value0'} for item in d: # prints "key1 => value1 key0 => value0"
print("%s => %s" % (item, d[item]), end=" ")
for c in 'this is a string': # prints "t h i s i s a s t r i n g"
print(c, end=" ")
This last one, most people despise
Let's assume arrays are like tuples
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
for (; i < 10; i++) {
printf("%d ", a[i]);
}
a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
i = 0
while i < 10:
print(a[i], end=" ")
i += 1
vs
a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for i in a:
print(i, end=" ")
Bye, bye loop counter. I shall miss you.
a = "string foo bar"
print(a[:6]) # prints string
print(a[0:6])
print(a[7:10]) # prints foo
print(a[11:]) # prints bar
b = [1, 2, 3, 4]
assert b[:3] == [1, 2]
assert b[2:] == [2, 3, 4]
Slices seem like magic but they're awesome
Unfortunately they're a bit beyond the scope of this talk
But after the next topic I can discuss them a bit if I have time
In C:
/* user.h */
typedef struct { /* We're all good citizens who use typedefs, right? */
int id;
char* login;
char* real_name;
/* ... */
} t_gh_user;
t_gh_user* gh_new_user(int, char*, char*, /* ... */);
int gh_update_user(t_gh_user, char*, char*, /* ... */);
/* And many more "methods" */
In Python:
class User(object):
def __init__(self, id, login, real_name,
# ...
):
self.id = id
self.login = login
# ...
def update(self, # ...
):
# ...
In C:
t_gh_user* u = gh_new_user(1, "mojombo", "Tom Preston Warner", /* ... */);
if (!gh_update_user(u, "Tom Preston (the Awesome) Warner", /* ... */) {
error("We can not update mojombo's profile! Abort! Abort!");
}
In Python:
u = User(login="mojombo", id=1, real_name="Tom Preston Warner", #...
)
try:
u.update(real_name="Tom Preston (the Awesome) Warner", #...
)
except Exception:
error("We can not update mojombo's profile! Abort! Abort!)
__init__
__init__
's first parameter is self
self
self
.You should never need to see them
See possible up-coming talk on metaprogramming in Python
This may or may not be a vision test
(Twitter, GitHub, StackOverflow)