
槽多无口, 里面的接口还有 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 的属性值吗?
我这里想问一下大家, 如果我这么做了, 会收到律师函吗?
1 glasslion 2019-01-22 17:19:35 +08:00 这种代码一看就是 先有 Java 的 SDk, 实现 Python SDK 时直接照搬了 Java 的实现。 这种行为虽然挺恶心的, 但毕竟是最节省开发资源的做法,也只能忍了 。 Google 的 Python SDK 一样大量这种代码。 抛开代码风格的问题, 阿里云 Python SDK 早期的代码质量极差, 连培训班水平都不如。 目前的质量已经比以前好太多了。 https://github.com/aliyun/aliyun-openapi-python-sdk/issues/43 |
2 www5070504 2019-01-22 17:21:55 +08:00 支持 这种带有 java “风味”的 python 代码是 python 中的毒瘤 |
3 qq976739120 2019-01-22 17:24:07 +08:00 我之前看他们的代码以为是我功力不够的原因....原来是他们写的查,汗! |
4 wuhaochen999 2019-01-22 17:43:33 +08:00 get set 有啥问题 |
5 Cbdy 2019-01-22 18:08:59 +08:00 via Android 估计是直接根据 Java SDK 代码用编译器编译成 Python 代码的(代码生成)吧,其他语言类似。这样只需要维护一份 Java 代码即可 |
6 wusphinx 2019-01-22 18:10:11 +08:00 在保证代码正确性的前提下,代码不够 pythonic 还是可以接受的 |
7 alvin666 2019-01-22 18:13:19 +08:00 via Android 妈耶 这代码要人命啊 |
8 janxin 2019-01-22 18:15:22 +08:00 支持,每次用 Python 的 SDK 用的想死 |
9 aldslvda 2019-01-22 19:05:58 +08:00 java 味太浓 |
10 dongqihong 2019-01-22 19:32:18 +08:00 这是自动化生成的接口代码。。。 |
11 shaodamao 2019-01-22 19:50:50 +08:00 之前用过他们的 python sdk,sdk 代码应该是根据 api 生成的。 可以用 aliyunsdkcore.request 中的 CommonRequest,自己封装了个通用的方法,将就用一下 orz |
12 KgM4gLtF0shViDH3 2019-01-22 20:26:01 +08:00 这明显是自动生成的代码。。和机器人较真干啥。 |
13 iorilu 2019-01-22 22:18:37 +08:00 这个 sdk 是干啥用的 |
14 Faiz555 2019-01-22 23:03:28 +08:00 用过阿里云的短信 SDK,调用的时候感觉巨恶心 |
15 luozic 2019-01-22 23:49:45 +08:00 via iPhone 这种要么是基于 Java 生成,要么直接根据 OAI 类似 swagger 的规范直接一把生成,不分 java 还是啥 |
16 qingtangsdk 2019-01-22 23:59:57 +08:00 亲们,大家好,我是阿里云 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 这里吐槽,我们会抓紧时间改进哦! 亲们的批评就是我们改进的动力,为此我们会不断努力,感谢对阿里云的关心! |
17 lxml 2019-01-23 00:26:16 +08:00 via Android 想起了金山云的 SDK,fork 自 aws 的,然后大家表示读不懂代码,宁愿调用我的 http 接口都不愿意自己去接入。 |
18 niubee1 2019-01-23 01:26:42 +08:00 在国内的项目组里呢一般都只有边缘人和实习生才会干搞 SDK 这类事情, 质量差是很自然的事情 |
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 相比之下并没看出有什么优点,仅仅是满足了你自己的偏好而已。 |
20 busyboy 2019-01-23 07:54:59 +08:00 我已经很知足了,比部分云厂商的好很多 |
21 susucoolsama 2019-01-23 08:08:12 +08:00 via iPhone @qingtangsdk 这个回复应该是妹子客服回复的吧,这文笔不像是程序 gg.... |
22 est 2019-01-23 08:58:20 +08:00 via Android 代码烂不是槽点,依赖一个老掉牙的加密库才是 |
23 glasslion 2019-01-23 10:18:01 +08:00 |
24 Feiox 2019-01-23 10:51:21 +08:00 相比之下 微软的 azure python sdk 如同神赐一般 |
25 guanhui07 2019-01-23 14:25:16 +08:00 好像 java |
26 LeoQ OP @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 中是更友好的一种调用方法, 你觉得呢? |
29 abmin521 2019-01-23 16:30:46 +08:00 |
30 LeoQ OP @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 这些冗长的字符的. 这些快捷方式, 阿里做了吗? 没有. |
31 LeoQ OP |
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 的重度用户,其实不太明白为什么有“动态设置属性”这种需求。你有什么场景是必须要动态设置属性的嘛? |
33 LeoQ OP 我最后提到动态属性的意思是, 属性值是有好处的, 代码风格上会看起来更简单易读. 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 都没有用到. |
34 Trim21 2019-01-23 22:13:46 +08:00 via Android 看着真像自动生成的… |