patternpythonMinor
Dictionary as context manager
Viewed 0 times
contextdictionarymanager
Problem
I've created a class that implements customized addition, subtraction, etc. But for this class I'd like to have some additional parameters for these methods (
So my first thought was: A global dictionary that can be set or altered before I do the operations and internally it just accesses this dictionary and assumes these were given as
So I thought I can solve this with a dictionary that can be used as context manager that allows me to set them temporarly but cleans up afterwards:
Do you think this approach is good? Are there alternatives or even builtin or plugins that do essentially the same (maybe some configuration-like class)? Is the code fairly straightforward without to
__add__, ...).So my first thought was: A global dictionary that can be set or altered before I do the operations and internally it just accesses this dictionary and assumes these were given as
kwargs. This obviously solved my problem but another one emerged: I had to keep track of this global dictionary and it's state.So I thought I can solve this with a dictionary that can be used as context manager that allows me to set them temporarly but cleans up afterwards:
from copy import deepcopy
class Arguments(object):
"""A dictionary container that can be used as context manager.
The context manager allows to modify the dictionary values and after
exiting it resets them to the original state.
Parameters
----------
kwargs :
Initial values for the contained dictionary.
Attributes
----------
defaults : dict
The `dict` containing the defaults as key-value pairs
"""
defaults = {}
def __init__(self, **kwargs):
# Copy the original and update the current dictionary with the values
# passed in.
self.dct_copy = deepcopy(self.defaults)
self.defaults.update(kwargs)
def __enter__(self):
# return the dictionary so one can catch it if one wants don't want to
# always update the class attribute or change some defaults in between
return self.defaults
def __exit__(self, type, value, traceback):
# clear the dictionary (in case someone added a new value) and update
# it with the original values again
self.defaults.clear()
self.defaults.update(self.dct_copy)Do you think this approach is good? Are there alternatives or even builtin or plugins that do essentially the same (maybe some configuration-like class)? Is the code fairly straightforward without to
Solution
What you’re trying to do reminds me a lot of
And then
For reference, here are the relevant parts of
I picked the parts not using thread-locals here but the principle is the same if they are enabled.
And for the context manager part:
Based on that, if you don't plan on using threads, you can simplify the design:
The advantage of such approach is that you can directly use
But if you want to add extra methods to manualy supply extra arguments, it is simplified thanks to
Global usage is still pretty much the same, though:
decimal's contexts. Basically, you have setcontext and getcontext to manipulate the current context, they are a wrappers around threading and thread-local objects to be able to manage a different context per thread if need be. In your case, if you don't plan on supporting threads, a global object can do (as is your Arguments.defaults).And then
localcontext which is a thin layer around _ContextManager which performs pretty much what Arguments do: save a context using getcontext on __enter__ and restore it using setcontext on __exit__.For reference, here are the relevant parts of
decimal:def setcontext(context):
if context in (DefaultContext, BasicContext, ExtendedContext):
context = context.copy()
context.clear_flags()
threading.current_thread().__decimal_context__ = context
def getcontext():
try:
return threading.current_thread().__decimal_context__
except AttributeError:
context = Context()
threading.current_thread().__decimal_context__ = context
return contextI picked the parts not using thread-locals here but the principle is the same if they are enabled.
And for the context manager part:
def localcontext(ctx=None):
if ctx is None: ctx = getcontext()
return _ContextManager(ctx)
class _ContextManager(object):
def __init__(self, new_context):
self.new_context = new_context.copy()
def __enter__(self):
self.saved_context = getcontext()
setcontext(self.new_context)
return self.new_context
def __exit__(self, t, v, tb):
setcontext(self.saved_context)Based on that, if you don't plan on using threads, you can simplify the design:
import copy
class Arguments:
def __init__(self, **kwargs):
self.update(kwargs)
def update(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def get_arguments():
global _arguments
try:
return _arguments
except NameError:
_arguments = Arguments()
return _arguments
def set_arguments(arg=None, **kwargs):
global _arguments
if arg is None:
arg = get_arguments()
_arguments = copy.copy(arg)
_arguments.update(kwargs)
class local_arguments:
def __init__(self, arg=None, **kwargs):
if arg is None:
arg = get_arguments()
self.new_arguments = copy.copy(arg)
self.new_arguments.update(kwargs)
def __enter__(self):
self.old_arguments = get_arguments()
set_arguments(self.new_arguments)
return self.new_arguments
def __exit__(self, t, v, tb):
set_arguments(self.old_arguments)The advantage of such approach is that you can directly use
get_arguments in the magic methods, without having to rely on a delegated one:class Container(object):
def __init__(self, data):
self.data = data
def __add__(self, other):
args = get_arguments()
print(self, other, args)But if you want to add extra methods to manualy supply extra arguments, it is simplified thanks to
local_argument:def add(self, other, **kwargs):
with local_arguments(**kwargs):
return self + otherGlobal usage is still pretty much the same, though:
set_arguments(foo=42, bar='baz')
with local_arguments(foo=8):
Container(2) + 10Code Snippets
def setcontext(context):
if context in (DefaultContext, BasicContext, ExtendedContext):
context = context.copy()
context.clear_flags()
threading.current_thread().__decimal_context__ = context
def getcontext():
try:
return threading.current_thread().__decimal_context__
except AttributeError:
context = Context()
threading.current_thread().__decimal_context__ = context
return contextdef localcontext(ctx=None):
if ctx is None: ctx = getcontext()
return _ContextManager(ctx)
class _ContextManager(object):
def __init__(self, new_context):
self.new_context = new_context.copy()
def __enter__(self):
self.saved_context = getcontext()
setcontext(self.new_context)
return self.new_context
def __exit__(self, t, v, tb):
setcontext(self.saved_context)import copy
class Arguments:
def __init__(self, **kwargs):
self.update(kwargs)
def update(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def get_arguments():
global _arguments
try:
return _arguments
except NameError:
_arguments = Arguments()
return _arguments
def set_arguments(arg=None, **kwargs):
global _arguments
if arg is None:
arg = get_arguments()
_arguments = copy.copy(arg)
_arguments.update(kwargs)
class local_arguments:
def __init__(self, arg=None, **kwargs):
if arg is None:
arg = get_arguments()
self.new_arguments = copy.copy(arg)
self.new_arguments.update(kwargs)
def __enter__(self):
self.old_arguments = get_arguments()
set_arguments(self.new_arguments)
return self.new_arguments
def __exit__(self, t, v, tb):
set_arguments(self.old_arguments)class Container(object):
def __init__(self, data):
self.data = data
def __add__(self, other):
args = get_arguments()
print(self, other, args)def add(self, other, **kwargs):
with local_arguments(**kwargs):
return self + otherContext
StackExchange Code Review Q#128985, answer score: 2
Revisions (0)
No revisions yet.