Python Property Change Listener Pattern - python

Anyone know of any easy way to track changes to a dictionary object in python? I am at a high level doing crud, so I have a couple methods that handle changing a dictionary, if the dictionary changes I want to call a function to basically do an Observer/Notify.
class MyClass(object):
def update(self, item):
changed = False
if(self.my_dict.has_key(item.id)):
self.my_dict[item.id] = item
changed = True
if(changed):
self.notify()
What I am trying to avoid is all of the tracking(setting the boolean) code. Was hoping there was an easier way to track changes. This is a simple case, but there could have been more complicated logic that would result in me having to set the changed flag.

You can derive from the dict class and add a callback on any changes. This requires to overwrite any methods that change the dictionary:
class NotifyDict(dict):
__slots__ = ["callback"]
def __init__(self, callback, *args, **kwargs):
self.callback = callback
dict.__init__(self, *args, **kwargs)
def _wrap(method):
def wrapper(self, *args, **kwargs):
result = method(self, *args, **kwargs)
self.callback()
return result
return wrapper
__delitem__ = _wrap(dict.__delitem__)
__setitem__ = _wrap(dict.__setitem__)
clear = _wrap(dict.clear)
pop = _wrap(dict.pop)
popitem = _wrap(dict.popitem)
setdefault = _wrap(dict.setdefault)
update = _wrap(dict.update)

Subclass dict and override __setitem__, making sure to call dict.__setitem__ after your stuff to ensure the item actually gets saved.
class Notifier(dict):
def __setitem__(self, key, value):
print 'Something is being set!'
dict.__setitem__(self, key, value)

Related

Attributes as dictionary values

