V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
LeoQ
V2EX  ›  Python

阿里云的 Python SDK 是真的烂啊, 想起个 aliyunpythonsdksucks 项目组, 然后把他们的 sdk 封装一下

  •  
  •   LeoQ ·
    LeoQuote · 2019-01-22 17:00:12 +08:00 · 5978 次点击
    这是一个创建于 1892 天前的主题,其中的信息可能已经有所发展或是发生改变。

    槽多无口, 里面的接口还有 v20140815 , 所有的 class 看起来都一样, 一样的长, 一样的难以理解

    举个例子 CreateOnlineDataBaseTaskRequest

    # 阿里云的代码
    from aliyunsdkcore.request import RpcRequest
    class CreateOnlineDatabaseTaskRequest(RpcRequest):
    
        def __init__(self):
            RpcRequest.__init__(self, 'Rds', '2014-08-15', 'CreateOnlineDatabaseTask','rds')
    
        def get_ResourceOwnerId(self):
            return self.get_query_params().get('ResourceOwnerId')
    
        def set_ResourceOwnerId(self,ResourceOwnerId):
            self.add_query_param('ResourceOwnerId',ResourceOwnerId)
    
        def get_MigrateTaskId(self):
            return self.get_query_params().get('MigrateTaskId')
    
        def set_MigrateTaskId(self,MigrateTaskId):
            self.add_query_param('MigrateTaskId',MigrateTaskId)
    
        def get_DBName(self):
            return self.get_query_params().get('DBName')
    
        def set_DBName(self,DBName):
            self.add_query_param('DBName',DBName)
    
        def get_ResourceOwnerAccount(self):
            return self.get_query_params().get('ResourceOwnerAccount')
    
        def set_ResourceOwnerAccount(self,ResourceOwnerAccount):
            self.add_query_param('ResourceOwnerAccount',ResourceOwnerAccount)
    
        def get_ClientToken(self):
            return self.get_query_params().get('ClientToken')
    
        def set_ClientToken(self,ClientToken):
            self.add_query_param('ClientToken',ClientToken)
    
        def get_OwnerAccount(self):
            return self.get_query_params().get('OwnerAccount')
    
        def set_OwnerAccount(self,OwnerAccount):
            self.add_query_param('OwnerAccount',OwnerAccount)
    
        def get_DBInstanceId(self):
            return self.get_query_params().get('DBInstanceId')
    
        def set_DBInstanceId(self,DBInstanceId):
            self.add_query_param('DBInstanceId',DBInstanceId)
    
        def get_CheckDBMode(self):
            return self.get_query_params().get('CheckDBMode')
    
        def set_CheckDBMode(self,CheckDBMode):
            self.add_query_param('CheckDBMode',CheckDBMode)
    
        def get_OwnerId(self):
            return self.get_query_params().get('OwnerId')
    
        def set_OwnerId(self,OwnerId):
            self.add_query_param('OwnerId',OwnerId)
    
    # 我想要的代码
    class DatabaseTask:
        def __init__(self, **kwargs):
            self.migrate_task = migrate_task
            self.db_name = db_name
            self.resource_owner = resource_owner
            self.token = token
            self.check_db_mode = check_db_mode
        
        def run():
            # make some request
    
    if __name__ == '__main__':
        new_task = DatabaseTask(migrate_task=1, db_name='some_db',
                                resource_owner='some_user', token='some_token',
                                check_db_mode='some_mode')
        result = new_task.run()
        result.fetch()
    

    为什么不写成 一个 DatabaseTask 对象, 对象有几个属性值 Instance_id 等 ,然后再调用 DatabaseTask.create()

    这些 set 和 get 的方法, 不就是实现了 python 的属性值吗?

    我这里想问一下大家, 如果我这么做了, 会收到律师函吗?

    34 条回复    2019-01-23 22:13:46 +08:00
    glasslion
        1
    glasslion  
       2019-01-22 17:19:35 +08:00   ❤️ 2
    这种代码一看就是 先有 Java 的 SDk, 实现 Python SDK 时直接照搬了 Java 的实现。 这种行为虽然挺恶心的, 但毕竟是最节省开发资源的做法,也只能忍了 。

    Google 的 Python SDK 一样大量这种代码。


    抛开代码风格的问题, 阿里云 Python SDK 早期的代码质量极差, 连培训班水平都不如。 目前的质量已经比以前好太多了。
    https://github.com/aliyun/aliyun-openapi-python-sdk/issues/43
    www5070504
        2
    www5070504  
       2019-01-22 17:21:55 +08:00   ❤️ 3
    支持 这种带有 java “风味”的 python 代码是 python 中的毒瘤
    qq976739120
        3
    qq976739120  
       2019-01-22 17:24:07 +08:00   ❤️ 1
    我之前看他们的代码以为是我功力不够的原因....原来是他们写的查,汗!
    wuhaochen999
        4
    wuhaochen999  
       2019-01-22 17:43:33 +08:00   ❤️ 1
    get set 有啥问题
    Cbdy
        5
    Cbdy  
       2019-01-22 18:08:59 +08:00 via Android   ❤️ 1
    估计是直接根据 Java SDK 代码用编译器编译成 Python 代码的(代码生成)吧,其他语言类似。这样只需要维护一份 Java 代码即可
    wusphinx
        6
    wusphinx  
       2019-01-22 18:10:11 +08:00   ❤️ 1
    在保证代码正确性的前提下,代码不够 pythonic 还是可以接受的
    alvin666
        7
    alvin666  
       2019-01-22 18:13:19 +08:00 via Android   ❤️ 1
    妈耶
    这代码要人命啊
    janxin
        8
    janxin  
       2019-01-22 18:15:22 +08:00   ❤️ 1
    支持,每次用 Python 的 SDK 用的想死
    aldslvda
        9
    aldslvda  
       2019-01-22 19:05:58 +08:00   ❤️ 1
    java 味太浓
    dongqihong
        10
    dongqihong  
       2019-01-22 19:32:18 +08:00   ❤️ 1
    这是自动化生成的接口代码。。。
    shaodamao
        11
    shaodamao  
       2019-01-22 19:50:50 +08:00   ❤️ 1
    之前用过他们的 python sdk,sdk 代码应该是根据 api 生成的。
    可以用 aliyunsdkcore.request 中的 CommonRequest,自己封装了个通用的方法,将就用一下 orz
    bestkayle
        12
    bestkayle  
       2019-01-22 20:26:01 +08:00   ❤️ 1
    这明显是自动生成的代码。。和机器人较真干啥。
    iorilu
        13
    iorilu  
       2019-01-22 22:18:37 +08:00   ❤️ 1
    这个 sdk 是干啥用的
    Faiz555
        14
    Faiz555  
       2019-01-22 23:03:28 +08:00
    用过阿里云的短信 SDK,调用的时候感觉巨恶心
    luozic
        15
    luozic  
       2019-01-22 23:49:45 +08:00 via iPhone
    这种要么是基于 Java 生成,要么直接根据 OAI 类似 swagger 的规范直接一把生成,不分 java 还是啥
    qingtangsdk
        16
    qingtangsdk  
       2019-01-22 23:59:57 +08:00   ❤️ 6
    亲们,大家好,我是阿里云 SDK 的研发 GG,看到吐槽就赶紧诚惶诚恐地过来啦^_^。首先感谢大家对阿里云 Python SDK 的关注,这里跟大家十分十分(* 1024 )诚恳地道歉,Python SDK 没有按照 Python 的编码规范设计,我们没做好,给大家造成了不好的体验,对不起!

    为了解决好这个这个问题呢,我们阿里云 SDK 研发团队正在抓紧时间编写阿里云 Python SDK 的第二版,目前是刚起步,项目代码在这里: https://github.com/aliyun/alibabacloud-python-sdk-v2 请大家过目。亲们可以在: https://github.com/aliyun/alibabacloud-python-sdk-v2/issues 这里吐槽,我们会抓紧时间改进哦!

    亲们的批评就是我们改进的动力,为此我们会不断努力,感谢对阿里云的关心!
    lxml
        17
    lxml  
       2019-01-23 00:26:16 +08:00 via Android
    想起了金山云的 SDK,fork 自 aws 的,然后大家表示读不懂代码,宁愿调用我的 http 接口都不愿意自己去接入。
    niubee1
        18
    niubee1  
       2019-01-23 01:26:42 +08:00
    在国内的项目组里呢一般都只有边缘人和实习生才会干搞 SDK 这类事情, 质量差是很自然的事情
    incompatible
        19
    incompatible  
       2019-01-23 02:40:53 +08:00
    v20140815 有什么问题? 公有云产品的 API 必须向下兼容,即便某天发布了 v2020xxxx,先前的 v20140815 也是要永久保留的。
    SDK 里 client 与 request 各司其职,request 负责封装 API 的参数; client 负责从 request 取参数、做签名、拼 url 发请求、解析返回值。且各个产品的各个 API 都 follow 此 pattern,学一次就会用所有产品的 API 了。
    你自己定义的 DatabaseTask 相比之下并没看出有什么优点,仅仅是满足了你自己的偏好而已。
    busyboy
        20
    busyboy  
       2019-01-23 07:54:59 +08:00
    我已经很知足了,比部分云厂商的好很多
    susucoolsama
        21
    susucoolsama  
       2019-01-23 08:08:12 +08:00 via iPhone
    @qingtangsdk 这个回复应该是妹子客服回复的吧,这文笔不像是程序 gg....
    est
        22
    est  
       2019-01-23 08:58:20 +08:00 via Android
    代码烂不是槽点,依赖一个老掉牙的加密库才是
    glasslion
        23
    glasslion  
       2019-01-23 10:18:01 +08:00
    @dongqihong
    @bestkayle
    @luozic
    @Cbdy
    你们高估阿里云了, 这代码就是让实习生抄出来的
    Feiox
        24
    Feiox  
       2019-01-23 10:51:21 +08:00   ❤️ 1
    相比之下 微软的 azure python sdk 如同神赐一般
    guanhui07
        25
    guanhui07  
       2019-01-23 14:25:16 +08:00
    好像 java
    LeoQ
        26
    LeoQ  
    OP
       2019-01-23 15:42:20 +08:00
    @incompatible 谢谢你的不留情面的批评, 可能是我孤陋寡闻了, 但是国外的云厂商, 我在 aws 的 sdk https://github.com/boto/botocore/tree/develop/botocore 里没有发现版本的印记, 在 azure 的 sdk 里, 版本的印记还存在, 但是一些最近有修改的模块已经没有了版本的文件夹 https://github.com/Azure/azure-sdk-for-python .

    这说明带时间的版本已经不是最佳实践, azure 也在尝试改变这一点, 完全可以通过 SDK 的主版本进行控制

    带时间的版本我认为也确实不友好, 比如 20140817 版本和 20180704 版本兼容吗? 用户是不清楚的 , 如果是类似 0.0.1 和 3.0.0 , 那么用户自己就有感觉, 这个代码可能是不兼容了,再参阅一下文档, 确实是不兼容的, 那么, 要么改代码, 要么安装低版本的 sdk.

    我给出的版本确实是满足了自己的偏好, 但我认为是确实比之前的容易理解的, 把 get set 改为对对象的属性值操作

    既然可以
    ```
    new_task = DatabaseTask()
    new_task.owner = 'me'
    print(new_task.owner)
    ```

    那么为什么要
    ```
    new_task = DatabaseTask()
    new_task.set_owner('me')
    print(new_task.get_owner('me'))
    ```

    而且属性值也天生支持动态获取, 动态设置

    ```
    new_task.setattr('owner','me')
    new_task.getattr('owner')
    ```
    如果是函数的话, 没有办法做到动态的.

    我认为这在 python 中是更友好的一种调用方法, 你觉得呢?
    LeoQ
        27
    LeoQ  
    OP
       2019-01-23 15:47:04 +08:00
    @est 新的已经不依赖了, 感恩
    est
        28
    est  
       2019-01-23 16:18:34 +08:00
    @LeoQ 那还挺不错。不过已经被我们用一个 5 行的 python 函数+urllib 给代替了。
    LeoQ
        30
    LeoQ  
    OP
       2019-01-23 16:38:04 +08:00
    @abmin521 https://github.com/Azure/azure-sdk-for-python/blob/master/azure-mgmt-dns/azure/mgmt/dns/models.py
    确实是有, 但是你看一下这里, 在 models 层做了一个快捷方式, 默认情况下, 是调 v2018_03_01_preview.models 的, 对于开发者来说, 如果版本的需求, 是可以不用在代码里体现 v2018_03_01_preview 这些冗长的字符的.

    这些快捷方式, 阿里做了吗? 没有.
    LeoQ
        31
    LeoQ  
    OP
       2019-01-23 16:40:36 +08:00
    incompatible
        32
    incompatible  
       2019-01-23 20:41:38 +08:00
    @LeoQ

    关于 API 的向下兼容:并非指的是 v2020xxxx 要兼容 v20140815 的功能,而是说即便发布了 v2020xxxx 后,v20140815 依然可用并且行为不变。 对于调用方来说,升级 api 版本或者 sdk 版本都是非常 critical 的事,在升级过程中,校验 sdk 或 api 的版本兼容性是必不可少的步骤。当然如你所说,sdk 版本和 api 版本强绑定是个糟糕的设计。

    属性和 setter 之间的取舍,你看一下阿里 sdk 的源码,它是在 setter 里把 params 放到了父类的一个 dictionary 里了。另外基于它代码中有 add_query_param()的字样,我推测除了 query params 应该还有 body params、header_params、这些是基于 api metadata 生成 sdk 的 request 就固定下来的,如果不用 setter 的方式就需要额外的 metadata 来描述一个 param 的位置到底是在 query、body 还是 header 中。

    最后关于动态设置 request 的属性,我自己是阿里云 ecs/rds/slb 等产品 java sdk 的重度用户,其实不太明白为什么有“动态设置属性”这种需求。你有什么场景是必须要动态设置属性的嘛?
    LeoQ
        33
    LeoQ  
    OP
       2019-01-23 21:36:09 +08:00
    我最后提到动态属性的意思是, 属性值是有好处的, 代码风格上会看起来更简单易读.

    Python 和 Java 类似, 也可以有 setter 和 getter 函数

    不一样的是, python 还有 property 装饰器, 加入这个装饰器, 就可以正常使用 student.age del(student.age)这种做法了
    ```
    class Student(object):
    def __init__(self):
    self._age = None

    @property
    def age(self):
    return self._age

    @age.setter
    def age(self, age):
    if isinstance(age, int):
    self._age = age
    return
    if isinstance(age, str) and age.isdigit():
    age = int(age)
    self._age = age
    else:
    raise ValueError("age is illegal")

    @age.deleter
    def age(self):
    del self._age
    ```
    我还顺着翻到了它的 base 类 https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-core/aliyunsdkcore/request.py 到了 base 依然是有着大量的普通的 set_XXX / set_XXX 方法.

    而我因为他们代码都是 set_XXX 的, 我也得写这样冗长的代码, 为什么不改成上面的那种呢?

    我的意思是, python 里有很多的语法糖可以做这些事情, 但是阿里云的 sdk 都没有用到.
    Trim21
        34
    Trim21  
       2019-01-23 22:13:46 +08:00 via Android
    看着真像自动生成的…
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3240 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 12:14 · PVG 20:14 · LAX 05:14 · JFK 08:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.