Please enable Javascript to view the contents

Python 与 Go 开发笔记合集

 ·  ☕ 6 分钟

Python 与 Go 开发笔记合集

本文把若干零散的排查与操作记录合并在一起,每条都是当时环境下的实测结果;环境变化后命令可能失效,以官方文档为准。

一起来学 Go –(4)常用函数

1. main 和 init 函数

一个 package 里面可以写多个 init 函数,但必须仅包含一个 main 函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
	"fmt"
)

func init() {
	fmt.Println("init 1")
}

func init() {
	fmt.Println("init 2")
}

func main() {
	fmt.Println("main")
}
// init 1
// init 2
// main

2. defer 函数

Go 不会立即执行 defer 修饰的代码,而是进行标记,在程序退出之前执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"fmt"
)

func main() {
	defer func() {
		fmt.Println("before exit, in defer")
	}()
	fmt.Println("I am in main")
}
// I am in main
// before exit, in defer

3. panic 和 recover 函数

Go 中的异常处理:抛出一个 panic ,然后在 defer 中使用 recover 捕获异常,并将 panic 抛向上一层。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	defer fmt.Println("I am in main")
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
			// do something
		}
	}()

	panic("error message")
}
// error message
// I am in main

4. new 和 make 函数

  • new 函数

原型:

1
func new(Type) *Type

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"fmt"
)

func main() {

	tmp := new(int)
	fmt.Printf("tmp --> %#v \n", tmp)
	fmt.Printf("tmp point to --> %#v \n ", *tmp)
}
// tmp --> (*int)(0x40e020)
// tmp point to --> 0

new 用来分配内存,参数是类型,返回值是一个指向分配零值的指针。

  • make 函数

原型:

1
func make(Type, size IntegerType) Type

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"fmt"
)

func main() {

	var s1 []int
	fmt.Printf("s1 is --> %#v \n", s1)
	s2 := make([]int, 3)
	fmt.Printf("s2 is --> %#v \n", s2)
}
// s1 is --> []int(nil)
// s2 is --> []int{0, 0, 0}

make 用来为 slice,map 或 chan 类型分配内存、并初始化一个对象,参数是类型。但 make 返回类型的引用而不是指针。

区别:new 仅将内存置零,而 make 会对 slice、map、chan 引用的数据进行初始化。

Django 开发中常用命令

创建 Django 工程

1
django-admin startproject your_project_name

创建应用

1
2
3
django-admin.py startapp your_app_name
# 或者
python manage.py startapp your_app_name

python manage.py 和 django-admin 的功能基本一样。不同的是 python manage.py 还设置了 DJANGO_SETTINGS_MODULE 环境变量、将项目的路径加入了 sys.path 中。建议除了创建项目使用 django-admin,其他情况使用 python manage.py。

初始化数据

1
python manage.py migrate

创建缓存表

1
2
python manage.py createcachetable [cache_table_name] 
# 默认表名 django_cache

清除全部数据

1
python manage.py  flush --noinput

在指定端口启动服务

1
python manage.py runserver 0.0.0.0:8000

启动 celery 的后台任务

1
python manage.py celery worker --settings=settings -l info -c 4 --autoreload

启动 celery 的周期任务

1
python manage.py celery beat

安装项目的依赖包

1
pip install -r requirements.txt

关闭全部 Python 进程

1
2
taskkill -f -im python
taskkill -f -im python.exe

关闭全部 uwsgi 进程

1
ps aux|grep uwsgi|awk '{print $2}'|xargs kill -9

使用 uwsgi 启动 Django

1
uwsgi --ini uwsgi.ini

如何在 Django 中任意安全获取 request

在 Django 中,request 包含了一次请求的全部信息。后端处理逻辑经常需要用到 request 中的信息。比如, DRF 框架中想要随时能够获取到 request,或者将一些参数全局传递。Django 第三方 App 中有一些工具可以满足要求,但它们并不是安全可靠的。意思是,如果 Django 启动时,使用了多线程或协程,在获取 request 时,可能会发生错误。这显然是不能接受的。下面是一个安全可靠的实现版本,让你在任意位置都能获取 request 对象。

1. 实现

utils/local.py 文件

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# -*- coding: utf-8 -*-

"""Thread-local/Greenlet-local objects

Thread-local/Greenlet-local objects support the management of
thread-local/greenlet-local data. If you have data that you want
to be local to a thread/greenlet, simply create a
thread-local/greenlet-local object and use its attributes:

  >>> mydata = Local()
  >>> mydata.number = 42
  >>> mydata.number
  42
  >>> hasattr(mydata, 'number')
  True
  >>> hasattr(mydata, 'username')
  False

  Reference :
  from threading import local
"""
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident

__all__ = ["local", "Local"]


class Localbase(object):

    __slots__ = ('__storage__', '__ident_func__')

    def __new__(cls, *args, **kwargs):
        self = object.__new__(cls, *args, **kwargs)
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)
        return self


