go.talks: new talk Go for Pythonistas
R=adg
CC=golang-dev
https://golang.org/cl/16990043
diff --git a/2013/go4python.slide b/2013/go4python.slide
new file mode 100644
index 0000000..c1ed696
--- /dev/null
+++ b/2013/go4python.slide
@@ -0,0 +1,446 @@
+Go for Pythonistas
+
+Francesc Campoy Flores
+Gopher at Google
+http://campoy.cat
+
+@francesc
+campoy@golang.com
+
+* Goal of this talk
+
+Whetting your appetite for Go
+
+.image go4python/img/gopher.jpg
+
+* My tactics
+
+1. Showing you how Go is like Python.
+
+2. Showing you how Go is _not_ like Python.
+
+* Python, Go, and me
+
+Software Engineer at Google: _Feb_11-Aug_12_
+
+- Lots of C++ and Python.
+
+- SQL to C++ compiler in Python.
+
+Go Developer Relations: _Aug_12_ - `datetime.now()`
+
+- Lots of Go.
+
+* Things I don't like about Python (it'll be short)
+
+* Beautiful and simple
+
+Dynamic typing - nice because it's concise, like Python.
+
+ a = "hello"
+ b = 1
+ # but also
+ a = 2
+
+Static typing - can be verbose, like Java or C++.
+
+ Foo foo = new Foo();
+
+Static typing with inferred types, like Go.
+
+ a := "hello"
+ b := 1
+ // but no
+ a = 2
+
+Statically-typed Python? Check Guido's [[http://www.mypy-lang.org/][mypy]]
+
+* Run time pyrotechnics
+
+.play go4python/dyntyp.py /name/,
+
+I don't want start a flame war here but ...
+
+*100%*code*coverage*is*a*symptom*
+
+- Code coverage should point you to untested cases.
+- Not a way to find typos!
+- 100% code coverage doesn't mean bug free.
+
+* Other things I don't like
+
+- Deploying - managing dependencies.
+
+- Performance - "not too slow" is often not fast enough.
+
+- Magic! (e.g.: `__magic__`: `**kargs`, `__getattr__`)
+
+A list of magic methods in Python:
+
+.link http://www.rafekettler.com/magicmethods.html
+
+* And I *do* like concurrency!
+
+A lot has been said about Python's infamous Global Interpreter Lock.
+
+You should watch [[http://blip.tv/carlfk/mindblowing-python-gil-2243379][Mindblowing Python GIL]], by David Beazley.
+
+* Things I like about Python
+
+* Things I like about Python
+
+- The Zen of Python. ([[http://talks.golang.org/2012/zen.slide#1][Go and the Zen of Python]])
+
+- Hashes and arrays are part of the language.
+
+- The standard library.
+
+- Magic! A bit of code can do a lot.
+
+* A bit of code
+
+* fib.py
+
+Have you ever heard of Fibonacci?
+
+.play go4python/fib.py /fib/,
+
+* fib.go
+
+Something familiar?
+
+.play go4python/fib.go /func/,
+
+* Fibonacci without generators? What?
+
+Python generators are awesome.
+
+.code go4python/fib-gen.py /fib/,/^$/
+
+Mechanically complex.
+
+.play go4python/fib-gen.py /f = fib/,/^$/
+
+But very easy to use.
+
+.play go4python/fib-gen.py /for x/,/^$/
+
+* Python generators
+
+.image go4python/img/fib-py.png 500 320
+
+Note the generator executes concurrently. Hmm... I like concurrency.
+
+* Go concurrency
+
+Based on *goroutines* and *channels*.
+
+- Goroutines: very light processing actors (the gophers).
+
+- Channels: typed, synchronized, thread-safe pipes (the arrows).
+
+.image go4python/img/funnelin.jpg
+
+* "Generator" goroutines
+
+.image go4python/img/fib-go.png 500 350
+
+* "Generator" goroutines
+
+Uses a channel send instead of `yield`.
+
+.code go4python/fib-gen.go /fib/,/^}/
+
+.play go4python/fib-gen.go /main\(/,
+
+* "Generator" goroutines
+
+A more generator-like style:
+
+.play go4python/fib-gen2.go /func/,
+
+* Exercise: generating prime numbers
+
+Write a function that returns a channel and sends the first n prime numbers on
+it.
+
+Given the function `prime`:
+
+.code go4python/genex.go /prime/,/^}/
+
+Use the Go playground:
+
+.link http://golang.org/s/go4py-ex1
+
+* Solution: generating prime numbers
+
+.code go4python/genex.go /primes\(/,/^}/
+
+.play go4python/genex.go /main\(/,
+
+* Exercise: Fibonacci primes
+
+Write a `filterPrimes` function that takes a channel of ints as a
+parameter and returns another channel of ints.
+
+All the prime numbers that `filterPrimes` receives from the input channel are
+sent into the output channel.
+
+Complete this code snippet:
+
+.link http://golang.org/s/go4py-ex2
+
+* Solution: Fibonacci primes
+
+.code go4python/genex2.go /filterPrimes\(/,/^}/
+
+.play go4python/genex2.go /main\(/,
+
+* But there's much more
+
+Goroutines and channels aren't just for generators. They can be used to model
+all kinds of concurrent systems.
+
+To learn more:
+
+- [[http://talks.golang.org/2012/concurrency.slide#1][Concurrency patterns]], by Rob Pike
+
+- [[http://talks.golang.org/2013/advconc.slide#1][Advanced Concurrency Patterns]], by Sameer Ajmani
+
+* Object-oriented Go
+
+* Object-oriented Go
+
+A type declaration.
+
+.code go4python/typesandmethods.go /Name/,/^}/
+
+A method declaration.
+
+.code go4python/typesandmethods.go /String/,/^}/
+
+Constructing a `Name` and using it.
+
+.play go4python/typesandmethods.go /William/,/Print/
+
+* Methods on anything
+
+There's more to types than structs.
+
+.code go4python/typesandmethods.go /SimpleName/
+
+You can define methods on any type.
+
+.code go4python/typesandmethods.go /SimpleName\)/
+
+Or almost any type.
+
+ func (s string) NoWay()
+
+You can *only* define methods on types within the same package.
+
+* Duck typing
+
+* Duck typing
+
+_If_it_walks_like_a_duck_..._
+
+What defines a duck?
+
+- Is there an explicit list of "duck" features?
+
+- What if the duck is not exactly a duck?
+
+s/duck/file-like object/g
+
+* Quack?
+
+.image go4python/img/duck.jpg 500 500
+
+* Go interfaces
+
+Simply a set of methods.
+
+From the `fmt` package:
+
+ type Stringer interface {
+ String() string
+ }
+
+`fmt.Println` calls the String method if the parameter is a `Stringer`.
+
+.play go4python/typesandmethods.go /second/,/Print/
+
+A type with all the methods of the interface implements the interface.
+
+*Implicit*satisfaction*==*No*"implements"*
+
+Structural typing: it doesn't just sound like a duck, it *is* a duck.
+
+And that's checked at compile time.
+
+* Decorators
+
+* Decorators
+
+A convenient way to wrap a function.
+
+.code go4python/deco.py /auth_required/,/^$/
+
+A function can be decorated using `@`.
+
+.code go4python/deco.py /myHandler/,/user/
+
+* Decorators
+
+If we run it.
+
+.play go4python/deco.py /try/,
+
+This is unauthorized:
+
+.link http://localhost:8080/hi
+
+This is authorized:
+
+.link http://localhost:8080/hi?user=john
+
+* Decorators in Go?
+
+Not exactly, but close enough.
+
+Go doesn't provide decorators in the language, but its function literal syntax and simple scoping rules make it easy to do something similar.
+
+.code go4python/deco.go /hiHandler/,/^\)/
+
+A wrapper function.
+
+.code go4python/deco.go /authRequired/,/^$/
+
+* Decorators in Go?
+
+.play go4python/deco.go /func main/,/^}/
+
+This is unauthorized:
+
+.link http://localhost:8080/hi
+
+This is authorized:
+
+.link http://localhost:8080/hi?user=john
+
+* Exercise: errors in HTTP handlers
+
+In Go, functions can return errors to indicate that something bad happened.
+
+The `net/http` package from the standard library defines the type `HandlerFunc`.
+
+ type HandlerFunc func(ResponseWriter, *Request)
+
+But it's often useful to unify the error handling into a single function to avoid
+repetition.
+
+.code go4python/decoex.go /errorHandler/
+
+Write a decorator that given a `errorHandler` returns a `http.HandlerFunc`.
+If an error occurs it logs it and returns an http error page.
+
+* Exercise: errors in HTTP handlers (continuation)
+
+Given the function `handler`.
+
+.code go4python/decoex.go /handler/,/^}/
+
+We want to use it as follows.
+
+.code go4python/decoex.go /HandleFunc/
+
+Implement `handleError` using the playground.
+
+.link http://golang.org/s/go4py-ex3
+
+* Solution: errors in HTTP handlers
+
+.code go4python/decoex.go /handleError/,/^}/
+
+.code go4python/decoex.go /Fake/,/^$/
+
+.play go4python/decoex.go /john/,/^$/
+
+* Monkey patching
+
+* Monkey patching
+
+"A monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code." - _Wikipedia_
+
+.image go4python/img/monkey.jpg 400 500
+
+* Monkey patching
+
+Also known as "duck punching" ... poor duck.
+
+Often used for testing purposes.
+
+For example, say we want to test this function:
+
+.code go4python/monkey.py /def say_hi/,/^$/
+
+Which depends on a function that makes an HTTP request:
+
+.code go4python/monkey.py /def auth/,/^$/
+
+* Monkey patching
+
+We can test `say_hi` without making HTTP requests by stubbing out `auth`:
+
+.play go4python/monkey.py /sayhitest/,/done/
+
+* Gopher punching!
+
+The same effect can be achieved in Go.
+
+.code go4python/monkey.go /sayHi/,/^}/
+
+Which depends on
+
+.code go4python/monkey.go /auth /,/^}/
+
+* Gopher punching!
+
+Our test code can change the value of auth easily.
+
+.play go4python/monkey.go /TestSayHi/,/^}/
+
+* Conclusion
+
+* Conclusion
+
+Go is a bit like Python
+
+- simple
+- flexible
+- fun
+
+but a bit different too
+
+- fast
+- concurrent
+- statically typed
+
+_Disclaimer_:_
+
+- "No pythons, ducks, monkeys or gophers were harmed while writing this talk"
+
+* Try it
+
+Next steps
+
+.link http://golang.org
+
+Learn Go from your browser
+
+.link http://tour.golang.org
+
+The community: golang-nuts
+
+.link https://groups.google.com/d/forum/golang-nuts
diff --git a/2013/go4python/deco.go b/2013/go4python/deco.go
new file mode 100644
index 0000000..b589a26
--- /dev/null
+++ b/2013/go4python/deco.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+)
+
+func authRequired(f http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if r.FormValue("user") == "" {
+ http.Error(w, "unknown user", http.StatusForbidden)
+ return
+ }
+ f(w, r)
+ }
+}
+
+var hiHandler = authRequired(
+ func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Hi, %v", r.FormValue("user"))
+ },
+)
+
+func main() {
+ http.HandleFunc("/hi", hiHandler)
+ http.ListenAndServe(":8080", nil)
+}
diff --git a/2013/go4python/deco.py b/2013/go4python/deco.py
new file mode 100644
index 0000000..bf46318
--- /dev/null
+++ b/2013/go4python/deco.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
+from urlparse import urlparse,parse_qs
+
+PORT_NUMBER = 8080
+
+def auth_required(myfunc):
+ def checkuser(self):
+ user = parse_qs(urlparse(self.path).query).get('user')
+ if user:
+ self.user = user[0]
+ myfunc(self)
+ else:
+ self.wfile.write('unknown user')
+ return checkuser
+
+
+class myHandler(BaseHTTPRequestHandler):
+ @auth_required
+ def do_GET(self):
+ self.wfile.write('Hello, %s!' % self.user)
+
+try:
+ server = HTTPServer(('', PORT_NUMBER), myHandler)
+ server.serve_forever()
+
+except KeyboardInterrupt:
+ server.socket.close()
diff --git a/2013/go4python/decoex.go b/2013/go4python/decoex.go
new file mode 100644
index 0000000..42725db
--- /dev/null
+++ b/2013/go4python/decoex.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+)
+
+type errorHandler func(http.ResponseWriter, *http.Request) error
+
+func handleError(f errorHandler) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ err := f(w, r)
+ if err != nil {
+ log.Printf("%v", err)
+ http.Error(w, "Oops!", http.StatusInternalServerError)
+ }
+ }
+}
+
+func handler(w http.ResponseWriter, r *http.Request) error {
+ name := r.FormValue("name")
+ if name == "" {
+ return fmt.Errorf("empty name")
+ }
+ fmt.Fprintln(w, "Hi,", name)
+ return nil
+}
+
+// resp implements http.ResponseWriter writing
+type dummyResp struct {
+ io.Writer
+ h int
+}
+
+func newDummyResp() http.ResponseWriter {
+ return &dummyResp{Writer: &bytes.Buffer{}}
+}
+
+func (w *dummyResp) Header() http.Header { return make(http.Header) }
+func (w *dummyResp) WriteHeader(h int) { w.h = h }
+func (w *dummyResp) String() string { return fmt.Sprintf("[%v] %q", w.h, w.Writer) }
+
+func main() {
+ http.HandleFunc("/hi", handleError(handler))
+
+ // ListenAndServe is not allowed on the playground.
+ // http.ListenAndServe(":8080", nil)
+
+ // In the playground we call the handler manually with dummy requests.
+
+ // Fake request without 'name' parameter.
+ r := &http.Request{}
+ w := newDummyResp()
+ handleError(handler)(w, r)
+ fmt.Println("resp a:", w)
+
+ // Fake request with 'name' parameter 'john'.
+ r.Form["name"] = []string{"john"}
+ w = newDummyResp()
+ handleError(handler)(w, r)
+ fmt.Println("resp b:", w)
+
+}
diff --git a/2013/go4python/dyntyp.py b/2013/go4python/dyntyp.py
new file mode 100644
index 0000000..fa9e4ea
--- /dev/null
+++ b/2013/go4python/dyntyp.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+
+import random
+
+name = 'pythonista'
+
+# This code only works half of the time.
+if random.random() > 0.5:
+ print 'hey '+name+', you win!'
+else:
+ print 'sorry '+nane+', you lose'
diff --git a/2013/go4python/fib-gen.go b/2013/go4python/fib-gen.go
new file mode 100644
index 0000000..5142cc7
--- /dev/null
+++ b/2013/go4python/fib-gen.go
@@ -0,0 +1,21 @@
+package main
+
+import "fmt"
+
+func fib(c chan int, n int) {
+ a, b := 0, 1
+ for i := 0; i < n; i++ {
+ a, b = b, a+b
+ c <- a // HL
+ }
+ close(c)
+}
+
+func main() {
+ c := make(chan int)
+ go fib(c, 10) // HL
+
+ for x := range c { // HL
+ fmt.Println(x)
+ }
+}
diff --git a/2013/go4python/fib-gen.py b/2013/go4python/fib-gen.py
new file mode 100644
index 0000000..bc02cfd
--- /dev/null
+++ b/2013/go4python/fib-gen.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+def fib(n):
+ a, b = 0, 1
+ for i in range(n):
+ a, b = b, a + b
+ yield a
+
+f = fib(10)
+try:
+ while True:
+ print f.next()
+except StopIteration:
+ print 'done'
+
+for x in fib(10):
+ print x
+print 'done'
diff --git a/2013/go4python/fib-gen2.go b/2013/go4python/fib-gen2.go
new file mode 100644
index 0000000..9625ba1
--- /dev/null
+++ b/2013/go4python/fib-gen2.go
@@ -0,0 +1,22 @@
+package main
+
+import "fmt"
+
+func fib(n int) chan int {
+ c := make(chan int) // HL
+ go func() { // HL
+ a, b := 0, 1
+ for i := 0; i < n; i++ {
+ a, b = b, a+b
+ c <- a // HL
+ }
+ close(c)
+ }()
+ return c
+}
+
+func main() {
+ for x := range fib(10) {
+ fmt.Println(x)
+ }
+}
diff --git a/2013/go4python/fib.go b/2013/go4python/fib.go
new file mode 100644
index 0000000..dbfef11
--- /dev/null
+++ b/2013/go4python/fib.go
@@ -0,0 +1,24 @@
+package main
+
+import "fmt"
+
+func fib(n int) int {
+ a, b := 0, 1
+ for i := 0; i < n; i++ {
+ a, b = b, a+b
+ }
+ return b
+}
+
+func fibRec(n int) int {
+ if n <= 1 {
+ return 1
+ }
+ return fibRec(n-1) + fibRec(n-2)
+}
+
+func main() {
+ for i := 0; i < 10; i++ {
+ fmt.Println(fib(i), fibRec(i))
+ }
+}
diff --git a/2013/go4python/fib.py b/2013/go4python/fib.py
new file mode 100755
index 0000000..afc57fa
--- /dev/null
+++ b/2013/go4python/fib.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+def fib(n):
+ a, b = 0, 1
+ for i in range(n):
+ a, b = b, a + b
+ return b
+
+def fib_rec(n):
+ if n <= 1:
+ return 1
+ else:
+ return fib_rec(n-1) + fib_rec(n-2)
+
+for x in range(10):
+ print fib(x), fib_rec(x)
diff --git a/2013/go4python/genex.go b/2013/go4python/genex.go
new file mode 100644
index 0000000..6914d5f
--- /dev/null
+++ b/2013/go4python/genex.go
@@ -0,0 +1,35 @@
+package main
+
+import "fmt"
+
+// prime returns true if n is a prime number.
+func prime(n int) bool {
+ for i := 2; i < n; i++ {
+ if n%i == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// primes returns a channel of ints on which it writes the first n prime
+// numbers before closing it.
+func primes(n int) chan int {
+ c := make(chan int)
+ go func() {
+ for i := 1; n > 0; i++ {
+ if prime(i) {
+ c <- i
+ n--
+ }
+ }
+ close(c)
+ }()
+ return c
+}
+
+func main() {
+ for p := range primes(10) {
+ fmt.Println(p)
+ }
+}
diff --git a/2013/go4python/genex2.go b/2013/go4python/genex2.go
new file mode 100644
index 0000000..50daa06
--- /dev/null
+++ b/2013/go4python/genex2.go
@@ -0,0 +1,48 @@
+package main
+
+import "fmt"
+
+// prime returns true if n is a prime number.
+func prime(n int) bool {
+ for i := 2; i < n; i++ {
+ if n%i == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// fib returns a channel on which the first n Fibonacci numbers are written.
+func fib(n int) chan int {
+ c := make(chan int)
+ go func() {
+ a, b := 0, 1
+ for i := 0; i < n; i++ {
+ a, b = b, a+b
+ c <- a
+ }
+ close(c)
+ }()
+ return c
+}
+
+// filterPrimes returns a channel of ints on which it writes all the prime
+// numbers read from cin, and closes the returned channel when cin is closed.
+func filterPrimes(cin chan int) chan int {
+ cout := make(chan int)
+ go func() {
+ for v := range cin {
+ if prime(v) {
+ cout <- v
+ }
+ }
+ close(cout)
+ }()
+ return cout
+}
+
+func main() {
+ for p := range filterPrimes(fib(20)) {
+ fmt.Println(p)
+ }
+}
diff --git a/2013/go4python/img/duck.jpg b/2013/go4python/img/duck.jpg
new file mode 100644
index 0000000..344b31e
--- /dev/null
+++ b/2013/go4python/img/duck.jpg
Binary files differ
diff --git a/2013/go4python/img/fib-go.png b/2013/go4python/img/fib-go.png
new file mode 100644
index 0000000..6917140
--- /dev/null
+++ b/2013/go4python/img/fib-go.png
Binary files differ
diff --git a/2013/go4python/img/fib-py.png b/2013/go4python/img/fib-py.png
new file mode 100644
index 0000000..96bad1a
--- /dev/null
+++ b/2013/go4python/img/fib-py.png
Binary files differ
diff --git a/2013/go4python/img/funnelin.jpg b/2013/go4python/img/funnelin.jpg
new file mode 100644
index 0000000..2bf6873
--- /dev/null
+++ b/2013/go4python/img/funnelin.jpg
Binary files differ
diff --git a/2013/go4python/img/gopher.jpg b/2013/go4python/img/gopher.jpg
new file mode 100644
index 0000000..0a64306
--- /dev/null
+++ b/2013/go4python/img/gopher.jpg
Binary files differ
diff --git a/2013/go4python/img/monkey.jpg b/2013/go4python/img/monkey.jpg
new file mode 100644
index 0000000..f4b5569
--- /dev/null
+++ b/2013/go4python/img/monkey.jpg
Binary files differ
diff --git a/2013/go4python/monkey.go b/2013/go4python/monkey.go
new file mode 100644
index 0000000..161180a
--- /dev/null
+++ b/2013/go4python/monkey.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+)
+
+var authURL = ""
+
+var auth = func(user string) bool {
+ res, err := http.Get(authURL + "/" + user)
+ return err == nil && res.StatusCode == http.StatusOK
+}
+
+func sayHi(user string) {
+ if !auth(user) {
+ fmt.Printf("unknown user %v\n", user)
+ return
+ }
+ fmt.Printf("Hi, %v\n", user)
+}
+
+func TestSayHi() {
+ auth = func(string) bool { return true }
+ sayHi("John")
+
+ auth = func(string) bool { return false }
+ sayHi("John")
+}
+
+func init() {
+ auth = func(string) bool { return true }
+}
+
+func TestAnythingElse() {
+ // auth has been already set to the fake version
+}
+
+func main() {
+ TestSayHi()
+}
diff --git a/2013/go4python/monkey.py b/2013/go4python/monkey.py
new file mode 100644
index 0000000..6f33a7c
--- /dev/null
+++ b/2013/go4python/monkey.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import urllib
+
+auth_url = 'http://google.com'
+
+def auth(usr):
+ try:
+ r = urllib.urlopen(auth_url + '/' + usr)
+ return r.getcode() == 200
+ except:
+ return False
+
+def say_hi(usr):
+ if auth(usr):
+ print 'Hi, %s' % usr
+ else:
+ print 'unknown user %s' % usr
+
+def sayhitest():
+ # Test authenticated user
+ globals()['auth'] = lambda x: True
+ say_hi('John')
+
+ # Test unauthenticated user
+ globals()['auth'] = lambda x: False
+ say_hi('John')
+#done OMIT
+
+sayhitest()
diff --git a/2013/go4python/typesandmethods.go b/2013/go4python/typesandmethods.go
new file mode 100644
index 0000000..53bc1e2
--- /dev/null
+++ b/2013/go4python/typesandmethods.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+type Name struct {
+ First string
+ Middle string
+ Last string
+}
+
+func (n Name) String() string {
+ return fmt.Sprintf("%s %c. %s", n.First, n.Middle[0], strings.ToUpper(n.Last))
+}
+
+type SimpleName string
+
+func (s SimpleName) String() string { return string(s) }
+
+func main() {
+ n := Name{"William", "Mike", "Smith"}
+ fmt.Printf("%s", n.String())
+ return
+ // second OMIT
+ n = Name{"William", "Mike", "Smith"}
+ fmt.Println(n)
+}