patternpythonMinor
Thin proxy class for hashables
Viewed 0 times
proxyclasshashablesforthin
Problem
I need some kind of thin wrapper object to mark dictionary keys, like:
And these wrappers should behave like the wrapped object (comparison, hashing, etc):
With a single additional property: it should remember the added class:
I dislike those heavyweight Proxy Patterns which mimic all special properties and thinking of the following idea instead:
This basically instantiates the type again, but with an additional base, and seems to work:
Can you think of any disadvantages of this approach?
d = {
Required('name'): str,
Optional('age'): int,
}And these wrappers should behave like the wrapped object (comparison, hashing, etc):
marked = Required('name')
marked == 'name' #-> True
d[marked] = 'hello'
d['name'] #-> 'hello'With a single additional property: it should remember the added class:
isinstance(marked, Required) #-> TrueI dislike those heavyweight Proxy Patterns which mimic all special properties and thinking of the following idea instead:
class Wrapper(object):
def __new__(cls, value):
value_type = type(value)
Type = type(
value_type.__name__, # same name
(cls, value_type), # Wrapper + bases
{})
Type.__new__ = value_type.__new__ # prevent recursion
return Type(value)
# Override method
def __repr__(self):
return 'Wrapper({})'.format(self)This basically instantiates the type again, but with an additional base, and seems to work:
v = 'abc' # Wrapped value
vt = type(v) # Wrapped value type
w = Wrapper(v)
assert w == v # equality
assert isinstance(w, vt) # type matches
assert isinstance(w, Wrapper) # and is Wrapper
assert hash(w) == hash(v) # hash matches (required for dict keys)Can you think of any disadvantages of this approach?
Solution
You asked, "Can you think of any disadvantages of this approach?" One obvious problem is that this requires you to construct a new class each time the wrapper is called, which potentially ends up wasting a lot of memory. So you would want to cache and reuse these classes.
Note that this is a rare case where we actually want to use a private name — the idea is to avoid a name clash when wrapping a class that already has a
I can't help feeling, though, that Martijn Pieters is right in the Stack Overflow version of this question: if you just want to proxy a dictionary key, then you just need to proxy the
class Wrapper(object):
# A map from (value type, wrapper class) to wrapped class
__cache = {}
def __new__(cls, value):
value_type = type(value)
bases = value_type, cls
try:
wrapper_class = cls.__cache[bases]
except KeyError:
wrapper_class = type(value_type.__name__, bases, {})
cls.__cache[bases] = wrapper_class
return wrapper_class(value)Note that this is a rare case where we actually want to use a private name — the idea is to avoid a name clash when wrapping a class that already has a
cache (or _cache, or whatever name we pick) attribute.I can't help feeling, though, that Martijn Pieters is right in the Stack Overflow version of this question: if you just want to proxy a dictionary key, then you just need to proxy the
__eq__ and __hash__ special methods. Keep it simple!Code Snippets
class Wrapper(object):
# A map from (value type, wrapper class) to wrapped class
__cache = {}
def __new__(cls, value):
value_type = type(value)
bases = value_type, cls
try:
wrapper_class = cls.__cache[bases]
except KeyError:
wrapper_class = type(value_type.__name__, bases, {})
cls.__cache[bases] = wrapper_class
return wrapper_class(value)Context
StackExchange Code Review Q#61095, answer score: 3
Revisions (0)
No revisions yet.