Python类自定义

python类关键字 __init__ vs __new__ __init__为初始化方法,__new__为真正的构造函数。 描述符Descriptor __contains__ __slots__ 定制类 type() python作为动态语言,和静态语言最大的不同,即函数和类的定义,不是编译的时候创建的而是动态创建的。我们常见的对类的定义: 1 2 3 class Hello(object): def hello(self, name='world'): print('Hello, %s.' % name) >>> from hello import Hello >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class 'hello.Hello'> type()函数可以查看一类类型或者变量的类型,Hello是一个class, 它的类型是个type,而h是一个instance, 它的类型就是class Hello。 同时有一个概念,就是type()不仅可以返回对象的类型,还可以创建出新的类型。我们可以不用定义class Hello() ...而动态创建出Hello类。 >>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'> 创建一个class对象,type()函数传入3个参数: ...

December 14, 2020 · 3 min · 490 words · Me

BDD: Ginkgo测试框架

Preface BDD和TDD都是test case first的实现,无非是把后者的test改成前者的behavior。在TDD中,关注的核心点是function,即认为程序最基本单元是function,其test case可以认为是unit test,TDD和unit test的区别是TDD强调测试和开发结合而成的工作流: 写test case -> 写代码 -> 通过测试,继续写更多测试,写一次循环。 而BDD比TDD更关注高层的行为,而不是函数级别的行为,也就是在BDD中,不会强调函数的功能正确,这是unit test应该做的事。BDD更关注user story,即用户在特定场景,与软件交互发生的行为,这个behavior指的就是高层模块的行为。 如何区分BDD和TDD,简单理解,TDD是给programmer的,用来验证开发者的最基本模块的功能:在什么输入,应该产生什么输出,保证实现的边界,健全性。而BDD,其test case描述的是更高级的模块行为,脱离了具体的实现,容易用自然语言去描述,也就是BDD是给product manager的,告诉其系统的行为。 BDD in golang ​ 实现的时候,我们需要把Given-When-Then这种story格式组织test case翻译为测试代码,通过一系列的assertion来检查实现是否符合test case的预期,我们完全可以直接通过golang自带的testing模块来实现,不过testing的功能有时候比较简陋,本文记录了用Ginkgo+Gomega来组织test case,让我们的测试语言更加接近自然语言。 二者结合的目的是,ginkgo实现了test case的组织,并加入了其他方便的功能: 初始化,后续处理,异步等等。而gomega设计的目的是与ginkgo一起工作,实现易读的assertion(ginkgo中称为match)功能。 Gomega is ginkgo's preferred matcher library 初始化 ginkgo依托golang原生testing框架,即可以用go test ./.. 执行,也可以通过ginkgo binrary安装go install github.com/onsi/ginkgo,封装了ginkgo测试框架的各种feature。 初始化首先进入待测试的package: cd /path/to/package 执行初始化: ginkgo bootstrap 生成以suite_test.go文件,接下来向suite添加测试specs,生成比如ginkgo_cart package测试文件。 ginkgo generate ginkgo_cart 运行 生成ginkgo_cart_test.go,注意测试文件在ginkgo_cart_testpackage, 需要import package ginkgo_cart,即BDD层级高于unit test, 不应该了解package内部的具体实现,测试package的外部接口即可。编写测试代码,运行go test ./..即可。 Ginkgo Keyword Ginkgo测试代码骨架由一系列keyword关联的闭包组成,常用的有: Describe/Context/When: 测试逻辑块 BeforeEach/AfterEach/JustBeforeEach/JustAfterEach: 初始化测试用例块 It: 单一Spec,测试case keyword的声明均为传入Body参数,比如Describe: ...

December 4, 2020 · 2 min · 395 words · Me

Golang内存管理

