V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Lighthughjiajin
V2EX  ›  程序员

关于 Golang 和 Python 设计数据结构思维的区别

  •  
  •   Lighthughjiajin · 346 天前 · 1357 次点击
    这是一个创建于 346 天前的主题,其中的信息可能已经有所发展或是发生改变。
    from abc import ABC, abstractmethod
    
    
    class Device(ABC):
    
        @abstractmethod
        def fn(self):
            raise NotImplementedError
    
        def sync(self):
            print("Start Sync ...")
            self.fn()
            print("Start Done.")
    
    
    class PC(Device):
    
        def fn(self):
            print("PC Device fn!!!")
    
    
    class Router(Device):
    
        def fn(self):
            print("Router Device sync")
    
    
    
    def start_sync(d: Device):
        d.sync()
    
    
    if __name__ == '__main__':
        p = PC()
        r = Router()
        start_sync(p)  # Run p.fn()
        start_sync(r)  # Run r.fn()
    

    请熟悉 Go 的大佬指导下,在 Go 中如何用接口与结构体实现类似的功能?(子类只需要重写 fn 方法,在 start_sync 处调用基类的 sync 时,能调用到子类重写后的 fn 方法)

    11 条回复    2023-05-18 00:33:27 +08:00
    FreeEx
        1
    FreeEx  
       346 天前
    golang 不支持方法重载。

    https://go.dev/doc/faq#overloading

    > 为什么 Go 不支持方法和运算符的重载?
    > 如果方法分派也不需要进行类型匹配,则它会得到简化。使用其他语言的经验告诉我们,具有相同名称但不同签名的各种方法偶尔有用,但在实践中也可能令人困惑和脆弱。仅按名称匹配并要求类型一致是 Go 类型系统中的一个主要简化决策。

    > 关于运算符重载,它似乎比绝对要求更方便。同样,没有它,事情会更简单。
    Alias4ck
        2
    Alias4ck  
       346 天前
    语言的转换 你问 chatgpt 可能一下就搞定了
    Contextualist
        4
    Contextualist  
       346 天前   ❤️ 1
    Go 的 interface 和 struct 那一套鼓励的是组合 (composition) 而不是继承,所以像 PC 和 Router 这种实现具体功能的类是会被作为 Device 这种实现公共方法的类的**一部分**,就是 Device 包裹住 PC 或 Router 。下面是我个人认为的对应 Go 的地道解决办法: https://go.dev/play/p/wio55S6HtdK
    lesismal
        5
    lesismal  
       346 天前
    Lighthughjiajin
        6
    Lighthughjiajin  
    OP
       346 天前
    @Contextualist 终于看到比较地道的写法,是我想了解的。
    还有一点疑问,在这场景下,IDevice, Device 结构体、接口的命名有什么约定吗?
    Lighthughjiajin
        7
    Lighthughjiajin  
    OP
       346 天前
    @Contextualist 这个写法下,有个缺点,Router 无法重写 Device 的 sync 方法,类似于以下 Python 代码
    ```
    from abc import ABC, abstractmethod


    class Device(ABC):

    @abstractmethod
    def fn(self):
    raise NotImplementedError

    def sync(self):
    print("Start Sync ...")
    self.fn()
    print("Start Done.")


    class PC(Device):

    def fn(self):
    print("PC sync")


    class Router(Device):

    def fn(self):
    print("Router Device sync")

    def sync(self):
    print("Router 重写了 sync")
    super().sync()
    print("Router sync ...")


    def start_sync(d: Device):
    d.sync()


    if __name__ == '__main__':
    r = Router()
    start_sync(r) # Run r.fn()
    start_sync(PC())

    ```
    Contextualist
        8
    Contextualist  
       346 天前
    @Lighthughjiajin 嗯对,为了清晰对应你的例子我尽量沿用了你的命名。Go 里的 Interface 一般都是“动词 + er”来命名。假设我把 fn 重命名为 act ,那接口就叫 Actor 或者 DeviceActor ,对应的实现叫 PCActor 和 RouterActor 。

    关于你提到的无法重写 sync 方法的问题,这意味着在你的业务逻辑里 sync 并不是只有一个实现,那就应该也把它列进接口里。根据不同情况,可以开一个新的 interface ,也可以加进 IDevice 。
    - 开一个新的 interface 那就可以实现一个 type DefaultSyncer struct{},只是初始化 Device 时得传两个值
    - 加进 IDevice 就是强制要求每一个具体实现都要实现 sync
    Lighthughjiajin
        9
    Lighthughjiajin  
    OP
       346 天前
    @Contextualist 感谢大佬,我整理了一下。
    https://go.dev/play/p/Z5Hl57w5XdF
    看看我理解的对吗?
    Contextualist
        10
    Contextualist  
       346 天前
    @Lighthughjiajin 免大佬。基本没问题,就是注意一下依赖关系,Syncer 单方面依赖 Actor ,22 行是没必要的。

    不过这个玩具例子有点过于复杂了😅,但愿你在实际应用中不会经常遇到这种情况。
    Lighthughjiajin
        11
    Lighthughjiajin  
    OP
       346 天前 via iPhone
    我写的是组合多个结构体来满足函数的 Device 接口参数要求,我看你写的是组合多个结构体来调用 sync ,也就是最终都是同一个类型,就是一个结构体。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3269 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 11:27 · PVG 19:27 · LAX 04:27 · JFK 07:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.