python_reques接口测试框架,Excel作为案例数据源
一、框架菜单
1.1 common模块
1.2 其他
二、Excel接口测试案例编写
三、读取Excel测试封装(核心封装)
excel_utils.py 读取Excel中的数据


- import os
- import xlrd #内置模块、第三方模块pip install 自定义模块
- class ExcelUtils():
- def __init__(self,file_path,sheet_name):
- self.file_path = file_path
- self.sheet_name = sheet_name
- self.sheet = self.get_sheet() # 整个表格对象
- def get_sheet(self):
- wb = xlrd.open_workbook(self.file_path)
- sheet = wb.sheet_by_name(self.sheet_name)
- return sheet
- def get_row_count(self):
- row_count = self.sheet.nrows
- return row_count
- def get_col_count(self):
- col_count = self.sheet.ncols
- return col_count
- def __get_cell_value(self,row_index, col_index):
- cell_value = self.sheet.cell_value(row_index,col_index)
- return cell_value
- def get_merged_info(self):
- merged_info = self.sheet.merged_cells
- return merged_info
- def get_merged_cell_value(self,row_index, col_index):
- """既能获取普通单元格的数据又能获取合并单元格数据"""
- cell_value = None
- for (rlow, rhigh, clow, chigh) in self.get_merged_info():
- if (row_index >= rlow and row_index < rhigh):
- if (col_index >= clow and col_index < chigh):
- cell_value = self.__get_cell_value(rlow, clow)
- break; # 防止循环去进行判断出现值覆盖的情况
- else:
- cell_value = self.__get_cell_value(row_index, col_index)
- else:
- cell_value = self.__get_cell_value(row_index, col_index)
- return cell_value
- def get_sheet_data_by_dict(self):
- all_data_list = []
- first_row = self.sheet.row(0) #获取首行数据
- for row in range(1, self.get_row_count()):
- row_dict = {}
- for col in range(0, self.get_col_count()):
- row_dict[first_row[col].value] = self.get_merged_cell_value(row, col)
- all_data_list.append(row_dict)
- return all_data_list
- if __name__=='__main__':
- current_path = os.path.dirname(__file__)
- excel_path = os.path.join( current_path,'..','samples/data/test_case.xlsx' )
- excelUtils = ExcelUtils(excel_path,"Sheet1")
- for row in excelUtils.get_sheet_data_by_dict():
- print( row )
testdata_utils.py 读取Excel中的数据后处理成需要的数据


- import os
- from common.excel_utils import ExcelUtils
- from common.config_utils import config
- current_path = os.path.dirname(__file__)
- test_data_path = os.path.join( current_path,'..', config.CASE_DATA_PATH )
- class TestdataUtils():
- def __init__(self,test_data_path = test_data_path):
- self.test_data_path = test_data_path
- self.test_data = ExcelUtils(test_data_path,"Sheet1").get_sheet_data_by_dict()
- self.test_data_by_mysql = SqlUtils().get_mysql_test_case_info()
- def __get_testcase_data_dict(self):
- testcase_dict = {}
- for row_data in self.test_data:
- testcase_dict.setdefault( row_data['测试用例编号'],[] ).append( row_data )
- return testcase_dict
- def def_testcase_data_list(self):
- testcase_list = []
- for k,v in self.__get_testcase_data_dict().items():
- one_case_dict = {}
- one_case_dict["case_id"] = k
- one_case_dict["case_info"] = v
- testcase_list.append( one_case_dict )
- return testcase_list
- if __name__=="__main__":
- testdataUtils = TestdataUtils()
- for i in testdataUtils.def_testcase_data_list():
- print( i )
四、request封装(核心封装)
- requests_utils.py 包含post请求,get请求,异常,调用断言


