Go进阶笔记-关于error

编程艺术 peanut 发表了文章 0 个评论 21 次浏览 4 天前 来自相关话题

很多人对于Go的error比较吐槽,说代码中总是会有大量的如下代码: if err != nil { ... } 其实很多时候是使用的姿 ...查看全部

很多人对于Go的error比较吐槽,说代码中总是会有大量的如下代码:


if err != nil {
...
}

其实很多时候是使用的姿势不对,或者说,对于error的用法没有完全理解,这里整理一下关于Go中的error 。


关于源码中的error

先看一下go源码中go/src/builtin/builtin.go对于error的定义:


// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}

我们使用的时候经常会通过errors.New() 来返回一个error对象,这里可以看一下我们调用errors.New()的这段源码文件go/src/errors/errors.go,可以看到errorString实现了error解接口,而errors.New()其实返回的是一个 &errorString{text} 即errorString对象的指针。


package errors

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
s string
}

func (e *errorString) Error() string {
return e.s
}

如果之前看过一些优秀源码或者go源码的,会发现代码中通常会定义很多自定义的error,并且都是包级别的变量,即变量名首字母大写:


// https://golang.org/pkg/bufio


var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)

注意:自己之后在代码中关于这种自定义错误的定义,也要参照这种格式规范定义。
“当前的包名:错误信息”


package main

import (
"errors"
"fmt"
)

type errorString string

// 实现 error 接口
func (e errorString) Error() string {
return string(e)
}

func New(text string) error {
return errorString(text)
}

var errNamedType = New("EOF")
var ErrStructType = errors.New("EOF")

func main() {
// 这里其实就是两个结构体值的比较
if errNamedType == New("EOF") {
fmt.Println("Named Type Error") // 这行打印会输出
}
// 标准库中errors.New() 返回的是一个地址,每次调用都会返回一个新的内存地址
// 标准库这样设计也是为了避免碰巧如果两个结构体值相同了,而引发一些不期望的问题
if ErrStructType == errors.New("EOF") {
fmt.Println("Struct Type Error") // 这行打印不会输出
}
}

关于结构体值的比较:


如果两个结构体值的类型均为可比较类型,则它们仅在它们的类型相同或者它们的底层类型相同(要考虑字段标签)并且其中至少有一个结构体值的类型为非定义类型时才可以互相比较。


如果两个结构体值可以相互比较,则它们的比较结果等同于逐个比较它们的相应字段。



注意:关于Go中函数支持多参数返回,如果函数有error的通常把返回值的最后一个参数作为error



如果一个函数返回(value, error)这个时候必须先判定error
Go中的panic 意味着程序挂了不能继续运行了,不能假设调用者来解决panic。

对于刚学习go的时候经常用如下代码开启一个goroutine执行任务:


go func() {
...
}

这种情况也叫野生goroutine,并且这个时候recover是不能解决的。


可以定义一个包,通过调用该包中的Go() 方法来开goroutine,来避免野生goroutine。


package sync

func Go(x func()) {

if err := recover(); err != nil {
....
}
go x()
}

关于代码的panic 通常在代码中是很少使用的,只有在极少情况下,我们需要panic,如我们项目的初始化地方连接数据库连接不上,并且这个时候,数据库是我们程序的强依赖,那么这个时候是可以panic。


下面通过一个例子来演示error的使用姿势:


package main

import (
"errors"
"fmt"
)

// 判断正负数
func Positivie(n int) (bool, error) {
if n == 0 {
return false, errors.New("undefined")
}
return true, nil
}

func Check(n int) {
pos, err := Positivie(n)
if err != nil {
fmt.Println(n, err)
return
}
if pos {
fmt.Println(n, "is positive")
} else {
fmt.Println(n, "is negative")
}
}

func main() {
Check(1)
Check(0)
Check(-1)
}

上面是一种非常正确的姿势,我们通过返回(value, error) 这种方式来解决,也是非常go 的一种写法,只有err!=nil 的时候我们的value才有意义


那么在实际中可能有很多各种姿势来解决上述的问题,如下:


package main

import "fmt"

func Positive(n int) *bool {
if n == 0 {
return nil
}
r := n > -1
return &r
}

func Check(n int) {
pos := Positive(n)
if pos == nil {
fmt.Println(n, "is neither")
return
}
if *pos {
fmt.Println(n, "is positive")
} else {
fmt.Println(n, "is negative")
}
}

