接口数据变更

2024-05-17 08:54:32 浏览数 (1)

日常工作中会遇到数据处理需求频繁变更的情况,有时候需要处理大量的Json任务。如果家纺提出一点改动,Python处理数据如何避免大量修改代码。

这个问题,对接数据的同学会经常碰到。我们本文来探讨下如何有效率完成这些工作。

一、名字变更需求

首先这个问题,现在实际生产环境会用到 一些 接口协议:比如说thrift,proto、avro等成熟接口协议。特别是跨部分对接的工作很少直接裸用Json。因为这些成熟的接口协议在数据传输大小、数据存储、序列化反序列化效率上以及跨多种语言支持上有很棒的表现。裸用JSON也不能说没有,因为JSON在人类代码可读性这方面还是有优势。如果不会考虑很重的性能,从基于简单些来说,有时候也会直接用JSON。

那么如果碰到接口字段变更,这里我们分为两种情况考虑减少字段变更带来的代码维护成本:

  • JSON的字段变更
  • Protobuf等字段变更

JSON的字段变更这里我在细化为

  • 名字规范变更
  • 业务需求变更

二、名字规范变更

在业务上线初期,团队可能不会考虑很多代码规范,赶时间上线。但是到了中后期,随着业务代码量膨胀,那么规范代码风格是减少维护成本的重要一环。

我们来看一个python的官方规范,俗称PEP8,其中我们直接跳到名字规范这一节

https://peps.python.org/pep-0008/#descriptive-naming-styles

其中列举了这些不规范的写法

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords, or CamelCase – so named because of the bumpy look of its letters [4]). This is also sometimes known as StudlyCaps.Note: When using acronyms in CapWords, capitalize all the letters of the acronym. Thus HTTPServerError is better than HttpServerError.
  • mixedCase (differs from CapitalizedWords by initial lowercase character!)
  • Capitalized_Words_With_Underscores (ugly!)

那么如果碰到这样我们怎么进行有效率改造呢

2.1 pyhumps包

这里我介绍官方提供的一个pip包pyhumps。pyhumps提供了驼峰camel,去驼峰decamelize,pascal。kebab写法。比如说jack in the box我们用四种写法分别是:

  • 驼峰:jackInTheBox
  • 去驼峰:jack_in_the_box
  • pascal: JackInTheBox
  • kebab: jack-in-the-box

pyhumps的官方链接https://pypi.org/project/pyhumps/

安装包的方法:

代码语言:bash复制
pipenv install pyhumps

使用场景:

  • 转换名字
代码语言:python代码运行次数:0复制
import humps

humps.camelize("jack_in_the_box")  # jackInTheBox
humps.decamelize("rubyTuesdays")  # ruby_tuesdays
humps.pascalize("red_robin")  # RedRobin
humps.kebabize("white_castle")  # white-castle
  • Json转换词典keys
代码语言:python代码运行次数:0复制
import humps

array = [{"attrOne": "foo"}, {"attrOne": "bar"}]
humps.decamelize(array) # [{"attr_one": "foo"}, {"attr_one": "bar"}]

array = [{"attr_one": "foo"}, {"attr_one": "bar"}]
humps.camelize(array)  # [{"attrOne": "foo"}, {"attrOne": "bar"}]

array = [{'attr_one': 'foo'}, {'attr_one': 'bar'}]
humps.kebabize(array)  # [{'attr-one': 'foo'}, {'attr-one': 'bar'}]

array = [{"attr_one": "foo"}, {"attr_one": "bar"}]
humps.pascalize(array)  # [{"AttrOne": "foo"}, {"AttrOne": "bar"}]
  • 检查名字是否遵守规范
代码语言:python代码运行次数:0复制
import humps

humps.is_camelcase("illWearYourGranddadsClothes")  # True
humps.is_pascalcase("ILookIncredible")  # True
humps.is_snakecase("im_in_this_big_ass_coat")  # True
humps.is_kebabcase('from-that-thrift-shop')  # True
humps.is_camelcase("down_the_road")  # False

humps.is_snakecase("imGonnaPopSomeTags")  # False
humps.is_kebabcase('only_got_twenty_dollars_in_my_pocket')  # False


# what about abbrevations, acronyms, and initialisms? No problem!
humps.decamelize("APIResponse")  # api_response

三、业务需求变更:

如果裸用Json的情况下,业务要求变更名字。一个减少维护成本最简单的想法就是少hard code名字。名字用全局变量宏替代。比如说GLOBAL_NAME='name1', 业务代码都用GLOBAL_NAME。像这种名字可以放在constants包里面。然后代码哪里需要用到就去import Mymodule.Constants.

实际代码情况会是更复杂的情况。这源自于代码长期迭代,里面风格会变了又变,风格混用,名字各种版本的问题。特别是JSON key这种数据交换媒介,往往是自由风格的字符串。比如说拥有了混合风格的JSON

代码语言:json复制
{"keyOne": "value1", "key_one": "value2"}

这里所有会更推从用更成熟的数据交换协议,比如说protobuf

比如说proto协议定义如下

代码语言:txt复制
syntax = "proto2";

package tutorial;

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    PHONE_TYPE_UNSPECIFIED = 0;
    PHONE_TYPE_MOBILE = 1;
    PHONE_TYPE_HOME = 2;
    PHONE_TYPE_WORK = 3;
  }

  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = PHONE_TYPE_HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

与生成 Java 和 C protocol buffer 代码不同,Python protocol buffer 编译器不会直接生成数据访问代码。你将会看到addressbook_pb2.py它为所有消息、枚举和字段以及一些神秘的空类生成特殊描述符,每个类对应一种消息类型:

代码语言:python代码运行次数:0复制
class Person(message.Message):
  __metaclass__ = reflection.GeneratedProtocolMessageType

  class PhoneNumber(message.Message):
    __metaclass__ = reflection.GeneratedProtocolMessageType
    DESCRIPTOR = _PERSON_PHONENUMBER
  DESCRIPTOR = _PERSON

class AddressBook(message.Message):
  __metaclass__ = reflection.GeneratedProtocolMessageType
  DESCRIPTOR = _ADDRESSBOOK

Person就好像它将Message基类的每个字段定义为常规字段一样。例如,

代码语言:python代码运行次数:0复制
import addressbook_pb2
person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.PHONE_TYPE_HOME

请注意,这些分配不仅仅是向通用 Python 对象添加任意新字段。如果尝试分配.proto文件中未定义的字段,AttributeError则会引发错误。如果将字段分配给错误类型的值,TypeError则会引发 a 。此外,在设置字段之前读取字段的值会返回默认值。

代码语言:python代码运行次数:0复制
person.no_such_field = 1  # raises AttributeError

0 人点赞