- import ast
- import re
- import requests
- import jsonpath
- from requests.exceptions import RequestException
- from requests.exceptions import ProxyError
- from requests.exceptions import ConnectionError
- from common.config_utils import config
- from common.check_utils import CheckUtils
- class RequestsUtils():
- def __init__(self):
- self.hosts = config.hosts
- self.headers = {"ContentType":"application/json;charset=utf-8"}
- self.session = requests.session()
- self.temp_variables = {}
- def __get(self,get_info):
- try:
- url = self.hosts + get_info["请求地址"]
- response = self.session.get( url = url,
- params = ast.literal_eval(get_info["请求参数(get)"])
- )
- response.encoding = response.apparent_encoding
- if get_info["取值方式"] == "json取值":
- value = jsonpath.jsonpath( response.json(),get_info["取值代码"] )[0]
- self.temp_variables[ get_info["传值变量"] ] = value
- elif get_info["取值方式"] == "正则取值":
- value = re.findall(get_info["取值代码"],response.text)[0]
- self.temp_variables[get_info["传值变量"]] = value
- result = CheckUtils(response).run_check(get_info['期望结果类型'], get_info['期望结果'])
- except ProxyError as e:
- result = {'code': 4, 'result': '[%s]请求:代理错误异常' % (get_info["接口名称"])}
- except ConnectionError as e:
- result = {'code': 4, 'result': '[%s]请求:连接超时异常' % (get_info["接口名称"])}
- except RequestException as e:
- result = {'code': 4, 'result': '[%s]请求:Request异常,原因:%s' % (get_info["接口名称"], e.__str__())}
- except Exception as e:
- result = {'code':4,'result':'[%s]请求:系统异常,原因:%s'%(get_info["接口名称"],e.__str__())}
- return result
- def __post(self,post_info):
- try:
- url = self.hosts + post_info["请求地址"]
- response = self.session.post( url = url,
- headers = self.headers,
- params = ast.literal_eval(post_info["请求参数(get)"]),
- # data = post_infos["提交数据(post)"],
- json=ast.literal_eval(post_info["提交数据(post)"])
- )
- response.encoding = response.apparent_encoding
- if post_info["取值方式"] == "json取值":
- value = jsonpath.jsonpath( response.json(),post_info["取值代码"] )[0]
- self.temp_variables[ post_info["传值变量"] ] = value
- elif post_info["取值方式"] == "正则取值":
- value = re.findall(post_info["取值代码"],response.text)[0]
- self.temp_variables[post_info["传值变量"]] = value
- #调用CheckUtils()
- result = CheckUtils(response).run_check(post_info['期望结果类型'],post_info['期望结果'])
- except ProxyError as e:
- result = {'code': 4, 'result': '[%s]请求:代理错误异常' % (post_info["接口名称"])}
- except ConnectionError as e:
- result = {'code': 4, 'result': '[%s]请求:连接超时异常' % (post_info["接口名称"])}
- except RequestException as e:
- result = {'code': 4, 'result': '[%s]请求:Request异常,原因:%s' % (post_info["接口名称"], e.__str__())}
- except Exception as e:
- result = {'code':4,'result':'[%s]请求:系统异常,原因:%s'%(post_info["接口名称"],e.__str__())}
- return result
- def request(self,step_info):
- try:
- request_type = step_info["请求方式"]
- param_variable_list = re.findall('\\${\w+}', step_info["请求参数(get)"])
- if param_variable_list:
- for param_variable in param_variable_list:
- step_info["请求参数(get)"] = step_info["请求参数(get)"]\
- .replace(param_variable,'"%s"' % self.temp_variables.get(param_variable[2:-1]))
- if request_type == "get":
- result = self.__get( step_info )
- elif request_type == "post":
- data_variable_list = re.findall('\\${\w+}', step_info["提交数据(post)"])
- if data_variable_list:
- for param_variable in data_variable_list:
- step_info["提交数据(post)"] = step_info["提交数据(post)"] \
- .replace(param_variable, '"%s"' % self.temp_variables.get(param_variable[2:-1]))
- result = self.__post( step_info )
- else:
- result = {'code':1,'result':'请求方式不支持'}
- except Exception as e:
- result = {'code':4,'result':'用例编号[%s]的[%s]步骤出现系统异常,原因:%s'%(step_info['测试用例编号'],step_info["测试用例步骤"],e.__str__())}
- return result
- def request_by_step(self,step_infos):
- self.temp_variables = {}
- for step_info in step_infos:
- temp_result = self.request( step_info )
- # print( temp_result )
- if temp_result['code']!=0:
- break
- return temp_result
- if __name__=="__main__":
- case_info = [
- {'请求方式': 'get', '请求地址': '/cgi-bin/token', '请求参数(get)': '{"grant_type":"client_credential","appid":"wxXXXXXxc16","secret":"XXXXXXXX"}', '提交数据(post)': '', '取值方式': 'json取值', '传值变量': 'token', '取值代码': '$.access_token', '期望结果类型': '正则匹配', '期望结果': '{"access_token":"(.+?)","expires_in":(.+?)}'},
- {'请求方式': 'post', '请求地址': '/cgi-bin/tags/create', '请求参数(get)': '{"access_token":${token}}', '提交数据(post)': '{"tag" : {"name" : "衡东"}}','取值方式': '无', '传值变量': '', '取值代码': '', '期望结果类型': '正则匹配', '期望结果': '{"tag":{"id":(.+?),"name":"衡东"}}'}
- ]
- RequestsUtils().request_by_step(case_info)
五、断言封装(核心封装)
- check_utils.py 断言封装,与实际结果核对


- import re
- import ast
- class CheckUtils():
- def __init__(self,check_response=None):
- self.ck_response=check_response
- self.ck_rules = {
- '无': self.no_check,
- 'json键是否存在': self.check_key,
- 'json键值对': self.check_keyvalue,
- '正则匹配': self.check_regexp
- }
- self.pass_result = {
- 'code': 0,
- 'response_reason': self.ck_response.reason,
- 'response_code': self.ck_response.status_code,
- 'response_headers': self.ck_response.headers,
- 'response_body': self.ck_response.text,
- 'check_result': True,
- 'message': '' # 扩招作为日志输出等
- }
- self.fail_result = {
- 'code': 2,
- 'response_reason': self.ck_response.reason,
- 'response_code': self.ck_response.status_code,
- 'response_headers': self.ck_response.headers,
- 'response_body': self.ck_response.text,
- 'check_result': False,
- 'message': '' # 扩招作为日志输出等
- }
- def no_check(self):
- return self.pass_result
- def check_key(self,check_data=None):
- check_data_list = check_data.split(',') #把需要判断的值做切割,取出键值
- res_list = [] #存放每次比较的结果
- wrong_key = [] #存放比较失败key
- for check_data in check_data_list: #把切割的键值和取出响应结果中的所有的键一个一个对比
- if check_data in self.ck_response.json().keys():
- res_list.append(self.pass_result )
- else:
- res_list.append( self.fail_result )
- wrong_key.append(check_data) #把失败的键放进来,便于后续日志输出
- # print(res_list)
- # print(wrong_key)
- if self.fail_result in res_list:
- return self.fail_result
- else:
- return self.pass_result
- def check_keyvalue(self,check_data=None):
- res_list = [] # 存放每次比较的结果
- wrong_items = [] # 存放比较失败 items
- for check_item in ast.literal_eval(check_data).items(): #literal_eval()安全性的把字符串转成字典,items()取出键值对
- if check_item in self.ck_response.json().items():
- res_list.append( self.pass_result )
- else:
- res_list.append( self.fail_result )
- wrong_items.append(check_item)
- # print( res_list )
- # print( wrong_items )
- if self.fail_result in res_list:
- return self.fail_result
- else:
- return self.pass_result
- def check_regexp(self,check_data=None):
- pattern = re.compile(check_data)
- if re.findall(pattern=pattern,string=self.ck_response.text): #匹配到了,不为空,为true
- return self.pass_result
- else:
- return self.fail_result
- def run_check(self,check_type=None,check_data=None):
- code = self.ck_response.status_code
- if code == 200:
- if check_type in self.ck_rules.keys():
- result=self.ck_rules[check_type](check_data)
- return result
- else:
- self.fail_result['message'] = '不支持%s判断方法'%check_type
- return self.fail_result
- else:
- self.fail_result['message'] = '请求的响应状态码非%s'%str(code)
- return self.fail_result
- if __name__=="__main__":
- # 检查键是否存在,{"access_token":"hello","expires_":7200} 设为响应结果,"access_token,expires_in" 为检查对象值
- CheckUtils({"access_token":"hello","expires_":7200}).check_key("access_token,expires_in")
- #检查键值对是否存在
- CheckUtils({"access_token":"hello","expires_i":7200}).check_keyvalue('{"expires_in": 7200}')
- #正则对比
- #TURE
- print(CheckUtils('{"access_token":"hello","expires_in":7200}').check_regexp('"expires_in":(.+?)'))
- #False
- print(CheckUtils('{"access_token":"hello","expires":7200}').check_regexp('"expires_in":(.+?)'))
六、api_testcase下的api_test.py 封装