func main() {
Check(1)
Check(0)
Check(-1)
}

另外一种姿势:


package main

import "fmt"

func Positive(n int) bool {
if n == 0 {
panic("undefined")
}
return n > -1
}

func Check(n int) {
defer func() {
if recover() != nil {
fmt.Println("is neither")
}
}()

if Positive(n) {
fmt.Println(n, "is positive")
} else {
fmt.Println(n, "is negative")
}
}

func main() {
Check(1)
Check(0)
Check(-1)
}

上面这两种姿势虽然也可以实现这个功能,但是非常的不好,也不推荐使用。在代码中尽可能还是使用(value, error) 这种返回值来解决error的情况。


对于真正意外的情况,那些不可恢复的程序错误,例如索引越界,不可恢复的环境问题,栈溢出等才会使用panic,对于其他的情况我们应该还是期望使用error来进行判定。


error 处理套路

Sentinel Error 预定义error

通常我们把代码包中如下的这种error叫预定义error.


// https://golang.org/pkg/bufio


var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)

这种姿势的缺点:


  • 对于这种错误,在实际中的使用中我们通常会使用 if err == ErrSomething {....} 这种姿势来进行判断。但是也不得不说,这种姿势是最不灵活的错误处理策略,并且不能对于错误提供有用的上下文。


  • Sentinel errors 成为API的公共部分。如果你的公共函数或方法返回一个特定值的错误,那么该错误就必须是公共的,当然要有文档记录,这最终会增加API的表面积。


  • Sentinel errors 在两个包之间创建了依赖。对于使用者不得不导入这些错误,这样就在两个包之间建立了依赖关系,当项目中有许多类似的导出错误值时,存在耦合,项目中的其他包必须导入这些错误值才能检查特定的错误条件。


Error types

Error type 是实现了error接口的自定义类型,例如MyError类型记录了文件和行号以展示发生了什么


type MyError struct {
Msg string
File string
Line int
}

func (e *MyError) Error() string {
return fmt.Sprintf("%s:%d:%s", e.File,e.Line, e.Msg)
}

func test() error {
return &MyError("something happened", "server.go", 11)
}

func main() {
err := test()
switch err := err.(type){
case nil:
// ....
case *MyError:
fmt.Println("error occurred on line:", err.Line)
default:
// ....
}
}

这种方式其实在标准库中也有使用如os.PathError


// https://golang.org/pkg/os/#PathError

type PathError struct {
Op string
Path string
Err error
}

调用者要使用类型断言和类型switch,就要让自定义的error变成public,这种模型会导致和调用者产生强耦合,从而导致API变得脆弱。


Opaque errors

这种方式也称为不透明处理,这也是相对来说比较优雅的处理方式,如下


func fn() error {

x, err := bar.Foo()
if err != nil {
return err
}
// use x
}

这种不透明的实现方式,一种比较好的用法,这里以net库的代码来看:


// https://golang.org/pkg/net/#Error

type Error interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}

这里是定义了一个Error接口,而让其他需要用到error的来实现这个接口,如net中的下面这个错误


// https://golang.org/pkg/net/#DNSConfigError

type DNSConfigError
func (e *DNSConfigError) Error() string
func (e *DNSConfigError) Temporary() bool
func (e *DNSConfigError) Timeout() bool
func (e *DNSConfigError) Unwrap() error

按照这个方式实现我们使用net时的异常处理可能就是如下情况:


if neerr, ok := err.(net.err); ok && nerr.Temporary() {
time.Sleep(time.Second * 10)
continue
}
if err != nil {
log.Fatal(err)
}

其实这样还是不够优雅,好的方式是我们卡一定义temporary的接口,然后取实现这个接口,这样整体代码就看着非常简洁清楚,对外我们就只需要暴露IsTemporary方法即可,而不用外部再进行断言。


Type temporary interface {
Temporary() bool
}

func IsTemporary(err error) bool {
te, ok := err.(temporary)
return ok && te.Temporary()
}

以上这几种姿势,其实各有各的用处,不同的场景,选择可能也不同,需要根据实际场景实际分析。


一个error 技巧使用例子

先看一段代码,相信这段代码如果很多人实现的时候也都是这个样子:


type Header struct {
Key, Value string
}