class Local(Localbase):

    def __iter__(self):
        ident = self.__ident_func__()
        return iter(self.__storage__[ident].items())

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        ident = self.__ident_func__()
        try:
            return self.__storage__[ident][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if name in ('__storage__', '__ident_func__'):
            raise AttributeError(
                "%r object attribute '%s' is read-only"
                % (self.__class__.__name__, name))

        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        if name in ('__storage__', '__ident_func__'):
            raise AttributeError(
                "%r object attribute '%s' is read-only"
                % (self.__class__.__name__, name))

        ident = self.__ident_func__()
        try:
            del self.__storage__[ident][name]
            if len(self.__storage__[ident]) == 0:
                self.__release_local__()
        except KeyError:
            raise AttributeError(name)

local = Local()


if __name__ == '__main__':
    def display(id):
        # import time
        local.id = id
        for i in range(3):
            print get_ident(), local.id, "\n"
            # time.sleep(1)

    def gree(id):
        import gevent
        t = []
        for i in range(10):
            t.append(gevent.spawn(display, "%s-%s" % (id, i)))
        gevent.joinall(t)

    # test one
    # l1 = Local()
    # l2 = Local()
    # l.xxx = 1
    # print l.xxx
    # print l1.xxx
    # print l2.xxx

    # test two
    # import gevent
    # t = []
    # for i in range(10):
    #     g = gevent.spawn(display, i)
    #     t.append(g)
    # gevent.joinall(t)

    # test three
    import threading
    t = []
    for i in range(10):
        t.append(threading.Thread(target=gree, args=(i,)))

    [th.start() for th in t]
    [th.join() for th in t]

utils/request_middlewares.py 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# -*- coding: utf-8 -*-
from django.dispatch import Signal
from django.conf import settings
from utils.local import local


class AccessorSignal(Signal):
    allowed_receiver = 'utils.request_middlewares.RequestProvider'

    def __init__(self, providing_args=None):
        Signal.__init__(self, providing_args)

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
        receiver_name = '.'.join(
            [receiver.__class__.__module__, receiver.__class__.__name__]
        )
        if receiver_name != self.allowed_receiver:
            raise Exception(
                u"%s is not allowed to connect" % receiver_name)
        if not self.receivers:
            Signal.connect(self, receiver, sender, weak, dispatch_uid)


request_accessor = AccessorSignal()


class RequestProvider(object):
    """
    @summary: request事件接收者
    """

    def __init__(self):
        request_accessor.connect(self)

    def process_request(self, request):
        """
            这里可以在 request 上添加自定义的一些数据、处理逻辑
        """
        local.current_request = request
        return None

    def process_view(self, request, view_func, view_args, view_kwargs):
        your_args = view_kwargs.get("your_args", "")
        if not your_args:
            your_args = (request.POST.get('your_args') or
                      request.GET.get('your_args')) or ""
        request.your_args = your_args

    def process_response(self, request, response):
        if hasattr(local, 'current_request'):
            assert request is local.current_request
            del local.current_request

        return response

    def __call__(self, **kwargs):
        if not hasattr(local, 'current_request'):
            raise Exception(
                u"get_request can't be called in a new thread.")
        return local.current_request


def get_request():
    if hasattr(local, 'current_request'):
        return local.current_request
    else:
        raise Exception(u"get_request: current thread hasn't request.")


def get_x_request_id():
    x_request_id = ''
    http_request = get_request()
    if hasattr(http_request, 'META'):
        meta = http_request.META
        x_request_id = (meta.get('HTTP_X_REQUEST_ID', '')
                        if isinstance(meta, dict) else '')
    return x_request_id

2. 使用

1
2
3
4
MIDDLEWARE_CLASSES = (
    ...
    'utils.request_middlewares.RequestProvider',
    ... )
1
2
3
4
5
from utils.request_middlewares import local

def my_function():
    local.current_request
    pass

Go mod 使用问题

1. 升级 Go 版本之后 go.sum 版本不匹配

执行命令 go build 报错。

错误提示:

missing go.sum entry for module providing package golang.org/x/time/rate; to add

解决办法:

在 go build 之前更新 go.sum,执行命令 go mod tidy

2. tls 错误

执行命令 go mod download 报错。

错误提示:

fatal: unable to access 'https://github.com/agiledragon/gomonkey/': GnuTLS recv error (-110): The TLS connection was non-properly terminated.

解决办法:

禁用证书校验执行命令,export GIT_SSL_NO_VERIFY=1 或 git config --global http.sslVerify false。

3. goproxy 不支持 latest

执行命令 go mod download 报错。

错误提示:

github.com/agiledragon/gomonkey: no matching versions for query "latest"

解决办法:

换其他 goproxy 代理。

NPM 常用命令

** 默认执行命名时,针对的是当前目录环境。如果加上 -g 参数,则表示针对的是全局生效。**

  • npm 升级
1
npm install npm -g
  • 查看当前版本
1
npm -v
  • 查看 npm 源配置
1
npm config get registry
  • 修改 npm 源
1
2
npm config set registry http://registry.npm.taobao.org/
# npm config set registry https://registry.npmjs.org/
  • 临时指定 npm 安装源
1
 npm install [package name] --registry=https://registry.npm.taobao.org
  • 设置 proxy
1
npm config set proxy http://proxy.example.com:8080
  • 查看 proxy
1
npm config get proxy
  • 删除proxy
1
npm config delete proxy
  • 查看所有配置
1
npm config list
  • 列出安装的包
1
npm list
  • 安装 packages.json 中描述的包
1
npm install
  • 全局安装
1
npm install [package name] -g
  • 局部安装 dependencies
1
npm install [package name] --save
  • 局部安装 devDependencies
1
npm install [package name] --save-dev
  • 指定安装最新版本
1
npm install [package name]@latest --save
  • 指定安装版本
1
npm install [package name]@1.0.0 --save
  • 指定安装大于等于@1.0.0 小于 2.2.0 版本
1
npm install [package name]@">=2.0.0 <1.0.0" --save
  • 删除全局包
1
npm uninstall  [package name] -g
  • 删除局部包
1
npm uninstall  [package name]
  • 列出本地全部可更新包
1
npm outdated
  • 升级全部包
1
npm update

Django Snippets

1. Admin 自动注册全部 Model 字段

admin.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# -*- coding: utf-8 -*-
import inspect
from django.contrib import admin
from . import models
for name, obj in inspect.getmembers(models):
    try:
        if inspect.isclass(obj):
            admin.site.register(getattr(models, name))
    except Exception as e:
        pass

2. 获取全部 View Name

获取 Project 全部 View Name

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from django.conf import settings
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern

root_urlconf = __import__(settings.ROOT_URLCONF)
all_urlpatterns = root_urlconf.urlpatterns
VIEW_NAMES = [] # maintain a global list

def get_all_view_names(urlpatterns):
    global VIEW_NAMES
    for pattern in urlpatterns:
        if isinstance(pattern, RegexURLResolver):
            get_all_view_names(pattern.url_patterns) # call this function recursively
        elif isinstance(pattern, RegexURLPattern):
            view_name = pattern.callback.func_name # get the view name
            VIEW_NAMES.append(view_name) # add the view to the global list
    return VIEW_NAMES

get_all_view_names(all_urlpatterns)

获取 App 全部 View Name

1
2
3
from my_app.urls import urlpatterns as my_app_urlpatterns

my_app_views = get_all_view_names(my_app_urlpatterns)

3. Admin 中 ManyToMany Field 支持搜索

models.py

1
2
class SomeModel(models.Model):
    users = models.ManyToMany(User)

admin.py

1
2
class SomeModelAdmin(admin.ModelAdmin):
    filter_horizontal = ('users',)

4. Model 中 Choices 使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding: utf-8 -*-
from enum import Enum

class ChoiceEnum(Enum):
    @classmethod
    def choices(cls):
        return tuple((x.name, x.value) for x in cls)

    @classmethod
    def choices_name(cls):
        return tuple(x.name for x in cls)

    @classmethod
    def choices_value(cls):
        return tuple(x.value for x in cls)

    @classmethod
    def get_name(cls, value):
        if value in cls.choices_value():
            return cls.choices_name()[cls.choices_value().index(value)]
        else:
            return ''

    @classmethod
    def get_value(cls, name):
        if name in cls.choices_name():
            return cls.choices_value()[cls.choices_name().index(name)]
        else:
            return ''

class Colors(ChoiceEnum):
    RED = 'red'
    WHITE = 'white'
    BLUE = 'blue'

models.py

1
2
class Desk(models.Model):
    color = models.CharField(max_length=32, choices=Colors.choices(), default=Colors.RED.name)

console 中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
print Colors.choices()
(('BLUE', 'blue'), ('RED', 'red'), ('WHITE', 'white'))

print Colors.RED.value
red

print Colors.get_name(Colors.RED.value)
RED

print Colors.choices_value()
('blue', 'red', 'white')

print Colors.choices_name()
('BLUE', 'RED', 'WHITE')

5. 字符串与时间相互转换

  • 将字符串转换为时间对象
1
2
3
from datetime import datetime
print datetime.strptime('2017-09-02 17:41:20', '%Y-%m-%d %H:%M:%S')
datetime.datetime(2017, 9, 2, 17, 41, 20)
  • 将时间对象转换为字符串
1
2
3
from datetime import datetime
print datetime.now().strftime('%Y-%m-%d %H:%M:%S')
'2017-09-30 09:50:57'

6. 组装装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def compose_decorators(*funs):
    """
    按照顺序组装装饰器
    @A
    @B
    @C
    等价于
    @compose_decorators(A, B,C)
    """
    def deco(f):
        for fun in reversed(funs):
            f = fun(f)
        return f
    return deco

VS Code 无法下载 Go 插件的工具包

VS Code 安装 Go 插件之后,打开 Go 项目时,编辑器会提示安装工具包。

但是 golang.org 无法直接访问,我们需要将包手动下载到 GOPATH 目录,进行安装。

  1. 创建包目录
1
2
cd $GOPATH
mkdir -p src/golang.org/x
  1. 下载包
1
2
3
cd src/golang.org/x
git clone https://github.com/golang/tools.git
git clone https://github.com/golang/lint.git
  1. 安装包
1
2
go get golang.org/x/tools/...
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.27.0

陈少文
作者
陈少文