像python操作attr一样在go里用reflect 操作field (标题与内容弱相关)
Posted xiaotushaoxia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了像python操作attr一样在go里用reflect 操作field (标题与内容弱相关)相关的知识,希望对你有一定的参考价值。
用go快两年了,其实是基本没怎么用过反射。主要是感觉对上层的使用来说没啥用。
之前模仿python的getattr和setattr简单写过GetField和SetField,写完简单测了一下就丢一边了也没大量用(因为没有需求啊)
func SetField(obj any, attr string, value any) error
_, field, err := checkObjAndGetField(obj, attr)
if err != nil
return err
if !field.CanSet()
return fmt.Errorf("cannot set %s field value in obj %T", attr, obj)
val := reflect.ValueOf(value)
if field.Type() != val.Type()
return fmt.Errorf("provided value type %T didn\'t match obj field type %s", value, field.Type())
field.Set(val)
return nil
func GetField(obj any, attr string) (any, error)
_, field, err := checkObjAndGetField(obj, attr)
if err != nil
return nil, err
return field.Interface(), nil
func checkObjAndGetField(obj any, attr string) (ov reflect.Value, field reflect.Value, err error)
if obj == nil
err = fmt.Errorf("field set/get on nil")
return
k := reflect.TypeOf(obj).Kind()
if k != reflect.Struct && k != reflect.Ptr
err = fmt.Errorf("cannot set/get field on a non-struct interface: %T", obj)
return
ov = reflect.Indirect(reflect.ValueOf(obj))
field = ov.FieldByName(attr)
if !field.IsValid()
err = fmt.Errorf("no such field: %s in obj %T", attr, obj)
return
今天用SetField的时候发现出现了我意料外的错误。当然这个错误是应该发生的,也很合理,这个意料外只要是指这种情况没有在我考虑之内
type TaskState int
type Task struct
State TaskState
func TestSetField(t *testing.T)
a := &Task
err := SetField(a, "State", 3)
fmt.Println(err) // provided value type int didn\'t match obj field type TaskState
ok,开始处理这个问题(嗯,这里需要一些reflect的前置知识)
int和TaskState类型(Type)是不一样的,在go里面Type是无穷无尽的,不过反射里面还有个叫Kind的东西,这个Kind是有限的。所以int和TaskState的Kind是一样的,
所以我把
field.Type() != val.Type()改成了
field.Type().Kind() != val.Type().Kind()
结果发现在filed.Set的时候panic了,“panic: reflect.Set: value of type int is not assignable to type TaskState”
哦 所以这里我要做一下类型转换,所以要用到reflet.Value.Convert(reflet.Type)
然后想到如果Kind也不一样的话,Convert肯定会panic的(测试结果也是如此)
所以就有两个(我脑子里冒出来两个)选择了。1.先检查Kind,Kind相同才Convert 2. recover起来写个tryConvert。
嗯,写了个检查Kind的东西
val := reflect.ValueOf(value)
ft := field.Type()
vt := val.Type()
if ft.Kind() != vt.Kind()
return fmt.Errorf("provided value %v(type:%s, kind:%s) didn\'t match obj(%T) field %s (type:%s, kind:%s)",
val, vt, vt.Kind(), obj, attr, ft, ft.Kind())
写完发现不对了,Kind里面有map,但是都是map,map[int]int和map[int]string肯定不能转换。(emm 应该是也有办法拿到kv的type,但是这太麻烦了就不弄了)
好的。(好像)只能用2了。
emm用类型转换的话,就可能会发生float到int这种转换,如果喜欢这样的话,这算是一个意外之喜了,但是对类型严格的话,这样就不好了
所以最后写了两个
func SetField(obj any, attr string, value any) error
_, field, err := checkObjAndGetField(obj, attr)
if err != nil
return err
if !field.CanSet()
return fmt.Errorf("cannot set %s field value in obj %T", attr, obj)
val := reflect.ValueOf(value)
// 先尝试convert, convert失败就报错
// 这样会导致字段是int但是set float也会成功,因为float是可以convert到int的。如果偏爱这个行为的话,用这个挺好的
fuzzError := tryConvertAndSet(field, val)
if fuzzError == nil
return nil
clearErr := checkKind(obj, attr, field, val)
if clearErr != nil
return clearErr
return fuzzError
// 最初的版本 类型别名之间不能Set 这不能接受
//if field.Type() != val.Type()
// return fmt.Errorf("provided value type %T didn\'t match obj field type %s", value, field.Type())
//
//field.Set(val)
//return nil
func SetFieldStrict(obj any, attr string, value any) error
_, field, err := checkObjAndGetField(obj, attr)
if err != nil
return err
if !field.CanSet()
return fmt.Errorf("cannot set %s field value in obj %T", attr, obj)
val := reflect.ValueOf(value)
// 1 先尝试检查Kind, Kind不一样肯定就不能接受Set 这样不会有"隐式"的类型转换
err = checkKind(obj, attr, field, val)
if err != nil
return err
return tryConvertAndSet(field, val)
func checkKind(obj any, attr string, field reflect.Value, val reflect.Value) error
vt := val.Type()
ft := field.Type()
if ft.Kind() != vt.Kind()
return fmt.Errorf("provided value %v(type:%s, kind:%s) didn\'t match obj(%T) field %s (type:%s, kind:%s)",
val, vt, vt.Kind(), obj, attr, ft, ft.Kind())
return nil
func tryConvertAndSet(field reflect.Value, val reflect.Value) error
if converted, er := tryConvert(field.Type(), val); er != nil
return trySet(field, converted)
return fmt.Errorf("convert %s to %s error", val.Type(), field.Type())
// trySet 就是有点怕 其实这个应该是没有必要的
func trySet(field reflect.Value, v reflect.Value) (err error)
defer func()
if p := recover(); p != nil
err = fmt.Errorf("%v", p)
()
field.Set(v)
return
func tryConvert(t reflect.Type, oldV reflect.Value) (v reflect.Value, err error)
defer func()
if p := recover(); p != nil
err = fmt.Errorf("%v", p)
()
v = oldV.Convert(t)
return
好的 这个就叫做hreflect了(hight reflect) 顺便放到github欢迎使用
go get github.com/xiaotushaoxia/hreflect
好晚了。最后一个疑问,有空再看
// reflect.ValueOf(value).Kind()
//
// reflect.TypeOf(value).Kind()
// 这两个东西有区别吗?怎么感觉一样的啊
在linux里用python(2.7)的matplotlib后,调用 show()之后无法显示图像,不会出现图形窗口。
代码如下:
In [1]: import matplotlib.pyplot as plt
In [2]: plt.plot([1,2,3])
Out[2]: [<matplotlib.lines.Line2D at 0xad9a66c>]
In [3]: plt.ylabel('some numbers')
Out[3]: <matplotlib.text.Text at 0xae3792c>
In [4]: plt.show()
之后就像什么事也没发生一样,让我输入下一条命令了。
2个必要的包都已装了(numpy,matplotlib)。研究了好几天了。
2. plt.show()依赖一个图形环境, 如tk, wxpython等, 要先安装一个 参考技术A 请问你这个问题有答案了吗?我也是这个问题,研究了好久天,没有结果。
以上是关于像python操作attr一样在go里用reflect 操作field (标题与内容弱相关)的主要内容,如果未能解决你的问题,请参考以下文章
Blazor 是不是有一些机制像 vue 中的 $attrs 一样工作?