type Status struct {
Code int
Reason string
}

func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {

_, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
if err != nil {
return err
}

for _, h := range headers {
_, err := fmt.Fprintf(w, "%s:%s\r\n", h.Key, h.Value)
if err != nil {
return err
}
}

if _, err := fmt.Fprint(w, "\r\n"); err != nil {
return err
}

_, err = io.Copy(w, body)
return err
}

看这段代码时候估计很多就开始吐嘈go的error的处理,感觉代码中会存在很多err的判断处理,其实这里是可以写的更优雅一点的,上面的姿势不对,来换个姿势:


type errWriter struct {
io.Writer
err error
}

func(e *errWriter) Write(buf []byte) (int, error) {
if e.err != nil {
return 0, e.err
}

var n int
n, e.err = e.Writer.Write(buf)
return n,nil
}

func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
ew :=&errWriter{Writer:w}
fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)

for _, h := range headers {
fmt.Fprintf(ew, "%s:%s\r\n", h.Key, h.Value)
}

fmt.Fprint(w, "\r\n")

io.Copy(w, body)
return ew.err
}

对比之下这种代码看起来是不是就非常简洁,所有很多时候可能是自己写代码的姿势不对,而不是go的error设计的不好。


Wrap errors

就像下面这段代码一样,这样的使用方式,我自己在工程代码中也经常看到,这样就会导致生成的错误没有file:line信息,没有导致错误的调用堆栈信息,如果出现异常就非常不方便排查到底是哪里导致的问题,其次因为这里通过fmt.Errorf对错误进行了包装,也就破坏了原始错误。


func AuthenticateReuest(r *Request) error {
err := authenticate(r.User)
if err != nil {
return fmt.Errorf("authenticate failed:%v", err)
}
return nil
}

关于error的处理中还有一个非常重要的地方就是是否是每次出现err!=nil的时候,我们都需要打印日志? 如果这样做了,你会发现到处在打印日志,还有很多地方可能打印的是相同的日志。


func WriteAll(w io.Writer, buf[]byte) error {
_, err := w.Write(buf)
if err != nil {
log.Println("unalbe to write:",err) //这里记录了日志
return err //将日志进行上抛给调用者
}
return nil
}

func WriteConfig(w io.Writer, conf *Config) error {
buf, err := json.Marshal(conf)
if err != nil {
log.Printf("cound not marshal config:%v", err)
return err
}
if err := WriteAll(w, buf); err != nil {
log.Println("cound not write config:%v",err)
return err
}
return nil
}

在上面这个例子中, 这个错误逐层返回给调用者,如果处理不好,可能就像上面这个例子,每次都打印日志,一直到程序的顶部
所以:error应该只被处理一次。
Go中错误的处理契约规定:在出现错误的情况下,不能对其他返回值的内容做任何假设,如下代码中,由于json序列化失败,buf的内容是未知的,这个时候把损坏的buf传给后续处理逻辑,这样就会导致一些未知的错误发生。

func WriteConfig(w io.Writer, conf *Config) error {
buf, err := json.Marshal(conf)
if err != nil {
log.Printf("cound not marshal config:%v", err)
// 忘记return
}
if err := WriteAll(w, buf); err != nil {
log.Println("cound not write config:%v",err)
return err
}
return nil
}

关于错误日志处理的规则:


  • 错误要被日志记录
  • 应用程序处理错误,保证100%的完整性
  • 之后不再报告当前错误

github.com/pkg/errors 这个error处理包非常受欢迎,看一下这个包对错误的处理例子:


package main

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/pkg/errors"
)

func ReadFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, errors.Wrap(err, "open failed")
}
defer f.Close()
buf, err := ioutil.ReadAll(f)
if err != nil {
return nil, errors.Wrap(err, "read failed")
}
return buf, nil
}

func ReadConfig() ([]byte, error) {
home := os.Getenv("HOME")
config, err := ReadFile(filepath.Join(home, ".settings.xml"))
return config, errors.WithMessage(err, "cound not read config")
}

func main() {
_, err := ReadConfig()
if err != nil {
fmt.Printf("original err:%T %v\n", errors.Cause(err), errors.Cause(err))
fmt.Printf("stack trace:\n %+v\n",err) // %+v 可以在打印的时候打印完整的堆栈信息
os.Exit(1)
}
}

