Go for Pythonistas
https://talks.golang.org/2013/go4python.slide#1
Dynamic typing - nice because it's concise, like Python.
a = "hello"
b = 1
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 mypy and Cython.
name = 'pythonista'
if random.random() > 0.5:
print 'hey '+name+', you win!'
else:
print 'sorry '+nane+', you lose'
I don't want start a flame war here but …
100% code coverage is a symptom
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:
www.rafekettler.com/magicmethods.html
A lot has been said about Python's infamous Global Interpreter Lock.
You should watch Mindblowing Python GIL, by David Beazley.
The Zen of Python. (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.
Have you ever heard of Fibonacci?
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)
Something familiar?
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))
}
}
Python generators are awesome.
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
yield a
Mechanically complex.
f = fib(10)
try:
while True:
print f.next()
except StopIteration:
print 'done'
But very easy to use.
for x in fib(10):
print x
print 'done'
Note the generator executes concurrently. Hmm… I like concurrency.
Based on goroutines and channels.
Goroutines: very light processing actors (the gophers).
Channels: typed, synchronized, thread-safe pipes (the arrows).
Uses a channel send instead of yield
.
func fib(c chan int, n int) {
a, b := 0, 1
for i := 0; i < n; i++ {
a, b = b, a+b
c <- a
}
close(c)
}
func main() {
c := make(chan int)
go fib(c, 10)
for x := range c {
fmt.Println(x)
}
}
A more generator-like style:
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
}
func main() {
for x := range fib(10) {
fmt.Println(x)
}
}
Write a function that returns a channel and sends the first n prime numbers on
it.
Given the function prime
:
// 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
}
Use the Go playground:
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)
}
}
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:
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)
}
}
Goroutines and channels aren't just for generators. They can be used to model
all kinds of concurrent systems.
To learn more:
Concurrency patterns, by Rob Pike
Advanced Concurrency Patterns, by Sameer Ajmani
A type declaration.
type Name struct {
First string
Middle string
Last string
}
A method declaration.
func (n Name) String() string {
return fmt.Sprintf("%s %c. %s", n.First, n.Middle[0], strings.ToUpper(n.Last))
}
Constructing a Name
and using it.
n := Name{"William", "Mike", "Smith"}
fmt.Printf("%s", n.String())
There's more to types than structs.
type SimpleName string
You can define methods on any type.
func (s SimpleName) String() string { return string(s) }
Or almost any type.
func (s string) NoWay()
You can only define methods on types within the same package.
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
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
.
n = Name{"William", "Mike", "Smith"}
fmt.Println(n)
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.
A convenient way to wrap a function.
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
A function can be decorated using @
.
class myHandler(BaseHTTPRequestHandler):
@auth_required
def do_GET(self):
self.wfile.write('Hello, %s!' % self.user)
If we run it.
try:
server = HTTPServer(('', PORT_NUMBER), myHandler)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
This is unauthorized:
This is authorized:
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.
var hiHandler = authRequired(
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi, %v", r.FormValue("user"))
},
)
A wrapper function.
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)
}
}
func main() {
http.HandleFunc("/hi", hiHandler)
http.ListenAndServe(":8080", nil)
}
This is unauthorized:
This is authorized:
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.
type errorHandler func(http.ResponseWriter, *http.Request) error
Write a decorator that given a errorHandler
returns a http.HandlerFunc
.
If an error occurs it logs it and returns an http error page.
Given the function handler
.
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
}
We want to use it as follows.
http.HandleFunc("/hi", handleError(handler))
Implement handleError
using the playground.
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)
}
}
}
// 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)
"A monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code." - Wikipedia
Also known as "duck punching" … poor duck.
Often used for testing purposes.
For example, say we want to test this function:
def say_hi(usr):
if auth(usr):
print 'Hi, %s' % usr
else:
print 'unknown user %s' % usr
Which depends on a function that makes an HTTP request:
def auth(usr):
try:
r = urllib.urlopen(auth_url + '/' + usr)
return r.getcode() == 200
except:
return False
We can test say_hi
without making HTTP requests by stubbing out auth
:
def sayhitest():
# Test authenticated user
globals()['auth'] = lambda x: True
say_hi('John')
# Test unauthenticated user
globals()\['auth'\] = lambda x: False
say\_hi('John')
The same effect can be achieved in Go.
func sayHi(user string) {
if !auth(user) {
fmt.Printf("unknown user %v\n", user)
return
}
fmt.Printf("Hi, %v\n", user)
}
Which depends on
var auth = func(user string) bool {
res, err := http.Get(authURL + "/" + user)
return err == nil && res.StatusCode == http.StatusOK
}
Our test code can change the value of auth easily.
func TestSayHi() {
auth = func(string) bool { return true }
sayHi("John")
auth = func(string) bool { return false }
sayHi("John")
}
Go is a bit like Python
but a bit different too
Disclaimer :
Next steps
Learn Go from your browser
The community: golang-nuts
groups.google.com/d/forum/golang-nuts
Go and the Zen of Python
https://talks.golang.org/2012/zen.slide#1
手机扫一扫
移动阅读更方便
你可能感兴趣的文章