一: 需求
- 为什么要封装一个G变量?
- 在进行数据开发的过程中, 我们很多时候需要得到某一个维度的很多变量。而这些变量是通过计算得到的。如果每次都调用方法/发送请求获取这个值, 我们会浪费大量的资源, 去做重复的事情。
- 如何解决这样的问题?
- 我们可以将获取的值, 全部存储到一个变量中, 这样如果后续的计算, 再需要这个结果, 我们直接拿来用就好了。
二: 简单的封装G变量
-
思路:
- 重构getattr方法, 让使用g.属性的时候, 直接调用方法, 获取到的值,加入字典中, 并将本次的值返回。
- 方法中/API中,暴露一个字典,让其他三方通过参数的方式可以直接调用内部的方法。
-
method.py代码
from types import FunctionType
def name():
return "renshanwen"
def age():
return 18
method_map = dict()
for func in dir():
if isinstance(eval(func), FunctionType):
method_map.update({
func: eval(func)})
- g.py代码
# -*- coding: utf-8 -*-
from methods import method_map
class G(object):
def __getattr__(self, func_name):
# 调用方法, 获取值, 并将值赋值给__dict__, 这样满足下次直接使用.属性可以获取到。
if func_name in method_map.keys():
result = method_map.get(func_name)()
self.__dict__[func_name] = result
return result
def clean(self):
self.__dict__ = {
}
g = G()
- operaters 代码
from g import g
if __name__ == '__main__':
if g.name == "renshanwen" and g.age == 18:
print("get var success, name is {}, age is {}. \n".format(g.name, g.age))
else:
print("get var fail. \n")
- 运行结果:
get var success, name is renshanwen, age is 18.
三: 采用协程进行性能优化
-
存在的问题:
- 上述代码是存在问题的, 如果我们需要获取G关联的100个属性, 那么我们需要串行的一个一个方法/API的调用, 方法调用还好, 如果是API调用, 那网络延迟是非常影响性能的。因此需要优化。
-
解决方案:
- 采用协程, 并行获取G变量的多个属性。
-
g.py增加协程
# -*- coding: utf-8 -*-
import gevent
from methods import method_map
from gevent import monkey
monkey.patch_all()
class G(object):
def __getattr__(self, func_name):
# 调用方法, 获取值, 并将值赋值给__dict__, 这样满足下次直接使用.属性可以获取到。
if func_name in method_map.keys():
print("方法被调用了")
result = method_map.get(func_name)()
self.__dict__[func_name] = result
return result
def clean(self):
self.__dict__ = {
}
def parallel_req_vars(self, vars_list):
# 并行请求变量列表
tasks = [gevent.spawn(self.__getattr__, var) for var in vars_list]
gevent.joinall(tasks)
g = G()
- operates.py 进行测试
import time
from g import g
if __name__ == '__main__':
g.parallel_req_vars(vars_list=["name", "age"])
time.sleep(2)
print("在耗时....")
print(g.name)
print(g.age)
- 测试结果
方法被调用了
方法被调用了
在耗时....
renshanwen
18
四: 替换成API调用
- G变量的getattr进行调整
def __getattr__(self, func_name):
# 调用方法, 获取值, 并将值赋值给__dict__, 这样满足下次直接使用.属性可以获取到。
# 此处换成HTTP/RPC调用即可
result = rpc('input', func_name)
self.__dict__[func_name] = result
return result
五:方法/API调用的时候传入参数
- 代码调整:
def __getattr__(self, func_name, **kwargs):
# 调用方法, 获取值, 并将值赋值给__dict__, 这样满足下次直接使用.属性可以获取到。
if func_name in method_map.keys():
result = method_map.get(func_name)(**kwargs)
self.__dict__[func_name] = result
return result