• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Python与Go中“类的归一化设计”实现与对比

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

Python中类的归一化设计

在Python中实现类的归一化设计有2种思路:一种是使用abc模块限制,另外一种是在父类种给某些方法主动抛出异常,如果子类不实现父类的方法,根据方法的调用顺序程序会报错。

abc模块实现类的归一化

import abc

class Father(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def run(self):
        pass
    
class Son(Father):
    pass

if __name__ == '__main__':
    son = Son()

抽象类Father在定义的时候指定其元类为abc.ABCMeta,里面的run方法用装饰器abc.abstractmethod装饰,这样所有继承它的子类就必须实现run这个方法。
上述代码中Son类继承了Father类但是没有实现其父类的run方法,在Son类实例化对象的时候会抛出下列异常:

TypeError: Can't instantiate abstract class Son with abstract methods run

我们在子类中必须实现run方法才能使程序正常运行:

import abc

class Father(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def run(self):
        pass
    
class Son(Father):
    def run(self):
        return 'run'

if __name__ == '__main__':
    son = Son()

异常处理的方式实现归一化效果

利用abc模块实现的归一化设计是在子类实例化的时候进行判断的,这种方式难免有些强制,我平时比较常用异常处理的方式:

class Father(object):

    def run(self):
        raise NotImplementedError("must have method run()")

class Son(Father):
    pass

if __name__ == '__main__':
    
    son = Son()
    son.run()

从上面的代码可以看出:在实例化son对象的时候不会发生异常,但是在son试图调用run方法的时候,根据对象的属性与方法的查找顺序的原理可知,由于Son类本身没有实现run方法,只能从父类中去找,而父类的run方法会直接抛出异常!同样也实现了强制子类必须定义run方法的效果。

Go语言实现“类归一化效果”1

很多源码中会使用这种方式,使用下划线(_):

package main

type DataBaseInterface interface {
    Run1() string
    Run2() int
}

type TestStruct struct {
    Name string
    Age  int
}

func (t *TestStruct) Run1() string {
    return "Run1"
}

func (t *TestStruct) Run2() int {
    return 666
}

// TestStruct必须实现了DataBase接口才能编译过去 —— 确保结构体是实现了对应的interface
var _ DataBaseInterface = &TestStruct{}

// 或者:var _ DataBaseInterface = (*TestStruct)(nil)
func main() { }

Go语言实现“类归一化效果”2

在Go种其实并没有面向对象的概念,但是几乎所有面向对象的思想及设计我们都可以利用interface与struct去实现。

明确几个概念

首先,struct之间可以通过“组合”的方式实现类的“继承效果”。

其次,所谓的实现了某个interface,其实就是实现了这个interface种定义的所有方法!—— 注意这里的 所有 很重要!

实现代码

接下来直接上代码:

package main

// 定义一个interface,里面有3个方法
type TaskInterface interface {
    Run() int
    Task1() string
    Task2() string
}

// 父级struct,只实现了 TaskInterface 的2个方法 (当然也可以实现额外的方法)
type BasicTask struct{
    Name string
}
// 实现interface的方法
func (b *BasicTask) Task1() string{
    return "task1..."
}
func (b *BasicTask) Task2() string{
    return "task2..."
}
// 自己的方法
func (b *BasicTask) BasicTask1() (int, string){
    return 666, "666"
}


// 子struct继承上面的基类
type MyTask struct {
    BasicTask
}

// 子struct 必须实现 Run方法!!!否则下面的工厂函数会报错!!!
func (m *MyTask) Run() int{
    return 222
}

// 定义一个工厂函数,返回TaskInterface(实际上返回的是子类实例化对象的结构体指针)
// 注意这个工厂函数必须要求子struct Mytask必须实现Run()方法
func MyTaskChild1() TaskInterface{

// 注意在return之前,还可以实现一些其他的功能
return &MyTask{ BasicTask{ Name: "MyTaskChild1"
, }, } } func main() { }

实现讲解

1、首先我们定义了一个interface,这个interface有3个方法。

2、结构体BasicTask,只实现了interface的其中2个方法,没有实现Run方法。

3、子结构体MyTask继承了BasicTask,这样MyTask也相当于实现了interface的其中2个方法,但是也没有实现Run方法。

4、最核心的就是那个工厂函数MyTaskCHild1,注意它在定义阶段返回的是我们之前定义的那个接口TaskInterface对象,而我们return的是子类Mytask的结构体指针。

5、问题来了:如果MyTask这个结构体并没有实现接口TaskInterafce(如果没有实现Run方法),那么程序会报这样的错:

6、这样就实现了“类归一化设计的效果”:继承了BasicTask的子类MyTask,必实现Run方法才能使程序正确运行。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
POJ 3767 I Wanna Go Home (dijkstra算法)发布时间:2022-07-10
下一篇:
gogoroutines使用小结发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap