本质上,我想将一个变量放在堆栈上,堆栈上该部分下方的所有调用都可以访问该变量,直到块退出。 在Java中,我将使用带有支持方法的本地静态线程来解决这个问题,然后可以从方法中访问该方法。
典型示例:您收到一个请求,并打开一个数据库连接。 在请求完成之前,您希望所有代码都使用此数据库连接。 完成并关闭请求后,您可以关闭数据库连接。
我需要这个的目的是一个报告生成器。 每个报告由多个部分组成,每个部分可以依赖于不同的计算,有时不同的部分部分依赖于相同的计算。 由于我不想重复繁重的计算,因此我需要缓存它们。 我的想法是用缓存装饰器来装饰方法。 缓存根据方法名称和模块及其参数创建一个 id,查看堆栈变量中是否已计算出该 id,如果没有,则执行该方法。
我将尝试通过展示我当前的实现来澄清这一点。 我想做的就是简化那些实现计算的代码。
首先,我有中央缓存访问对象,我将其称为 MathContext:
class MathContext(object):
def __init__(self, fn):
self.fn = fn
self.cache = dict()
def get(self, calc_config):
id = create_id(calc_config)
if id not in self.cache:
self.cache[id] = calc_config.exec(self)
return self.cache[id]
fn 参数是创建上下文所关联的文件名,可以从中读取数据进行计算。
然后我们有计算类:
class CalcBase(object):
def exec(self, math_context):
raise NotImplementedError
这是一个愚蠢的斐波那契示例。 这些方法实际上不是递归的,它们适用于大量数据,但它可以演示如何依赖其他计算:
class Fibonacci(CalcBase):
def __init__(self, n): self.n = n
def exec(self, math_context):
if self.n < 2: return 1
a = math_context.get(Fibonacci(self.n-1))
b = math_context.get(Fibonacci(self.n-2))
return a+b
我希望斐波那契只是一个装饰方法:
@cache
def fib(n):
if n<2: return 1
return fib(n-1)+fib(n-2)
以 math_context 为例,当math_context 超出了范围,所有缓存的值也超出了范围。 我想要装饰器也有同样的东西。 IE。 在 X 点,@cache 缓存的所有内容都被取消引用以进行 gced。
In essence, I want to put a variable on the stack, that will be reachable by all calls below that part on the stack until the block exits. In Java I would solve this using a static thread local with support methods, that then could be accessed from methods.
Typical example: you get a request, and open a database connection. Until the request is complete, you want all code to use this database connection. After finishing and closing the request, you close the database connection.
What I need this for, is a report generator. Each report consist of multiple parts, each part can rely on different calculations, sometimes different parts relies in part on the same calculation. As I don't want to repeat heavy calculations, I need to cache them. My idea is to decorate methods with a cache decorator. The cache creates an id based on the method name and module, and it's arguments, looks if it has this allready calculated in a stack variable, and executes the method if not.
I will try and clearify by showing my current implementation. Want I want to do is to simplify the code for those implementing calculations.
First, I have the central cache access object, which I call MathContext:
class MathContext(object):
def __init__(self, fn):
self.fn = fn
self.cache = dict()
def get(self, calc_config):
id = create_id(calc_config)
if id not in self.cache:
self.cache[id] = calc_config.exec(self)
return self.cache[id]
The fn argument is the filename the context is created in relation to, from where data can be read to be calculated.
Then we have the Calculation class:
class CalcBase(object):
def exec(self, math_context):
raise NotImplementedError
And here is a stupid Fibonacci example. Non of the methods are actually recursive, they work on large sets of data instead, but it works to demonstrate how you would depend on other calculations:
class Fibonacci(CalcBase):
def __init__(self, n): self.n = n
def exec(self, math_context):
if self.n < 2: return 1
a = math_context.get(Fibonacci(self.n-1))
b = math_context.get(Fibonacci(self.n-2))
return a+b
What I want Fibonacci to be instead, is just a decorated method:
@cache
def fib(n):
if n<2: return 1
return fib(n-1)+fib(n-2)
With the math_context example, when math_context goes out of scope, so does all it's cached values. I want the same thing for the decorator. Ie. at point X, everything cached by @cache is dereferrenced to be gced.
发布评论
评论(5)
我继续做了一些可以满足你想要的东西。 它既可以用作装饰器,也可以用作上下文管理器:
I went ahead and made something that might just do what you want. It can be used as both a decorator and a context manager:
这个问题似乎是两个问题
b)你已经回答了自己
a)我似乎不明白为什么你需要将它放在堆栈上?
你可以做其中之一
可能是它的属性,
这样他们就可以从
中心位置
全局连接方法,
围绕它,或创建一个上下文
对象并传递
上下文,连接可以是一部分
上下文
等
this question seems to be two question
b) you have answered yourselves
a) I don't seem to understand why you need to put it on stack?
you can do one of these
could be attribute of it
so that they get a connection from
central location
global connection method
around it, or create a context
object and pass around
context,connection can be a part of
context
etc, etc
您可以使用封装在 getter 函数中的全局变量:
You could use a global variable wrapped in a getter function:
“你收到一个请求,并打开一个数据库连接......你关闭数据库连接。”
这就是对象的用途。 创建连接对象,将其传递给其他对象,然后在完成后关闭它。 全局变量不合适。 只需将值作为参数传递给正在执行该工作的其他对象即可。
“每个报告由多个部分组成,每个部分可以依赖于不同的计算,有时不同的部分部分依赖于相同的计算......我需要缓存它们”
这就是对象的用途。 创建包含有用计算结果的字典,并将其在报表部件之间传递。
你不需要搞乱“堆栈变量”、“静态线程本地”或类似的东西。
只需将普通变量参数传递给普通方法函数即可。 你会快乐很多。
您可以像这样使用它
您可以定义任意数量的计算并将它们全部折叠到单个容器对象中。
如果需要,您可以将 MathContext 变成正式的上下文管理器,以便它与 with 语句一起使用。 将这两个方法添加到 MathContext。
然后你就可以这样做。
在 with 语句的末尾,您可以保证调用
__exit__
方法。"you get a request, and open a database connection.... you close the database connection."
This is what objects are for. Create the connection object, pass it to other objects, and then close it when you're done. Globals are not appropriate. Simply pass the value around as a parameter to the other objects that are doing the work.
"Each report consist of multiple parts, each part can rely on different calculations, sometimes different parts relies in part on the same calculation.... I need to cache them"
This is what objects are for. Create a dictionary with useful calculation results and pass that around from report part to report part.
You don't need to mess with "stack variables", "static thread local" or anything like that.
Just pass ordinary variable arguments to ordinary method functions. You'll be a lot happier.
You can use it like this
You can define any number of calculations and fold them all into a single container object.
If you want, you can make the MathContext into a formal Context Manager so that it work with the with statement. Add these two methods to MathContext.
Then you can do this.
At the end of the with statement, you can guaranteed that the
__exit__
method was called.同时(python 3.7及更高版本)同样的问题找到了另一个正确的解决方案:
from contextvars import ContextVar
这里是 PEP
https://peps.python.org/pep-0567/
meanwhile (python 3.7 and later) the same problem has found another proper solution:
from contextvars import ContextVar
here is the PEP
https://peps.python.org/pep-0567/