设计原则 现在我们来看 Go 中另一重要的关键组件:内存分配器。 Go 的内存分配器基于 Thread-Cache Malloc (tcmalloc) ,tcmalloc 为每个线程实现了一个本地缓存, 区分了小对象(小于 32kb)和大对象分配两种分配类型,其管理的内存单元称为 span。 我们不再介绍更多 tcmalloc 的具体细节,因为 Go 的内存分配器与 tcmalloc 存在一定差异。 这个差异来源于 Go 语言被设计为没有显式的内存分配与释放, 完全依靠编译器与运行时的配合来自动处理,因此也就造就了内存分配器、垃圾回收器两大组件。 我们知道,在计算机领域中,无外乎时间换空间、空间换时间。统一管理内存会提前分配或一次性释放一大块内存, 进而减少与操作系统沟通造成的开销,进而提高程序的运行性能。 支持内存管理另一个优势就是能够更好的支持垃圾回收,这一点我们留到垃圾回收器的章节中进行讨论。 主要结构 Go 的内存分配器主要包含以下几个核心组件: heapArena: 保留整个虚拟地址空间 mheap:分配的堆,在页大小为 8KB 的粒度上进行管理 mspan:是 mheap 上管理的一连串的页 mcentral:收集了给定大小等级的所有 span mcache:为 per-P 的缓存。 其中页是向操作系统申请内存的最小单位,目前设计为 8KB。 每一个结构虽然不都像是调度器 M/P/G 结构那样的大部头,但初次阅读这些结构时想要理清他们之间的关系还是比较麻烦的。 传统意义上的栈被 Go 的运行时霸占,不开放给用户态代码;而传统意义上的堆内存,又被 Go 运行时划分为了两个部分, 一个是 Go 运行时自身所需的堆内存,即堆外内存;另一部分则用于 Go 用户态代码所使用的堆内存,也叫做 Go 堆。 Go 堆负责了用户态对象的存放以及 goroutine 的执行栈。 Arena heapArena Go 堆被视为由多个 arena 组成,每个 arena 在 64 位机器上为 64MB,且起始地址与 arena 的大小对齐, 所有的 arena 覆盖了整个 Go 堆的地址空间。 ...

December 2, 2020 · 6 min · 1078 words · Me

Golang逃逸分析

问题: golang函数传参是不是应该和c一样,尽量不要直接传结构体,而是要传结构体指针? 逃逸分析 逃逸分析指的是,在计算机语言编译器优化原理中,分析指针动态范围的方法,和编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中被分配后,其指针有可能被返回或者被全局引用,这种现象就是指针(或引用)的逃逸(Escape)。 其实在java概念中有一个误解 — new出来的东西都在堆上,栈上存的是它的引用。 这句话在现代JVM上有问题,就是因为逃逸分析机制。简单来说,就是JVM的逃逸分析会在运行时(runtime)检测当前方法栈帧(frame)内new出来的对象的引用,是否被传出当前的栈帧。如果传出,就会发生逃逸,没有传出则不会。对于未发生逃逸的变量,则会直接在栈上分配内存。因为栈上内存由在函数返回时自动回收,而堆上的的内存需要gc去回收,如果程序中有大量逃逸的对象,那么势必会增加gc的压力。 1 2 3 4 5 6 7 8 9 10 public void test(){ List<Integer> a = new ArrayList<>(); a.add(1); // a 未逃逸,在栈上分配 } public List<Integer> test1(){ List<Integer> a = new ArrayList<>(); a.add(1); return a // 发生逃逸,因此分配在堆上 } 区别 不同于JVM运行时的逃逸分析,Golang的逃逸分析是在编译期完成。 golang的逃逸分析只针对指针。一个值引用变量如果没有被取址,那么它永远不可能逃逸。 go version go1.13.4 darwin/amd64 验证某个函数的变量是否发生逃逸的方法: go run -gcflags “-m -l” (-m打印逃逸分析信息,-l禁止内联编译) ...

November 23, 2020 · 4 min · 852 words · Me

Design Pattern: Overview

Design pattern Builder Pattern scenario:build complicated object 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 package msg type Message struct { Header *Header Body *Body } type Header struct { SrcAddr string SrcPort uint64 DestAddr string DestPort uint64 Items map[string]string } type Body struct { Items []string } // Message对象的复杂对象 type builder struct{ once *sync.Once msg *Message } // 返回Builder对象 func Builder() *builder{ return &builder{ once: &sync.Once{}, msg: &Message{Header: &Header{}, Body: &Body{}}, } } func (b *builder) WithSrcAddr(srcAddr string) *builder{ b.msg.Header.SrcAddr = srcAddr return b } //...... func (b *builder) WithHeaderItem(key, value string) *builder{ //map只初始化一次 b.once.Do(func(){ b.msg.Header.Items = make(map[string]string) }) b.msg.Header.Items[key] = value return b } func (b *builder) WithBodyItem(record string) *builder{ b.msg.Body.Items = append(b.msg.Body.Items, record) return b } func (b *builder) Build() *Message{ return b.msg } Test code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package test func TestMessageBuilder(t *testing.T) { // 使用消息建造者进行对象创建 message := msg.Builder(). WithSrcAddr("192.168.0.1"). WithSrcPort(1234). WithDestAddr("192.168.0.2"). WithDestPort(8080). WithHeaderItem("contents", "application/json"). WithBodyItem("record1"). WithBodyItem("record2"). Build() if message.Header.SrcAddr != "192.168.0.1" { t.Errorf("expect src address 192.168.0.1, but actual %s.", message.Header.SrcAddr) } if message.Body.Items[0] != "record1" { t.Errorf("expect body item0 record1, but actual %s.", message.Body.Items[0]) } } Abstract Factory Pattern ...

November 11, 2020 · 6 min · 1098 words · Me