Python - Log the things a string has previously been - python

If this is my code:
x = 1
x = 2
x = 3
How can I “log” the things x has been and print them? If my explanation was dumb, then here’s what I expect:
>>> # Code to print the things x has been
1, 2, 3
>>>
How can I achieve this?

Since assignment overwrites the value of the object (in your example 'x'), it is not possible to do exactly what you want. However, you could create an object, of which the value can be changed and its history remembered. For example like this:
#!/usr/bin/env/python3
class ValueWithHistory():
def __init__(self):
self.history = []
self._value = None
#property
def value(self):
return self._value
#value.setter
def value(self, new_value):
self.history.append(new_value)
self._value = new_value
def get_history(self):
return self.history
def clear_history(self):
self.history.clear()
def main():
test = ValueWithHistory()
test.value = 1
print(test.value)
test.value = 2
print(test.value)
test.value = 3
print(test.value)
print(test.get_history())
if __name__ == '__main__':
main()
This prints:
1
2
3
[1, 2, 3]
Of course, you could also use a set instead of a list to only remember each unique value once, for example.

You can order a second thread to observe the string and print the changes:
from threading import Thread
def string_watcher():
global my_string
global log
temp = ''
while True:
if my_string != temp:
log.append(my_string)
temp = my_string
t = Thread(target=string_watcher, daemon=True)
t.start()
This checks weather the string „my_string“ was manipulated and appends it to the list „log“, if it has been changed. With this you should be able to perform
Print(log)
At any moment of the runtime

Related

How to create a method that accepts an arbitrary instance variable and modify it in place?

I haven't used classes in for a while and I am a bit rusty.
I would like to create a method that can modify any variable from an instance:
class classTest:
def __init__(self):
self.a=1
self.b=0
def addN(self,x,n):
x+=n
test = classTest()
print(test.a) # prints 1
print(test.b) # prints 0
test.addN(test.a,2)
test.addN(test.b,5)
print(test.a) # prints 1, but I want it to print 3
print(test.b) # prints 0, but I want it to print 5
So is there an easy way to do this?
Thanks
Here's a way to achieve what you (maybe?) want. I don't think it's very useful, but there's a way to do it.
class classTest:
def __init__(self):
self.a=1
self.b=0
def addN(self, var_name, n):
val = getattr(self, var_name)
setattr(self, var_name, val + n)
test = classTest()
print(test.a) # prints 1
print(test.b) # prints 0
test.addN("a", 2)
test.addN("b", 5)
print(test.a) # prints 3
print(test.b) # 5
Depending on your needs, the most straightforward solution to me looks:
test.a = test.addN(test.a, 3)
For this you only have to change the addN method to:
def addN(self, x, n):
return x+n
...
print(test.a) # returns 4

Dectecting a value change in Python loop