I am attempting to set attributes on a dict key and have a feeling my approach is wrong. How would I get the current key inside alfa? Alfa in production will be used to create and update a dataclass.
class Dictn(dict):
def __init__(self, *args, **kwds):
super(Dictn, self).__init__(*args, **kwds)
self.__dict__ = self
def __getitem__(self, key):
if key not in self.keys():
self.__setitem__(key, None)
dict.__getitem__(self, key)
return self
def alfa(self, **kwds):
''' Need current key to set attributes on it '''
self.__setattr__(**kwds)
def beta(self, **kwds):
''' Need current key to set attributes on it '''
self.__setattr__(**kwds)
dictn = Dictn()
dictn['ABC'].alfa(es=1, te=2)
dictn['ABC'].beta(es=3, te=4)
A dictionary stores objects that can be recovered as key-value pairs. (Remember, everything in python is an object.) If you want those objects to have additional attributes, you need to edit them directly, not the dict class which only acts as a container. The dict doesn't really care what it stores, and it certainly isn't (and shouldn't be) responsible for handling any properties on its contents.
(By the way, dictn["ABC"] represents a value in the dictionary, not a key. They key here is "ABC". I'm not sure if you're just confusing the definitions here or if you mean something else entirely.)
You could set an instance variable to the key used in __getitem__().
class Dictn(dict):
def __init__(self, *args, **kwds):
super(Dictn, self).__init__(*args, **kwds)
self.__dict__ = self
def __getitem__(self, key):
if key not in self.keys():
self.__setitem__(key, None)
self.current_key = key
dict.__getitem__(self, key)
return self
def alfa(self, **kwds):
// use self.current_key to get current_key to set attributes on it
self.__setattr__(**kwds)
def beta(self, **kwds):
// use self.current_key to get current_key to set attributes on it
self.__setattr__(**kwds)
Solution is to use a class as an attribute setter:
#dataclass()
class Position:
es: int = None
te: int = None
class ValueObj():
def alfa(self, **kwds):
setattr(self, 'alfa', Position(**kwds))
def beta(self, **kwds):
setattr(self, 'beta', Position(**kwds))
class Dictn(dict):
def __init__(self, *args, **kwds):
super(Dictn, self).__init__(*args, **kwds)
self.__dict__ = self
def __getitem__(self, key):
if key not in self.keys():
self.__setitem__( key,
ValueObj(),
)
return dict.__getitem__(self, key)
dictn = Dictn()
dictn['ABC'].alfa(es=1, te=2)
dictn['ABC'].alfa
>> Position(es=1, te=2)
This answer was posted as an edit to the question Attributes as dictionary values by the OP misantroop under CC BY-SA 4.0.

Python: how to disable creation of new keys in attribute dict?

Here is a simple code of attribute dict:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
More context:
https://stackoverflow.com/a/14620633/1179925
I can use it like:
d = AttrDict({'a':1, 'b':2})
print(d)
I want this to be possible:
d.b = 10
print(d)
But I want this to be impossible:
d.c = 4
print(d)
Is it possible to throw an error on new key creation?
You could check if they are already in there
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__dict__ = self
def __setattr__(self, key, value):
if key not in [*self.keys(), '__dict__']:
raise KeyError('No new keys allowed')
else:
super().__setattr__(key, value)
def __setitem__(self, key, value):
if key not in self:
raise KeyError('No new keys allowed')
else:
super().__setitem__(key, value)
First I thought this would be a bad idea since no initial values could be added but from the builtins it states the following:
dict(iterable) -> new dictionary initialized as if via:
d = {}
for k, v in iterable:
d[k] = v
So this does allow you to change the methods without them having effect on the initialization of the Class as it creates a new one from {} instead of from its own instance.
They will be able to change __ dict __ always though..
You can override the __setattr__ special method.
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__setattr__('_initializing', True)
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
super(AttrDict, self).__setattr__('_initializing', False)
def __setattr__(self, x, value):
if x == '_initializing':
raise KeyError("You should not edit _initalizing")
if self._initializing or x in self.__dict__.keys():
super(AttrDict, self).__setattr__(x, value)
else:
raise KeyError("No new keys allowed!")
Note that I needed to add an attribute _initializing to let __setattr__ distinguish between attributes created by __init__ and attributes created by users.
Since python does not have private attributes, users might still set _initializing to True and then add their attributes to the AttrDict instance, so I added a further check to be sure that they are not trying to edit _initializing.
It is still not 100% safe, since an user could still use super() to set _initializing to True.

Add methods to a class generated from other methods

I have classes like this:
class Tool(object):
def do_async(*args):
pass
for which I want to automatically generate non-async methods that make use of the async methods:
class Tool(object):
def do_async(*args):
pass
def do(*args):
result = self.do_async(*args)
return magical_parser(result)
This gets to be particularly tricky because each method needs to be accessible as both an object and class method, which is normally achieved with this magical decorator:
class class_or_instance(object):
def __init__(self, fn):
self.fn = fn
def __get__(self, obj, cls):
if obj is not None:
f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
else:
f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
functools.update_wrapper(f, self.fn)
return f
How can I make these methods, and make sure they're accessible as both class and object methods? This seems like something that could be done with decorators, but I am not sure how.
(Note that I don't know any of the method names in advance, but I know that all of the methods that need new buddies have _async at the end of their names.)
I think I've gotten fairly close, but this approach does not appropriately set the functions as class/object methods:
def process_asyncs(cls):
methods = cls.__dict__.keys()
for k in methods:
methodname = k.replace("_async","")
if 'async' in k and methodname not in methods:
#class_or_instance
def method(self, verbose=False, *args, **kwargs):
response = self.__dict__[k](*args,**kwargs)
result = self._parse_result(response, verbose=verbose)
return result
method.__docstr__ = ("Returns a table object.\n" +
cls.__dict__[k].__docstr__)
setattr(cls,methodname,MethodType(method, None, cls))
Do not get the other method from the __dict__; use getattr() instead so the descriptor protocol can kick in.
And don't wrap the method function in a MethodType() object as that'd neutralize the descriptor you put on method.
You need to bind k to the function you generate; a closured k would change with the loop:
#class_or_instance
def method(self, verbose=False, _async_method_name=k, *args, **kwargs):
response = getattr(self, _async_method_name)(*args,**kwargs)
result = self._parse_result(response, verbose=verbose)
return result
cls.__dict__[methodname] = method
Don't forget to return cls at the end; I've changed this to use a separate function to create a new scope to provide a new local name _async_method_name instead of a keyword parameter; this avoids difficulties with *args and explicit keyword arguments:
def process_asyncs(cls):
def create_method(async_method):
#class_or_instance
def newmethod(self, *args, **kwargs):
if 'verbose' in kwargs:
verbose = kwargs.pop('verbose')
else:
verbose = False
response = async_method(*args,**kwargs)
result = self._parse_result(response, verbose=verbose)
return result
return newmethod
methods = cls.__dict__.keys()
for k in methods:
methodname = k.replace("_async","")
if 'async' in k and methodname not in methods:
async_method = getattr(cls, k)
setattr(cls, methodname, create_method(async_method))
return cls

Python dictionary with memory of keys that were accessed?

I would like to create a data structure that behaves like a dictionary with one added functionality which is to keep track of which keys have been "consumed". Please note that I can't just pop the values as they are being reused.
The structure should support these three cases, i.e. mark the key as consumed when accessed as:
if key in d:
...
d[key]
d.get(key)
This is what I have written:
class DictWithMemory(dict):
def __init__(self, *args, **kwargs):
self.memory = set()
return super(DictWithMemory, self).__init__(*args, **kwargs)
def __getitem__(self, key):
self.memory.add(key)
return super(DictWithMemory, self).__getitem__(key)
def __contains__(self, key):
self.memory.add(key)
return super(DictWithMemory, self).__contains__(key)
def get(self, key, d=None):
self.memory.add(key)
return super(DictWithMemory, self).get(key, d)
def unused_keys(self):
"""
Returns the list of unused keys.
"""
return set(self.keys()).difference(self.memory)
As I am not very familiar with the internals of dict, is there a better way to achieve this result?
Here's a solution that abstracts everything away inside a metaclass. I'm not sure if this is really any more elegant, but it does provide some amount of encapsulation should you change your mind about how to store the used keys:
class KeyRememberer(type):
def __new__(meta, classname, bases, classDict):
cls = type.__new__(meta, classname, bases, classDict)
# Define init that creates the set of remembered keys
def __init__(self, *args, **kwargs):
self.memory = set()
return super(cls, self).__init__(*args, **kwargs)
cls.__init__ = __init__
# Decorator that stores a requested key in the cache
def remember(f):
def _(self, key, *args, **kwargs):
self.memory.add(key)
return f(self, key, *args, **kwargs)
return _
# Apply the decorator to each of the default implementations
for method_name in [ '__getitem__', '__contains__', 'get' ]:
m = getattr(cls, method_name)
setattr(cls, method_name, remember(m))
return cls
class DictWithMemory(dict):
# A metaclass that ensures the object
# has a set called 'memory' as an attribute,
# which is updated on each call to __getitem__,
# __contains__, or get.
__metaclass__ = KeyRememberer
def unused_keys(self):
"""
Returns the list of unused keys.
"""
print "Used", self.memory
return list(set(super(DictWithMemory,
self).keys()).difference(self.memory))

python: get constructor to return an existing object instead of a new one

I have a class that knows its existing instances. Sometimes I want the class constructor to return an existing object instead of creating a new one.
class X:
def __new__(cls, arg):
i = f(arg)
if i:
return X._registry[i]
else:
return object.__new__(cls)
# more stuff here (such as __init_, _registry, etc.)
Of course, if the first branch is executed, I don't need __init__, but it's invoked anyways. What's a good way to tell __init__ to do nothing?
I can probably just add some attribute to keep track of whether __init__ has run yet, but perhaps there's a better way?
In languages that support private constructors (C#, Dart, Scala, etc), factory methods provide a robust solution to this problem.
In Python, however, class constructors are always accessible, and so a user of your class may easily forget the factory method and call the constructor directly, producing duplicate copies of objects that should be unique.
A fool-proof solution to this problem can be achieved using a metaclass. The example below assumes that the zeroth constructor argument can be used to uniquely identify each instance:
class Unique(type):
def __call__(cls, *args, **kwargs):
if args[0] not in cls._cache:
self = cls.__new__(cls, *args, **kwargs)
cls.__init__(self, *args, **kwargs)
cls._cache[args[0]] = self
return cls._cache[args[0]]
def __init__(cls, name, bases, attributes):
super().__init__(name, bases, attributes)
cls._cache = {}
It can be used as follows:
class Country(metaclass=Unique):
def __init__(self, name: str, population: float, nationalDish: str):
self.name = name
self.population = population
self.nationalDish = nationalDish
placeA = Country("Netherlands", 16.8e6, "Stamppot")
placeB = Country("Yemen", 24.41e6, "Saltah")
placeC = Country("Netherlands", 11, "Children's tears")
print(placeA is placeB) # -> False
print(placeA is placeC) # -> True
print(placeC.nationalDish) # -> Stamppot
In summary, this approach is useful if you want to produce a set of unique objects at runtime (possibly using data in which entries may be repeated).
Use a factory, i.e.
_x_singleton = None
def XFactory():
global _x_singleton
if _x_singleton is None:
_x_singleton = X()
return _x_singleton
or use a "create" classmethod in your class that behaves the way you want it to,
class X(object):
instance = None
def __init__(self):
# ...
#classmethod
def create(cls):
if cls.instance is None:
cls.instance = cls()
return cls.instance
You might even consider making __init__ raise an exception if some condition isn't met (i.e. self.instance not being None)

Categories