I want a Python object that will monitor whether other objects have changed since the last time they were checked in, probably by storing their hash and comparing. It should behave sort of like this:
>>> library = Library()
>>> library.is_changed(object1)
False
>>> object1.change_somehow()
>>> library.is_changed(object1)
True
>>> library.is_changed(object1)
False
Do you know of anything like that?
Here is an implementation for you. Note that the objects you monitor must be hashable and picklable. Note also the use of a WeakKeyDictionary which means that the Monitor won't stop the monitored objects from being deleted.
from weakref import WeakKeyDictionary
from cPickle import dumps
class Monitor():
def __init__(self):
self.objects = WeakKeyDictionary()
def is_changed(self, obj):
current_pickle = dumps(obj, -1)
changed = False
if obj in self.objects:
changed = current_pickle != self.objects[obj]
self.objects[obj] = current_pickle
return changed
class MyObject():
def __init__(self):
self.i = 1
def change_somehow(self):
self.i += 1
If you test it like this
object1 = MyObject()
monitor = Monitor()
print monitor.is_changed(object1)
object1.change_somehow()
print monitor.is_changed(object1)
print monitor.is_changed(object1)
It prints
False
True
False
It sounds like you're describing the observer pattern. Check here:
http://rudd-o.com/projects/python-observable/
Twisted observable
http://radio.weblogs.com/0124960/2004/06/15.html#a30 - includes explanation
I stole the idea from Nick Craig-Wood, and changed it to a Mix-Class. For me, this is easier to use:
from cPickle import dumps
#base class for monitoring changes
class ChangesMonitor:
_cm_last_dump = None
def is_changed(self):
prev_dump = self._cm_last_dump
self._cm_last_dump = None
cur_dump = dumps(self, -1)
self._cm_last_dump = cur_dump
return (prev_dump is not None) and (prev_dump != cur_dump)
if __name__ == '__main__':
print 'Test Example'
#mix monitoring class with your regular class
class MyGreatObject(ChangesMonitor, object):
one_val = 5
second_val = 7
def some_changes(self):
self.second_val += 5
#and testing
my_obj = MyGreatObject()
print my_obj.is_changed() #False
print my_obj.is_changed() #False
my_obj.some_changes()
print my_obj.is_changed() #True
print my_obj.is_changed() #False
I haven't heard of anything like this... but you could write it pretty easily. Use a dictionary to store a name:hash pair for each object, then use the pickle module to save the dictionary.
This is based on Oduvan's answer, but implemented as a decorator instead of a mix-in class:
from cPickle import dumps
#decorator for monitoring changes
def ChangesMonitor(cls):
def is_changed(self):
prev_dump = self._cm_last_dump
self._cm_last_dump = None
cur_dump = dumps(self, -1)
self._cm_last_dump = cur_dump
return (prev_dump is not None) and (prev_dump != cur_dump)
cls.is_changed = is_changed
cls._cm_last_dump = None
return cls
print 'Test Example'
#decorate your common class
#ChangesMonitor
class MyGreatObject(object):
one_val = 5
second_val = 7
def some_changes(self):
self.second_val += 5
#and testing
my_obj = MyGreatObject()
print my_obj.is_changed() #False
print my_obj.is_changed() #False
my_obj.some_changes()
print my_obj.is_changed() #True
print my_obj.is_changed() #False
Note that #property could be added in front of the def is_changed(self): line such that print my_obj.is_changed() would become print my_obj.is_changed. This might be considered more pythonic...
Related
im writing a code like this
import random
class A:
def __init__(self):
self.att = self.set_att()
def set_att(self):
x = random.randint(0,10)
if x == 1:
return "att1"
elif x == 2:
return "att2"
# ... and so on
my question is: should i do it like this? or there is a better more pythonic way to do it.
i just want to call set_att in the init.
thank you
pycharm says i should use #staticmethod, but i dont understand is de difference
Here are two ideas I can think of:
import random
POSSIBLE_ATT_VALUES = [f'att{x}' for x in range(1, 11)]
class A:
def __init__(self):
# Produce a one-time random value.
self.att = random.choice(POSSIBLE_ATT_VALUES)
class B:
#property
def att(self):
# Every access returns a new random value.
return random.choice(POSSIBLE_ATT_VALUES)
>>> a = A()
>>> a.att
"att3" # Possible value.
>>> a.att
"att3" # The same value.
>>> b = B()
>>> b.att
"att5" # Possible value.
>>> b.att
"att1" # Possibly different value.
I have a class file. Let's call it "C1.py". The sample code looks like below.
class C1(object):
def __init__(self):
self.greeting = "Hello, world!"
def M1(ans):
if ans == 1 or ans == 2:
return True
else:
return False
Now, I have another python file in the same folder, which will access the class file shown above.
from trial import C1
def getAns(class1):
while True:
ans = input("Answer: ")
if class1.M1(ans):
return ans
break
sample = C1()
print sample.greeting
ans = getAns(sample)
print ans
When I run those files, sample.greeting prints fine. Everything is fine until when the execution reaches the line "ans = getAns(C1)", which gives the error "M1() takes exactly 1 argument (2 given)".
So, where in the code should I change so that I can call that method successfully?
Note here that the above code is only the abstraction of my whole program to highlight my problem. It sounds stupid with just the code above alone. So, please, please bear with that for me.
M1 is currently defined as a method of C1, as such it needs to have an additional self argument which would be the instance of the class. I.e
class C1(object):
def __init__(self):
self.greeting = "Hello, world!"
def M1(self, ans):
if ans == 1 or ans == 2:
return True
else:
return False
In other languages such as C++ or Java the presence of self (or this) is implicit, but in python it's explicit.
alternatively if you don't want or need M1 to access C1's state you could make M1 static via #staticmethod i.e.
class C1(object):
def __init__(self):
self.greeting = "Hello, world!"
#staticmethod
def M1(ans):
if ans == 1 or ans == 2:
return True
else:
return False
I am teaching myself Python and hit a roadblock with classes and modules.
The code below is something that you would probably never write, but I would like to just understand my error.
import random
class GetRandom:
def __init__(self):
self.data = ""
def ranNumber():
return random.random()
b = GetRandom()
bnum = b.ranNumber
print bnum
The output I am getting is:
<bound method GetRandom.ranNumber of <__main__.GetRandom instance at 0x7fe87818df38>>
I had expected a random number between 0 and 1. What am I doing wrong?
Thanks
There are two problems here:
You forgot to actually invoke GetRandom.ranNumber. Add () after it to do this:
bnum = b.ranNumber()
You need to make GetRandom.ranNumber accept the self argument that is passed implicitly when you invoke the method:
def ranNumber(self):
return random.random()
Once you address these issues, the code works as expected:
>>> import random
>>> class GetRandom:
... def __init__(self):
... self.data = ""
... def ranNumber(self):
... return random.random()
...
>>> b = GetRandom()
>>> bnum = b.ranNumber()
>>> print bnum
0.819458844177
>>>
I am maintaining a little library of useful functions for interacting with my company's APIs and I have come across (what I think is) a neat question that I can't find the answer to.
I frequently have to request large amounts of data from an API, so I do something like:
class Client(object):
def __init__(self):
self.data = []
def get_data(self, offset = 0):
done = False
while not done:
data = get_more_starting_at(offset)
self.data.extend(data)
offset += 1
if not data:
done = True
This works fine and allows me to restart the retrieval where I left off if something goes horribly wrong. However, since python functions are just regular objects, we can do stuff like:
def yo():
yo.hi = "yo!"
return None
and then we can interrogate yo about its properties later, like:
yo.hi => "yo!"
my question is: Can I rewrite my class-based example to pin the data to the function itself, without referring to the function by name. I know I can do this by:
def get_data(offset=0):
done = False
get_data.data = []
while not done:
data = get_more_starting_from(offset)
get_data.data.extend(data)
offset += 1
if not data:
done = True
return get_data.data
but I would like to do something like:
def get_data(offset=0):
done = False
self.data = [] # <===== this is the bit I can't figure out
while not done:
data = get_more_starting_from(offset)
self.data.extend(data) # <====== also this!
offset += 1
if not data:
done = True
return self.data # <======== want to refer to the "current" object
Is it possible to refer to the "current" object by anything other than its name?
Something like "this", "self", or "memememe!" is what I'm looking for.
I don't understand why you want to do this, but it's what a fixed point combinator allows you to do:
import functools
def Y(f):
#functools.wraps(f)
def Yf(*args):
return inner(*args)
inner = f(Yf)
return Yf
#Y
def get_data(f):
def inner_get_data(*args):
# This is your real get data function
# define it as normal
# but just refer to it as 'f' inside itself
print 'setting get_data.foo to', args
f.foo = args
return inner_get_data
get_data(1, 2, 3)
print get_data.foo
So you call get_data as normal, and it "magically" knows that f means itself.
You could do this, but (a) the data is not per-function-invocation, but per function (b) it's much easier to achieve this sort of thing with a class.
If you had to do it, you might do something like this:
def ybother(a,b,c,yrselflambda = lambda: ybother):
yrself = yrselflambda()
#other stuff
The lambda is necessary, because you need to delay evaluation of the term ybother until something has been bound to it.
Alternatively, and increasingly pointlessly:
from functools import partial
def ybother(a,b,c,yrself=None):
#whatever
yrself.data = [] # this will blow up if the default argument is used
#more stuff
bothered = partial(ybother, yrself=ybother)
Or:
def unbothered(a,b,c):
def inbothered(yrself):
#whatever
yrself.data = []
return inbothered, inbothered(inbothered)
This last version gives you a different function object each time, which you might like.
There are almost certainly introspective tricks to do this, but they are even less worthwhile.
Not sure what doing it like this gains you, but what about using a decorator.
import functools
def add_self(f):
#functools.wraps(f)
def wrapper(*args,**kwargs):
if not getattr(f, 'content', None):
f.content = []
return f(f, *args, **kwargs)
return wrapper
#add_self
def example(self, arg1):
self.content.append(arg1)
print self.content
example(1)
example(2)
example(3)
OUTPUT
[1]
[1, 2]
[1, 2, 3]
Apologies if this question has already been asked but I do not think I know the correct terminology to search for an appropriate solution through google.
I would like to select an object from a list of objects by the value of it's attribute, for example:
class Example():
def __init__(self):
self.pList = []
def addPerson(self,name,number):
self.pList.append(Person(self,name,number))
class Person():
def __init__(self,name,number):
self.nom = name
self.num = number
a = Example()
a.addPerson('dave',123)
a.addPerson('mike',345)
a.pList #.... somehow select dave by giving the value 123
in my case the number will always be unique
Thanks for the help
One option is to use the next() built-in:
dave = next(person for person in a.pList if person.num == 123)
This will throw StopIteration if nothing is found. You can use the two-argument form of next() to provide a default value for that case:
dave = next(
(person for person in a.pList if person.num == 123),
None,
)
A slightly more verbose alternative is a for loop:
for person in a.pList:
if person.num == 123:
break
else:
print "Not found."
person = None
dave = person
If those nom's are unique keys, and all you are ever going to do is access your persons using this unique key you should indeed rather use a dictionary.
However if you want to add more attributes over time and if you like to be able to retrieve one or more person by any of those attributes, you might want to go with a more complex solution:
class Example():
def __init__(self):
self.__pList = []
def addPerson(self,name,number):
self.__pList.append(Person(name,number))
def findPerson(self, **kwargs):
return next(self.__iterPerson(**kwargs))
def allPersons(self, **kwargs):
return list(self.__iterPerson(**kwargs))
def __iterPerson(self, **kwargs):
return (person for person in self.__pList if person.match(**kwargs))
class Person():
def __init__(self,name,number):
self.nom = name
self.num = number
def __repr__(self):
return "Person('%s', %d)" % (self.nom, self.num)
def match(self, **kwargs):
return all(getattr(self, key) == val for (key, val) in kwargs.items())
So let's assume we got one Mike and two Dave's
a = Example()
a.addPerson('dave',123)
a.addPerson('mike',345)
a.addPerson('dave',678)
Now you can find persons by number:
>>> a.findPerson(num=345)
Person('mike', 345)
Or by name:
>>> a.allPersons(nom='dave')
[Person('dave', 123), Person('dave', 678)]
Or both:
>>> a.findPerson(nom='dave', num=123)
Person('dave', 123)
The terminology you need is 'map' or 'dictionnary' : this will lead you to the right page in the python doc.
Extremely basic example:
>>> a = {123:'dave', 345:'mike'}
>>> a[123]
'dave'
The missing underscore makes plist a public property. I don't think that's what you want, since it does not encapsulate the functionality and you could call a.plist.append instead of a.addPerson.
class Example():
...
def filter(self, criteria):
for p in self.plist:
if criteria(p):
yield p
def getByNum(self, num):
return self.filter(lambda p: p.num == num)
dave = next(a.getByNum(123))
If the numbers are unique, you may also consider using a dictionary that maps from number to name or person instead of a list. But that's up to your implementation.