I have some function which uses outside variables. A (substantially) simplified example:
a = 2
b = 3
def f(x):
return x * a + b
While I need a
and b
in f
, I don't need them anywhere else. In particular, one can write a = 5
, and that will change the behavior of f
. How should I make a
and b
invisible to the outside?
Other languages allow me to write roughly the following code:
let f =
a = 2
b = 3
lambda x: x * a + b
What I want:
f
must work as intended and have the same signature a
and b
must be computed only once a
and b
must not exist in the scope outside of f
a =...
and b =...
don't affect f
g
and then deletes it, which I don't like (eg there is a risk of overriding an existing g
and I believe that it's simply ugly):def g():
a = 2
b = 3
return lambda x: x * a + b
f = g()
del g
One method is to simply use a class. This allows you to place a
and b
in the scope of the class while f
can still access them.
class F:
def __init__(self):
self.a = 2
self.b = 3
def __call__(self, x):
return x * self.a + self.b
f = F()
f(1)
# returns:
5
If you don't like having to call the class constructor, you can override __new__
to essentially create a callable with internal stored variables. This is an antipattern though and not very pythonic.
class f:
a = 2
b = 3
def __new__(cls, x):
return x * cls.a + cls.b
f(1)
# returns:
5
This approach is based on the answers provided in this thread , though scoped to the specific problem above. You can use a decorator to update the global variables available to the function while also storin a
and b
within a closure.
from functools import wraps
def dec_ab(fn):
a = 2
b = 3
@wraps(fn)
def wrapper(*args, **kwargs):
# get global scope
global_scope = f.__globals__
# copy current values of variables
var_list = ['a', 'b']
current_vars = {}
for var in var_list:
if var in global_scope:
current_vars[var] = global_scope.get(var)
# update global scope
global_scope.update({'a': a, 'b': b})
try:
out = fn(*args, **kwargs)
finally:
# undo the changes to the global scope
for var in var_list:
global_scope.pop(var)
global_scope.update(current_vars)
return out
return wrapper
@dec_ab
def f(x):
"""hello world"""
return x * a + b
This preserves the functions signature and keeps a
and b
from being altered
f(1)
# returns:
5
a
# raises:
NameError: name 'a' is not defined
You can use default arguments to accomplish this. Default arguments are only computed once, when the closure is created (that is why if you have mutable objects as default arguments, the state is retained between invocations).
def f(x, a=2, b=3):
return x * a + b
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.