执行结果如下:


original err:*os.PathError open /Users/zhaofan/.settings.xml: no such file or directory
stack trace:
open /Users/zhaofan/.settings.xml: no such file or directory
open failed
main.ReadFile
/Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:15
main.ReadConfig
/Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:27
main.main
/Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:32
runtime.main
/Users/zhaofan/app/go/src/runtime/proc.go:204
runtime.goexit
/Users/zhaofan/app/go/src/runtime/asm_amd64.s:1374
cound not read config
exit status 1

从代码上也非常简洁,处理的非常优雅,最终不管是错误信息还是堆栈信息,还可以添加自定义的上下文,同时也完全满足上面提出的关于错误日志处理的规则。
关于代码中的Wrap源码如下:


// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}

可以看到我们每次调用errors.Wrap方法的时候都是把我们的错误信息err存入到withMessage结构体的cause字段,同时又把包装的withMessage 作为err存到withStack结构体中,同时withStack包含了调用堆栈的信息


type withMessage struct {
cause error
msg string
}

关于github.com/pkg/errors使用姿势

  • 你自己的应用程序中,使用errors.New或者errors.Errorf返回错误
  • 如果调用其他包内的函数或者你当前项目里的其他函数,通常简单的直接返回,即直接return err
  • 如果你使用第三方库如github库,公司的基础库,或者go的基础库,这个时候应该使用errors.Wrap或者errors.Wrapf保存堆栈信息,同时添加自定义的上下文信息
  • 直接返回错误,而不是每个错误产生的地方打日志
  • 在程序的顶部或者工作的goroutine顶部(请求入口)使用%+v把堆栈详情记录
  • 使用errors.Cause 获取root error即根因,在进行和sentinel error进行等值判定
  • 一旦错误被处理,包括你打印日志,或者降级处理等,这个时候你就不应该再向上抛出err,而应该return nil.

go1.13 中的errors

go 1.13 为errors和fmt标准库引入了新的特性,以简化处理包含其他错误的错误。其中最重要的就是:包含一个错误的error可以实现返回底层错误的Unwrap 方法。如果e1.Unwrap() 返回e2, 那么e1就包装了e2,就可以展开e1以获取e2


在Go的1.13 中fmt.Errorf支持新的%w ,这样就在错误信息中带入原始的信息,这样既保证了人阅读的方便,也方便了机器处理,如:


if err != nil {
return fmt.Errorf("access denied %w", ErrrPermission)
}

把之前的例子进行调整如下:


package main

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"errors"
)


func ReadFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("open failed: %w", err)
}

defer f.Close()
buf, err := ioutil.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("read failed: %w", err)
}
return buf, nil
}


func ReadConfig() ([]byte, error) {
home := os.Getenv("HOME")
config, err := ReadFile(filepath.Join(home, ".settings.xml"))
return config, fmt.Errorf("cound not read config: %w", err)
}

func main() {
_, err := ReadConfig()
if err != nil {
// errors.Is会一层一层的展开,找最内层的err
fmt.Println(errors.Is(err, os.ErrNotExist))
os.Exit(1)
}
}

但是1.13的errors有个非常大的问题就是不支持携带堆栈信息,所以最好的办法就是把标准库中的errorsgithub.com/pkg/errors


package main

import (
"errors"
"fmt"

xerrors "github.com/pkg/errors"
)

var errMy = errors.New("My Error")

func test0() error {
return xerrors.Wrapf(errMy, "test0 failed")
}

func test1() error {
return test0()
}

func test2() error {
return test1()
}

func main() {
err := test2()
fmt.Printf("main: %+v\n", err)
fmt.Println(errors.Is(err, errMy))
}

其实原则就是我们底层的错误还是通过 github.com/pkg/errorsWrapf 进行包装。并且这个时候也完全兼容标准库中的errors,可以使用errors.Iserrors.As方法做判断处理。

A股毛利率超80%的18强企业

互联网资讯 OS小编 发表了文章 0 个评论 4 次浏览 5 小时前 来自相关话题

一家公司的赚钱的难易程度,显然是能否公司持续长青的重要因素,今天我们将就毛利率这个指标来做一次筛选,希望对粉丝朋友们有一些借鉴意义。 本次筛选的标准:最近5年平均毛利率超过80%,年营收超过5亿元,负债率低于50%,股价超过40 ...查看全部

