A basic generic pattern is to use an array of objects and store in it different types of subclasses that all share a common base class. In regular Go you can do this with an array of empty interfaces, and then in your for loop you type switch and copy your code into each case - this is ugly and should be done instead with a code generator like Gython.
Gython Input
class A: def foo(self) -> string: return 'xxxx' class B(A): def foo(self) -> string: return 'hello' class C(A): def foo(self) -> string: return 'world' def push( arr:[]int, x:int ): arr.append( x ) def push2( arr:[]*A, x:*A ): arr.append( x ) def my_generic( s:A ): print( s.foo() ) def main(): b1 = B() barr = []B( b1, ) c1 = C() barr.append( c1 ) bb = barr[0] print(bb.foo()) cc = barr[1] print(cc.foo()) print('----testing generic----') for subclass in barr: print('subclass in bar:', subclass) my_generic(subclass)
Gython Output
... type A struct { __object__ } func (self *A) foo() string { return "xxxx" } func __new__A() *A { ob := A{} ob.__class__ = "A" return &ob } func (self *B) A_foo() string { return "xxxx" } type B struct { A } func (self *B) foo() string { return "hello" } func __new__B() *B { ob := B{} ob.__class__ = "B" return &ob } func (self *C) A_foo() string { return "xxxx" } type C struct { A } func (self *C) foo() string { return "world" } func __new__C() *C { ob := C{} ob.__class__ = "C" return &ob } func push(arr *[]int, x int) { __8 := append(*arr,x); *arr = __8; } func push2(arr *[]*A, x *A) { __9 := append(*arr,x); *arr = __9; } func my_generic(__gen__ interface{}) { __type__ := "INVALID" __super__, __ok__ := __gen__.(object) if __ok__ { __type__ = __super__.getclassname(); } else { fmt.Println("Gython RuntimeError - struct must implement the `object` interface"); } switch __type__ { case "C": s,__ok__ := __gen__.(*C) if __ok__ { fmt.Println(s.foo()); } else { switch __gen__.(type) { case *B: s := C( *__gen__.(*B) ) fmt.Println(s.foo()); case *A: fmt.Println("Generics RuntimeError - can not cast base class to a subclass type", s); } } case "B": s,__ok__ := __gen__.(*B) if __ok__ { fmt.Println(s.foo()); } else { switch __gen__.(type) { case *C: s := B( *__gen__.(*C) ) fmt.Println(s.foo()); case *A: fmt.Println("Generics RuntimeError - can not cast base class to a subclass type", s); } } case "A": s,__ok__ := __gen__.(*A) if __ok__ { fmt.Println(s.foo()); } else { fmt.Println("Generics RuntimeError - generic argument is not a pointer to a struct", s); fmt.Println("struct: ",__gen__); } } } func main() { b1 := __new__B(); barr := &[]*B{b1}; c1 := __new__C(); __addr11 := B(*c1);__11 := append(*barr,&__addr11); *barr = __11; __subclass__ := (*barr)[0] switch __subclass__.__class__ { case "C": __addr := C(*__subclass__) bb := &__addr fmt.Println(bb.foo()); __subclass__ := (*barr)[1] switch __subclass__.__class__ { case "C": __addr := C(*__subclass__) cc := &__addr fmt.Println(cc.foo()); fmt.Println("----testing generic----"); for _,subclass := range *barr { fmt.Println("subclass in bar:", subclass); my_generic(subclass) } case "B": __addr := B(*__subclass__) cc := &__addr fmt.Println(cc.foo()); fmt.Println("----testing generic----"); for _,subclass := range *barr { fmt.Println("subclass in bar:", subclass); my_generic(subclass) } } case "B": __addr := B(*__subclass__) bb := &__addr fmt.Println(bb.foo()); __subclass__ := (*barr)[1] switch __subclass__.__class__ { case "C": __addr := C(*__subclass__) cc := &__addr fmt.Println(cc.foo()); fmt.Println("----testing generic----"); for _,subclass := range *barr { fmt.Println("subclass in bar:", subclass); my_generic(subclass) } case "B": __addr := B(*__subclass__) cc := &__addr fmt.Println(cc.foo()); fmt.Println("----testing generic----"); for _,subclass := range *barr { fmt.Println("subclass in bar:", subclass); my_generic(subclass) } } } } type _kwargs_type_ struct { }