- import warnings
- import unittest
- import paramunittest
- from common.testdata_utils import TestdataUtils
- from common.requests_utils import RequestsUtils
- #如果是mysql数据源的话切换成 def_testcase_data_list_by_mysql() exccel数据源:def_testcase_data_list()
- case_infos = TestdataUtils().def_testcase_data_list_by_mysql()
- @paramunittest.parametrized(
- *case_infos
- )
- class APITest(paramunittest.ParametrizedTestCase):
- def setUp(self) -> None:
- warnings.simplefilter('ignore', ResourceWarning) #不会弹出警告提示
- def setParameters(self, case_id, case_info):
- self.case_id = case_id
- self.case_info = case_info
- def test_api_common_function(self):
- '''测试描述'''
- self._testMethodName = self.case_info[0].get("测试用例编号")
- self._testMethodDoc = self.case_info[0].get("测试用例名称")
- actual_result = RequestsUtils().request_by_step(self.case_info)
- self.assertTrue( actual_result.get('check_result'),actual_result.get('message') )
- if __name__ == '__main__':
- unittest.main()
七、common下的log_utils.py 封装


- import os
- import logging
- import time
- from common.config_utils import config
- current_path = os.path.dirname(__file__)
- log_output_path = os.path.join( current_path,'..', config.LOG_PATH )
- class LogUtils():
- def __init__(self,log_path=log_output_path):
- self.log_name = os.path.join( log_output_path ,'ApiTest_%s.log'%time.strftime('%Y_%m_%d') )
- self.logger = logging.getLogger("ApiTestLog")
- self.logger.setLevel( config.LOG_LEVEL )
- console_handler = logging.StreamHandler() # 控制台输出
- file_handler = logging.FileHandler(self.log_name,'a',encoding='utf-8') # 文件输出
- formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
- console_handler.setFormatter(formatter)
- file_handler.setFormatter(formatter)
- self.logger.addHandler( console_handler )
- self.logger.addHandler( file_handler )
- console_handler.close() # 防止打印日志重复
- file_handler.close() # 防止打印日志重复
- def get_logger(self):
- return self.logger
- logger = LogUtils().get_logger() # 防止打印日志重复
- if __name__ == '__main__':
- logger.info('hello')
八、common下的config_utils.py的封装
配置文件的编写:
对配置文件的读取封装:


- import os
- import configparser
- current_path = os.path.dirname(__file__)
- cfgpath = os.path.join(current_path, "../conf/local_config.ini")
- print(cfgpath)
- class ConfigUtils:
- def __init__(self,config_path=cfgpath):
- self.__conf=configparser.ConfigParser()
- self.__conf.read(config_path, encoding="utf-8")
- def read_ini(self,sec,option):
- value=self.__conf.get(sec,option)
- return value
- @property
- def hosts(self):
- value=self.read_ini('default','hosts')
- return value
- @property
- def LOG_PATH(self):
- value = self.read_ini('path', 'LOG_PATH')
- return value
- @property
- def CASE_DATA_PATH(self):
- value = self.read_ini('path', 'CASE_DATA_PATH')
- return value
- @property
- def REPORT_PATH(self):
- value = self.read_ini('path', 'REPORT_PATH')
- return value
- @property
- def LOG_LEVEL(self):
- value = int(self.read_ini('log', 'LOG_LEVEL'))
- return value
- @property
- def smtp_server(self):
- smtp_server_value = self.read_ini('email', 'smtp_server')
- return smtp_server_value
- @property
- def smtp_sender(self):
- smtp_sender_value = self.read_ini('email', 'smtp_sender')
- return smtp_sender_value
- @property
- def smtp_password(self):
- smtp_password_value = self.read_ini('email', 'smtp_password')
- return smtp_password_value
- @property
- def smtp_receiver(self):
- smtp_receiver_value = self.read_ini('email', 'smtp_receiver')
- return smtp_receiver_value
- @property
- def smtp_cc(self):
- smtp_cc_value = self.read_ini('email', 'smtp_cc')
- return smtp_cc_value
- @property
- def smtp_subject(self):
- smtp_subject_value = self.read_ini('email', 'smtp_subject')
- return smtp_subject_value
- config=ConfigUtils()
- if __name__=='__main__':
- current_path = os.path.dirname(__file__)
- cfgpath = os.path.join(current_path, "../conf/local_config.ini")
- config_u=ConfigUtils()
- print(config_u.hosts)
- print(config_u.LOG_LEVEL)
九、test_runner下的run_case.py 封装


- class RunCase():
- def __init__(self):
- self.test_case_path = test_case_path
- self.report_path = test_report_path
- self.title = 'P1P2接口自动化测试报告'
- self.description = '自动化接口测试框架'
- self.tester = '测试开发组'
- def load_test_suite(self):
- discover = unittest.defaultTestLoader.discover(start_dir=self.test_case_path,
- pattern='api_test.py',
- top_level_dir=self.test_case_path)
- all_suite = unittest.TestSuite()
- all_suite.addTest( discover )
- return all_suite
- def run(self):
- report_dir = HTMLTestReportCN.ReportDirectory(self.report_path)
- report_dir.create_dir(self.title)
- report_file_path = HTMLTestReportCN.GlobalMsg.get_value('report_path')
- fp = open( report_file_path ,'wb' )
- runner = HTMLTestReportCN.HTMLTestRunner(stream=fp,
- title=self.title,
- description=self.description,
- tester=self.tester)
- runner.run( self.load_test_suite() )
- fp.close()
- return report_file_path
- if __name__=='__main__':
- report_path = RunCase().run()
- EmailUtils(open(report_path, 'rb').read(), report_path).send_mail()
十、common下的email_utils.py 封装


- import os
- import smtplib
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- from common.config_utils import config
- class EmailUtils():
- def __init__(self,smtp_body,smtp_attch_path=None):
- self.smtp_server = config.smtp_server
- self.smtp_sender = config.smtp_sender
- self.smtp_password = config.smtp_password
- self.smtp_receiver = config.smtp_receiver
- self.smtp_cc = config.smtp_cc
- self.smtp_subject = config.smtp_subject
- self.smtp_body = smtp_body
- self.smtp_attch = smtp_attch_path
- def mail_message_body(self):
- message = MIMEMultipart()
- message['from'] = self.smtp_sender
- message['to'] = self.smtp_receiver
- message['Cc'] = self.smtp_cc
- message['subject'] = self.smtp_subject
- message.attach( MIMEText(self.smtp_body,'html','utf-8') )
- if self.smtp_attch:
- attach_file = MIMEText(open(self.smtp_attch, 'rb').read(), 'base64', 'utf-8')
- attach_file['Content-Type'] = 'application/octet-stream'
- attach_file.add_header('Content-Disposition', 'attachment', filename=('gbk', '', os.path.basename(self.smtp_attch)))
- message.attach(attach_file)
- return message
- def send_mail(self):
- smtp = smtplib.SMTP()
- smtp.connect(self.smtp_server)
- smtp.login(user=self.smtp_sender, password=self.smtp_password)
- smtp.sendmail(self.smtp_sender,self.smtp_receiver.split(",")+ self.smtp_cc.split(","), self.mail_message_body().as_string())
- if __name__=='__main__':
- html_path = os.path.dirname(__file__) + '/../test_reports/接口自动化测试报告V1.1/接口自动化测试报告V1.1.html'
- EmailUtils('<h3 align="center">自动化测试报告</h3>',html_path).send_mail()
python_reques接口测试框架,Excel作为案例数据源的更多相关文章
- Web自动化框架之五一套完整demo的点点滴滴(excel功能案例参数化+业务功能分层设计+mysql数据存储封装+截图+日志+测试报告+对接缺陷管理系统+自动编译部署环境+自动验证false、error案例)
标题很大,想说的很多,不知道从那开始~~直接步入正题吧 个人也是由于公司的人员的现状和项目的特殊情况,今年年中后开始折腾web自动化这块:整这个原因很简单,就是想能让自己偷点懒.也让减轻一点同事的苦力 ...
- python接口测试框架遇到的坑(一)excel数字转文本
一.遇到的问题 python编写接口测试框架中,接口用例使用excel维护,其中预期值(code码的值)20000和实际值总是不一致,后来通过打印type发现一个是unicode,一个是float. ...
- 【转】基于Python的接口测试框架实例
下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 背景 最近公司在做消息推送,那么自然就会产生很多接口,测试 ...
- 基于Python的接口测试框架实例
文章来源:http://www.jb51.net/article/96481.htm 下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考. ...
- Python+API接口测试框架设计(pytest)
1.测试框架简介 整个接口测试框架的设计图如下: base:存放的是请求方法二次封装 common:存放的是自定义工具,操作excel,yaml文件等 data:存放的是公共动态数据,如data.xl ...
- python+API接口测试框架设计(unittest)
1.测试框架简介 整个接口测试框架的设计图如下: basepage:存放的是公共的方法 common:存放的是自定义工具 data:存放的是公共动态数据,如BB.xls/ Id.md log:存放的是 ...
- 初探接口测试框架--python系列7
点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...
- 初探接口测试框架--python系列6
点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...
- Python接口测试实战4(上) - 接口测试框架实战
如有任何学习问题,可以添加作者微信:lockingfree 课程目录 Python接口测试实战1(上)- 接口测试理论 Python接口测试实战1(下)- 接口测试工具的使用 Python接口测试实战 ...
随机推荐
- LA3177长城守卫
题意: 有n个人围成一个圈,每个人都有r[i]个礼物,任意两个相邻的人的礼物不能有重复的,问满足所有相邻不重复的最少礼物种数是多少?就是问最少多少种礼物能让任意相邻的两个人的礼物不重复. 思 ...
- hdu3117 斐波那契前后4位
题意: 求斐波那契的前后4位,n <= 10^8. 思路: 至于前四位,和hdu1568的求法一样: http://blog.csdn.net/u013761 ...
- poj2418map或者字典树
题意: 给你一些串,然后求出每个串出现的概率. 思路: 简单题目,做法也很多,我用字典树做了下,然后又用map做了下,其实这个题目我感觉直接排序一遍之后线性输出应该是最简单最快的( ...
- MS08_067漏洞攻击
MS08_067 MS08_067是典型的缓冲区溢出漏洞,Windows的Server服务在处理特制RPC请求时存在缓冲区溢出漏洞,远程攻击者可以通过发送恶意的RPC请求触发这个溢出,导致完全入侵用户 ...
- CVE-2013-0077:Microsoft DirectShow quartz.dll m2p 文件堆溢出漏洞简单分析
0x01 前言 2012 年 10 月 5 日,exploit-db 漏洞公布站点上发布了 QQplayer.exe 3.7.892 m2p quartz.dll Heap Pointer OverW ...
- React 代码共享最佳实践方式
任何一个项目发展到一定复杂性的时候,必然会面临逻辑复用的问题.在React中实现逻辑复用通常有以下几种方式:Mixin.高阶组件(HOC).修饰器(decorator).Render Props.Ho ...
- 2020.12.20vj补题
A - Insomnia cure 题意:一共s只龙,每隔k,l,m,n只龙就会受伤,问这s只龙有多少龙是受伤的 思路:看起来题目范围并不是很多,直接进行循环判断 代码: 1 #include< ...
- 迷失在Mysql的锁世界~
1.饮料 这是一杯饮料. 这是3杯饮料,每杯饮料的味道各不相同. 但是人类并不称呼[饮料]为[饮料],而是称呼[饮料]为[记录]. 2.红章鱼 这个红色的,长的像章鱼的家伙,我们就叫它红章鱼~ 红章鱼 ...
- OO随笔之魔鬼的第一单元——多项式求导
OO是个借助Java交我们面向对象的课,可是萌新们总是喜欢带着面向过程的脑子去写求导,然后就是各种一面(main)到底.各种方法杂糅,然后就是被hack的很惨. 第一次作业:萌新入门面向对象 题目分析 ...
- Java Arrays.sort()重写comparator方法
先看一下接口 Arrays.sort(T[],Comparator<? super T> c); comparator要重写compare方法 compare方法大概长这样,返回值> ...