Here is a pattern I often use:
last_value = None
while <some_condition>:
<get current_value from somewhere>
if last_value != current_value:
<do something>
last_value = current_value
One application example would be to print headings in a report when, say, a person's last name changes.
The whole last_value/current_value thing has always seemed clumsy to me. Is there a better way to code this in Python?
I agree that your pattern makes a lot of sense.
But for fun, you could do something like:
class ValueCache(object):
def __init__(self, val=None):
self.val = val
def update(self, new):
if self.val == new:
return False
else:
self.val = new
return True
Then your loop would look like:
val = ValueCache()
while <some_condition>:
if val.update(<get current_value from somewhere>):
<do something>
For example
import time
t = ValueCache()
while True:
if t.update(time.time()):
print("Cache Updated!")
If you changed time.time() to some static object like "Foo", you'd see that "Cache Updated!" would only appear once (when it is initially set from None to "Foo").
Obligatory realistic programmer's note: Don't do this. I can't easily find a good reason to do this in practice. It not only adds to the line count but to the complexity.
(Inspired by Alex Martelli's Assign and Test Recipe)
I think the pattern is very clear, but you can use a generator function to hide the last_value/current_value thing.
def value_change_iterator(iterable):
last_x = None
for x in iterable:
if x != last_x:
yield x
last_x = x
for x in value_change_iterator([1, 1, 2, 2, 3, 3, 4]):
print(x)
prints
1
2
3
4
Another alternative inspired by #jedwards' answer inspired by Alex Martelli's recipe (this one keeps around the current and last values, and lets you use None as an initial value if you're so inclined, also changes the semantics from semantics I don't particularly like to other semantics I'm not sure I much like either):
class undefined:
pass
class ValueCache:
def __init__(self, value=undefined):
self.current_value = value
self.last_value = undefined
self._is_changed = False
#property
def is_changed(self):
is_changed = self._is_changed
self._is_changed = False
return is_changed
def update(self, new_value):
self._is_changed = (new_value != self.current_value)
if self._is_changed:
self.last_value = self.current_value
self.current_value = new_value
Example:
>>> v = ValueCache()
>>> v.update(1)
>>> v.is_changed
True
>>> v.is_changed is False
False
>>> v.update(2)
>>> v.is_changed
True
>>> v.is_changed
False
Or in your case:
t = ValueCache()
while True:
t.update(time.time())
if t.is_changed:
print("Cache updated!")
Same obligatory realistic programmer's note applies.

OOP python program

from collections import Counter
class Runlength:
def __init__(self):
self.str = 0
def returner(self,str):
self.str = str
self.__str = ','.join(str(n) for n in self.__str)
self.__str = self.__str[::-1]
self.__str = self.__str.replace(',', '')
return self.__str
def final(self,num):
self.num = num
k = []
c = Counter(self.num).most_common()
for x in c:
k += x
return k
math = Runlength()
def Main():
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
The program takes a word as input and gives the occurrence of each repeating character and
outputs that number along with a single character of the repeating sequence.
I cant figure it out, why this doesn't work. I get this error:
NameError: global name 'returner' is not defined
The problem is that in Main() you are not accessing the global (outside the scope of the Main() method) math variable. Instead try initializing your math inside the Main() function
This lets the method know that it should use the global math variable instead of trying to look for a non-existent local one.
I got this error with your code:
self.__str = ','.join(str(n) for n in self.__str)
AttributeError: Runlength instance has no attribute '_Runlength__str'
Maybe you mean:
self.__str = ','.join(str(n) for n in self.str
And choose input argument for returner() method as str_ not str, cause str -- is the name of python built-in type, so better to not choose variable names with built-in type names.
So after this changes I got this output:
['a', 2, 'c', 2, 'b', 2]
So my python version is 2.7.3 and error you've got does not appear with my python version.
What python version you use to compile your code? If this python3 it works fine too.So try this code, it works fine for me:
from collections import Counter
class Runlength:
def __init__(self):
self.str = 0
def returner(self,str_):
self.string = str_
self.__str = ','.join(str(n) for n in self.string)
self.__str = self.__str[::-1]
self.__str = self.__str.replace(',', '')
return self.__str
def final(self,num):
self.num = num
k = []
c = Counter(self.num).most_common()
for x in c:
k += x
return k
math = Runlength()
def Main():
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
def Main():
math = Runlength()
a = "aabbcc"
b = math.returner(a)
c = math.final(b)
print(c)
Main()
This should work fine..
But I observed that the object can even be accessed if it is not declared as global. Is their any explantion for it in the above scenario?

Why does my synchronous job code does not work

I have wrote below code segment to initiate function in every 2 seconds. But, it seems it is not work. Why?
from threading import Timer
class A :
value = None
def AX(self):
value = 12
obj = B()
Timer(1,obj.BY, [self.value]).start()
class B:
def BY(self,value):
print "refreshed :", value
if __name__=='__main__':
obj = A()
obj.AX()
You also need to set value on self, or use the local variable as the argument:
def AX(self):
self.value = 12
obj = B()
Timer(1, obj.BY, [self.value]).start()
or:
def AX(self):
value = 12
obj = B()
Timer(1, obj.BY, [value]).start()
This prints:
>>> A().AX()
>>> refreshed : 12
Note that the Timer() instance will only call obj.BY once, it doesn't repeatedly call the function.
You need to reset the timer every time BY is called to do that:
class B:
def BY(self,value):
print "refreshed :", value
Timer(2, self.BY, [value]).start()
which will create a loop. If you need that loop to terminate at some point, you'll need to test for that condition separately, in BY:
class B:
somecondition = False
def BY(self,value):
print "refreshed :", value
if self.somecondition:
return # do not set the timer again
Timer(2, self.BY, [value]).start()

List callbacks?

Is there any way to make a list call a function every time the list is modified?
For example:
>>>l = [1, 2, 3]
>>>def callback():
print "list changed"
>>>apply_callback(l, callback) # Possible?
>>>l.append(4)
list changed
>>>l[0] = 5
list changed
>>>l.pop(0)
list changed
5
Borrowing from the suggestion by #sr2222, here's my attempt. (I'll use a decorator without the syntactic sugar):
import sys
_pyversion = sys.version_info[0]
def callback_method(func):
def notify(self,*args,**kwargs):
for _,callback in self._callbacks:
callback()
return func(self,*args,**kwargs)
return notify
class NotifyList(list):
extend = callback_method(list.extend)
append = callback_method(list.append)
remove = callback_method(list.remove)
pop = callback_method(list.pop)
__delitem__ = callback_method(list.__delitem__)
__setitem__ = callback_method(list.__setitem__)
__iadd__ = callback_method(list.__iadd__)
__imul__ = callback_method(list.__imul__)
#Take care to return a new NotifyList if we slice it.
if _pyversion < 3:
__setslice__ = callback_method(list.__setslice__)
__delslice__ = callback_method(list.__delslice__)
def __getslice__(self,*args):
return self.__class__(list.__getslice__(self,*args))
def __getitem__(self,item):
if isinstance(item,slice):
return self.__class__(list.__getitem__(self,item))
else:
return list.__getitem__(self,item)
def __init__(self,*args):
list.__init__(self,*args)
self._callbacks = []
self._callback_cntr = 0
def register_callback(self,cb):
self._callbacks.append((self._callback_cntr,cb))
self._callback_cntr += 1
return self._callback_cntr - 1
def unregister_callback(self,cbid):
for idx,(i,cb) in enumerate(self._callbacks):
if i == cbid:
self._callbacks.pop(idx)
return cb
else:
return None
if __name__ == '__main__':
A = NotifyList(range(10))
def cb():
print ("Modify!")
#register a callback
cbid = A.register_callback(cb)
A.append('Foo')
A += [1,2,3]
A *= 3
A[1:2] = [5]
del A[1:2]
#Add another callback. They'll be called in order (oldest first)
def cb2():
print ("Modify2")
A.register_callback(cb2)
print ("-"*80)
A[5] = 'baz'
print ("-"*80)
#unregister the first callback
A.unregister_callback(cbid)
A[5] = 'qux'
print ("-"*80)
print (A)
print (type(A[1:3]))
print (type(A[1:3:2]))
print (type(A[5]))
The great thing about this is if you realize you forgot to consider a particular method, it's just 1 line of code to add it. (For example, I forgot __iadd__ and __imul__ until just now :)
EDIT
I've updated the code slightly to be py2k and py3k compatible. Additionally, slicing creates a new object of the same type as the parent. Please feel free to continue poking holes in this recipe so I can make it better. This actually seems like a pretty neat thing to have on hand ...
You'd have to subclass list and modify __setitem__.
class NotifyingList(list):
def __init__(self, *args, **kwargs):
self.on_change_callbacks = []
def __setitem__(self, index, value):
for callback in self.on_change_callbacks:
callback(self, index, value)
super(NotifyingList, self).__setitem__(name, index)
notifying_list = NotifyingList()
def print_change(list_, index, value):
print 'Changing index %d to %s' % (index, value)
notifying_list.on_change_callbacks.append(print_change)
As noted in comments, it's more than just __setitem__.
You might even be better served by building an object that implements the list interface and dynamically adds and removes descriptors to and from itself in place of the normal list machinery. Then you can reduce your callback calls to just the descriptor's __get__, __set__, and __delete__.
I'm almost certain this can't be done with the standard list.
I think the cleanest way would be to write your own class to do this (perhaps inheriting from list).

Categories