我想创建仅在我真正需要时初始化变量的代码。但是以常规方式初始化:
var = None
if var is None:
var = factory()
var2 = var
在代码中制造太多噪音。
我尝试创建快速解决方案,但我觉得有更好的选择。这是我的解决方案,速度快但无法获取参数并使用defaultdict。
def lazy_variable(factory):
data = defaultdict(factory)
return lambda: data['']
var = lazy_variable(a_factory)
var2 = var()
更多问题:
编辑:
请考虑表现。我知道我可以创建一个可以具有此行为的类,但它比简单的解决方案和默认的dict解决方案慢。
尝试一些解决方案:
定义:
import cachetools.func
import random
@cachetools.func.lru_cache(None)
def factory(i):
return random.random()
并运行:
%%timeit
for i in xrange(100):
q = factory(i)
q = factory(i)
得到:
100 loops, best of 3: 2.63 ms per loop
幼稚:
%%timeit
for i in xrange(100):
a = None
if a is None:
a = random.random()
q = a
q = a
得到:
The slowest run took 4.71 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 14.8 µs per loop
我不确定缓存的是什么
defaultdict解决方案:
%%timeit
for i in xrange(100):
a = lazy_variable(random.random)
q = a()
q = a()
得到:
The slowest run took 4.11 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 76.3 µs per loop
TNX!
答案 0 :(得分:1)
如果我们正在谈论实例变量,那么是 - 您可以编写自己的包装器并使其按照您想要的方式运行:
class LazyVar(object):
def __init__(self, factory, *args, **kwargs):
self.id = "__value_" + str(id(self)) # internal store
self.factory = factory
self.args = args
self.kwargs = kwargs
def __get__(self, instance, owner):
if instance is None:
return self
else:
try:
return getattr(instance, self.id)
except AttributeError:
value = self.factory(*self.args, **self.kwargs)
setattr(instance, self.id, value)
return value
def factory(name):
print("Factory called, initializing: " + name)
return name.upper() # just for giggles
class TestClass(object):
foo = LazyVar(factory, "foo")
bar = LazyVar(factory, "bar")
您可以将其测试为:
test = TestClass()
print("Foo will get initialized the moment we mention it")
print("Foo's value is:", test.foo)
print("It will also work for referencing, so even tho bar is not initialized...")
another_bar = test.bar
print("It gets initialized the moment we set its value to some other variable")
print("They, of course, have the same value: `{}` vs `{}`".format(test.bar, another_bar))
将打印:
Foo will get initialized the moment we mention it Factory called, initializing: foo Foo's value is: FOO It will also work for referencing, so even tho bar is not initialized... Factory called, initializing: bar It gets initialized the moment we set its value to some other variable They, of course, have the same value: `BAR` vs `BAR`
不幸的是,你不能对全局声明的变量使用相同的技巧,因为__get__()
仅在作为实例变量访问时被调用。
答案 1 :(得分:1)
如果我理解正确,那么您感兴趣的一些功能由functools.lru_cache
提供:
import functools as ft
@ft.lru_cache(None)
def lazy():
print("I'm working soo hard")
return sum(range(1000))
lazy() # 1st time factory is called
# I'm working soo hard
# 499500
lazy() # afterwards cached result is used
# 499500
装饰工厂也可以采用参数:
@ft.lru_cache(None)
def lazy_with_args(x):
print("I'm working so hard")
return sum((x+i)**2 for i in range(100))
lazy_with_args(3.4)
# I'm working so hard
# 363165.99999999994
lazy_with_args(3.4)
# 363165.99999999994
# new parametes, factory is used to compute new value
lazy_with_args(-1.2)
# I'm working so hard
# 316614.00000000006
lazy_with_args(-1.2)
# 316614.00000000006
# old value stays in cache
lazy_with_args(3.4)
# 363165.99999999994
答案 2 :(得分:0)
一个简单的容器(但仍需要括号)可以完成,例如像这样:
class Container:
UNDEF = object()
def __init__(self, factory):
self.data = Container.UNDEF
self.factory = factory
def __call__(self):
if self.data is Container.UNDEF:
self.data = self.factory()
return self.data
# Test:
var = Container(lambda: 5)
print(var())
print(var())
答案 3 :(得分:0)
您可以只需访问<path>
或locals()
并输入
globals()
但我从来没有遇到过这样有用的情况,所以你应该评估一下你想做你想做的事情。
答案 4 :(得分:0)
好的,我认为我找到了一个使用生成器的快速解决方案:
def create_and_generate(creator):
value = creator()
while True:
yield value
def lazy_variable(creator):
generator_instance = create_and_generate(creator)
return lambda: next(generator_instance)
另一个快速解决方案是:
def lazy_variable(factory):
data = []
def f():
if not data:
data.extend((factory(),))
return data[0]
return f
但我觉得发电机更清晰。