一家公司的赚钱的难易程度,显然是能否公司持续长青的重要因素,今天我们将就毛利率这个指标来做一次筛选,希望对粉丝朋友们有一些借鉴意义。


本次筛选的标准:最近5年平均毛利率超过80%,年营收超过5亿元,负债率低于50%,股价超过40元(代表市场认可度),较好的市场品牌知名度和一定的分红比率公司,按总市值排名。


1、贵州茅台(600519):近5年毛利率在89.80%-92.23%,A股第一白酒龙头股,总市值2.15万亿。


2、五粮液(000858):近5年毛利率在69.20%-74.46%,A股第二白酒龙头股,总市值9195亿。


3、恒瑞医药(600276):近5年毛利率在85.28%-87.49%,A股第一医药龙头股,总市值4941亿。


4、金山办公(688111):近5年毛利率在85.58%-92.84%,A股国产替代办公软件第一股,总市值1516亿。


5、长春高新(000661):近5年毛利率在78.32%-85.19%,A股生长激素第一龙头股,总市值1532亿。


6、恒生电子(600570):近5年毛利率在92.69%-97.11%,A股第一金融IT龙头股,总市值1010亿。


7、广联达(002410):近5年毛利率在89.3%-95.97%,A股建筑工程信息化软件龙头,总市值965亿。


8、同花顺(300033):近5年毛利率在88.26%-91.80%,A股互联网金融信息服务第一龙头股,总市值863亿。


9、甘李药业(603087):近5年毛利率在90.61%-92.13%,A股糖尿病治疗人工合成胰岛素第一龙头股,总市值748亿。


10、健帆生物(300529):近5年毛利率在83.93%-86.21%,A股血液净化器械第一龙头股,总市值611亿。


11、贝达药业(300896):近5年毛利率在93.23%-96.95%,A股抗癌创新药龙头股之一,总市值470亿。


12、吉比特(603444):近5年毛利率在90.54%-96.44%,A股第一网络游戏龙头股,总市值422亿。


13、大博医疗(002901):近5年毛利率在80.4%-85.61%,A股骨科创伤类植入耗材第一龙头股,总市值376亿。


14、康弘药业(002773):近5年毛利率在89.13%-92.17%,A股眼药赛道第一龙头股,总市值376亿。


15、我武生物(300357):近5年毛利率在94.30%-96.43%,A股过敏性鼻炎第一龙头股,总市值298亿。


16、泛微网络(603039):近5年毛利率在95.41%-96.43%,A股在线办公OA第一龙头股,总市值203亿。


17、艾德生物(300685):近5年毛利率在90.37%-93.15%,A股肿瘤伴随诊断第一龙头股,总市值190亿。


18、万兴科技(300624):近5年毛利率在93.88%-97.99%,A股消费类软件第一股,总市值93.3亿。


18家公司,有9家医疗健康领域的公司,可见行业之优秀,也坚定了我们深耕这个行业持续为朋友们输出好观点的信心!



免责声明 : 不荐股、不诊股, 文章内容仅供参考,不构成任何投资建议!



分享阅读原文: https://henduan.com/c37lA

三季度高瓴资本重仓的20只A股标

互联网资讯 OS小编 发表了文章 0 个评论 2 次浏览 5 小时前 来自相关话题

利用周末时间,把高瓴资本2020年三季度A股持仓重新梳理了一遍。有遗漏的地方还请大家留言指出。 ...查看全部

利用周末时间,把高瓴资本2020年三季度A股持仓重新梳理了一遍。有遗漏的地方还请大家留言指出。

1. 生物医药行业

1、凯莱英


全球行业领先的CDMO解决方案提供商,2020年10月13日高瓴通过定赠买入440.53万股,成本227元,持股比例1.82%。锁定期6个月,2021/4/16解禁。


2020年第三季度前十大流通股东显示,公司在第三季度买入140万股,成本估计在220-240元之间。


2、泰格医药


国内最好的CRO创新药外包服务公司之一,持有750万股,持股比例1.39%,平均成本41元左右。公司分两次买入2018年6月11日通过大宗交易买入500万股,每股36.66元;2019年第三季度买入250万股,估计成本在50-60元/股之间。


3、药明康德


国内创新药CRO/CDMO龙头,高瓴在公司A股上市前持有2535.67万股,成本66.36元,为公司第六大股东。


4、爱尔眼科


国内眼科医院第一龙头,高瓴持有6337.32万股,成本16.5元左右,为公司第五大股东。


5、金域医学


国内医学独立实验室龙头,持股575万股,是在2019年第四季度进入,估计成本在50元左右,第6大流通股东,2020第三季度退出前十大股东。


6、凯利泰


脊柱骨科医疗器械龙头公司,2020第一季度前十大流通股,显示高瓴持有1762.37万股,持股比例2.46%,估计成本在14-16元之间。


11月20日晚凯利泰公告,5月份公司与高瓴资本和淡马锡签署的引进战投定增协议作废,高瓴和淡马锡不参与定增也不再参与战投,当时的定增价格定在18.73元/股。


高瓴终止参与定增的原因,可能与“高值耗材企业的集采政策”有关,由于冠脉支架的集采价格下跌超预期,而骨科耗材有可能是下一个被纳入集采的品类。所以,凯利泰的投资逻辑发生了变化,高值耗材企业估值可能会重构。


7、健帆生物


国内血液灌流器龙头,2020.05.08通过大宗交易买入1320.57万股,价格56元。


8、甘李药业


国内胰岛素生产龙头,高瓴资本在2014 年 11 月 12 日,通过一级市场,出资 54281.71万元受让1684.44万股,持股数占总股本比例 5.03%,单价是32.23元/股。此次转让价格,将公司整体估值定为100 亿元,对应 2014 年度净利润市盈率约为32.79 倍。


目前持有2540.53万股,占总股本4.52%,成本32元。上市后,锁定期12个月,2021/6/29解禁。


9、恒瑞医药


A股医药第一牛股,抗肿瘤创新药龙头股,高瓴2015年第四季度持有1662万股,进入前十大流通股股东,2017年第二季度已退出十大股东。


2. 消费行业

1、水井坊


次高端白酒,高瓴2019年第一季开始度建仓,持有452.49万股,估计成本在30-40元。


2、五粮液


中国高端白酒龙头,高瓴2020年第二季度持有1087.32万股,第三季度退出前十大股东。


3、良品铺子


高端零食第一股,国内零食风口龙头,高瓴通过一级市场持股4680.02万股,持股比例11.67%。上市后,锁定期12个月,2021/2/24解禁。


3. 制造行业

1、格力电器


国内空调家电领域龙头,高瓴资本在2016年1月买入4339.64万股,估计成本在15-17.00元之间,2019年12月3日,通过大宗交易400亿持股9亿多,持股比例15%,价格46.17元,为公司第二大股东。


2、美的集团


中国家电新龙头,高瓴2015年第四季度,进入公司前十大流通股,2019年第三季度退出十大股东。


3、公牛集团


转换器和墙壁开关插座领域龙头,高瓴通过一级市场8亿元购入1206万股,成本66.35元左右。上市后,锁定期12个月,2021/2/8解禁。


4、上海机电


国内机电装备制造龙头,高瓴2020年第三季度新进,持有2656.2万股,持股比例3.29%,估计成本在16-18元。


4. 新能源汽车行业

1、宁德时代


新能源电池动力领域龙头,2020/7/18参加定增买入5279.5万股,持股比例2.27%,第八大股东,定增价格161元。锁定期6个月,2021/2/4解禁。


2、恩捷股份


国内最大的湿法锂电池隔离膜生产企业,高瓴2020/9/3参加定增,15亿购入2083万股,成本72元。锁定期6个月,2021/3/4解禁。


5. 云计算

1、广联达


国内建筑信息行业龙头,2020/617参加定增,15亿认购2971.47万股,成本50.48元左右,持股比例2.51%。锁定期6个月,2020/12/22解禁。


6. 水泥

1、海螺水泥


国内水泥行业龙头,高瓴2019年第四季度以15亿买入3732万股,成本40元左右,位居第七大股东。


分享阅读原文: https://henduan.com/w5ABo=

更多话题 >>

热门话题

股票

3 个问题, 1 人关注

Go进阶笔记

2 个问题, 1 人关注

更多用户 >>

热门用户

OS小编

5 个问题, 3 次赞同

空心菜

148 个问题, 106 次赞同

peanut

6 个问题, 3 次赞同

chris

9 个问题, 10 次赞同