Python: how to count the access of an instance variable - python

I have a python class as below.
class A(object):
def __init__(self, logger):
self.b = B()
self.logger = logger
def meth1(self):
self.b.mymethod1()
def meth2(self):
self.meth1()
self.b.mymethod2()
.........
class B(object):
---------
How can I count how many time I accessed self.b variable on the invocation of meth2() or any method of class A. Is there any way, I can log the usage of self.b variable?

make 'b' a property and and increase the counter corresponding to be in the setter.
#property
def b(self):
self.b_counter += 1
return self._b
and in your class replace b with _b

If you don't want to make a property, you can log the read/write access using __getattribute__ (not __getattr__ since b exists and would not be called) and __setattr__:
class A(object):
def __init__(self):
# initialize counters first !
self.b_read_counter = 0
self.b_write_counter = 0
# initialize b
self.b = 12
def __getattribute__(self,attrib):
# log read usage
if attrib=="b":
self.b_read_counter+=1
# now return b value
return object.__getattribute__(self, attrib)
def __setattr__(self,attrib,value):
if attrib=="b":
self.b_write_counter+=1
return object.__setattr__(self, attrib,value)
a = A()
a.b = 23 # second write access (first is in the init method)
if a.b == 34: # first read access
print("OK")
if a.b == 34:
print("OK")
if a.b == 34: # third read access
print("OK")
print(a.b_read_counter)
print(a.b_write_counter)
result:
3
2

You can use descriptors for this or just make a property which is basically is descriptor.
class A(object):
def __init__(self, logger):
self._b = B()
self._b_counter = 0
self.logger = logger
#property
def b(self):
self._b_counter += 1
return self._b
def meth1(self):
self.b.mymethod1()
def meth2(self):
self.meth1()
self.b.mymethod2()

You can use property, somtehing like:
class A(object):
def __init__(self, logger):
self._b = B()
self._count = 0
self.logger = logger
#property
def b(self):
self._count += 1
return self._b
...
...

Related

how to modify parent class variable with the child class and use in another child class in python

class A(object):
__A = None
def get_a(self):
return self.__A
def set_a(self, value):
self.__A = value
class B(A):
def method_b(self, value):
self.set_a(value)
class C(A):
def method_c(self)
self.get_a()
Someone can to explain me how can i to catch installed value in method_b inside my 'C' class method?
P.S. In this variant i just getting nothing.
Python isn't Java; you don't need setters & getters here: just access the attributes directly.
There are three problems with your code.
C.method_c() has no return statement, so it returns None.
You are using __ name mangling when that's exactly what you don't want.
In A.set_a() you want to set a class attribute, but your assignment instead creates an instance attribute which shadows the class attribute.
Here's a repaired version.
class A(object):
_A = 'nothing'
def get_a(self):
return self._A
def set_a(self, value):
A._A = value
class B(A):
def method_b(self, value):
self.set_a(value)
class C(A):
def method_c(self):
return self.get_a()
b = B()
c = C()
print(c.method_c())
b.method_b(13)
print(c.method_c())
output
nothing
13
Here's a slightly more Pythonic version:
class A(object):
_A = 'nothing'
class B(A):
def method_b(self, value):
A._A = value
class C(A):
pass
b = B()
c = C()
print(c._A)
b.method_b(13)
print(c._A)

How to memoize a property in Python?

Consider the following minimal example:
class Foo(object):
def __init__(self):
self.b = self.a = 1
#property
def sum(self):
print 'Recalculating sum'
return self.a + self.b
foo = Foo()
print foo.sum
print foo.sum # Prints 'Recalculating sum' even though neither a or b has changed since previous call
foo.a = 2
print foo.sum # a has been changed to 2 so recalculation is necessary
I would like to memoize sum such that if self.a and self.b doesn't change, then we don't need to keep recalculating the property.
The property should only be recalculated when either self.a or self.b has changed -- is there an simple way to do this?
python3:
from functools import lru_cache as memoized
#property
#memoized(maxsize=1)
def sum(self):
return self.a + self.b
python 3.8
from functools import cached_property
#cached_property
def sum(self):
return self.a + self.b
Use properties for a and b too and clear up your cache in the setters:
class Foo(object):
def __init__(self):
self.a = 1
self.b = 1
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
self._clearsum()
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
self._clearsum()
def _clearsum(self):
self._sum = None
#property
def sum(self):
if self._sum is None:
self._sum = self.a + self.b
return self._sum
Or if you want something a bit more generic, you can check this too:
Storing calculated values in an object
Edit : someone recently suggested adding self._sum = None in __init__ to "avoid an error when accessing sum", but that's actually not necessary - __init__ invokes a.setter, which invokes _clearsum, which sets the _sum attribute, so it's garanteed self._sum will be created whatever.
there is a module that does this. Pypi link here: https://pypi.org/project/memoized-property/
For the above code I have this with using the module:
In [2]: from memoized_property import memoized_property
In [3]: class test():
...: def __init__(self):
...: self.a = 0
...: self.b = 0
...: #memoized_property
...: def sum(self):
...: print('calculating...')
...: return self.a + self.b
In [4]: t=test()
calculating...
In [5]: t.sum
Out[5]: 0
In [7]: t.a=5
In [8]: t.sum
Out[8]: 0

Newbie Debugging a Python Code

I want local variable of a class to be changed by other class. My script is similar to the following :
import datetime
b = []
class P:
def __init__(self):
self.count = 1
self.create()
def create(self):
global b
a = C(self.count)
for i in range(10):
a.print_name()
print b
class C:
def __init__(self, *data):
self.test = data[0]
#staticmethod
def print_name():
global b
b.append(datetime.datetime.now())
o = P()
How to avoid use of a global variable . On web i found use of "super " can resolve the issue . Please help in this regard .
Make C constructor to accept P instance. And call the method of P to append item to instance attribute of P object.
class P:
def __init__(self):
self.count = 1
self.items = []
self.create()
def append(self, item):
self.items.append(item)
def create(self):
a = C(self, self.count) # <-- Pass P instance (self)
for i in range(10):
a.print_name()
print self.items
class C:
def __init__(self, p_instance, *data):
self.p_instance = p_instance # <-- Save P instance to use later
self.test = data[0]
def print_name(self):
self.p_instance.append(datetime.datetime.now()) # <-- Call p instance method
You are probably looking for a class attribute. If you add b as an attribute to the C class, it can be accessed as C.b and C().b, i.e. from a reference to the class or any instance.
class C(object): # classes in py2 should inherit from object!
b = [] # b inside C definition
#classmethod
def print_name(cls):
cls.b.append(datetime.datetime.now())
class P(object):
def __init__(self):
self.count = 1
self.create()
def create(self):
a = C(self.count)
for i in range(10):
a.print_name()
print C.b # or a.b
Of course, you can also place b on P. In this case, do
def print_name():
P.b.append(datetime.datetime.now())

Python : How to "merge" two class

I want to add some attributes and methods into various class. The methods and attributes that I have to add are the same but not the class to assign them, so I want to construct a class who assign new methods and attributes for a class given in argument.
I try this but it's not working:
(I know that is a very wrong way to try to assign something to self, it's just to show what I want to do)
class A:
def __init__(self):
self.a = 'a'
def getattA(self):
return self.a
class B:
def __init__(self, parent) :
self = parent
# This is working :
print self.getattA()
def getattB(self):
return self.getattA()
insta = A()
instb = B(insta)
# This is not working :
print instb.getattB()
The result is :
a
Traceback (most recent call last):
File "D:\Documents and settings\Bureau\merge.py", line 22, in <module>
print instb.getattB()
File "D:\Documents and settings\Bureau\merge.py", line 16, in getattB
return self.getattA()
AttributeError: B instance has no attribute 'getattA'
And I expected to got 'a' for the call of instb.gettattB()
To resume I want to inherit class B from class A giving class A in argument of class B because my class B will be a subclass of various class, not always A.
The Best answer is in the comments, it was useful for me so I decided to show it in an answer (thank to sr2222):
The way to dynamicaly declare inherance in Python is the type() built-in function.
For my example :
class A(object) :
def __init__(self, args):
self.a = 'a'
self.args = args
def getattA(self):
return self.a, self.args
class B(object) :
b = 'b'
def __init__(self, args) :
self.b_init = args
def getattB(self):
return self.b
C = type('C', (A,B), dict(c='c'))
instc = C('args')
print 'attributes :', instc.a, instc.args, instc.b, instc.c
print 'methodes :', instc.getattA(), instc.getattB()
print instc.b_init
The code return :
attributes : a args b c
methodes : ('a', 'args') b
Traceback (most recent call last):
File "D:\Documents and settings\Bureau\merge2.py", line 24, in <module>
print instc.b_init
AttributeError: 'C' object has no attribute 'b_init'
My class C inerhite attributes and methods of class A and class B and we add c attribute. With the instanciation of C (instc = C('args')) The init for A is call but not for B.
Very useful for me because I have to add some attributes and methodes (the same) on different class.
I was having trouble with calling different constructors, using super doesn't necessarily make sense in a case like this, I opted to inherit and call each constructor on the current object manually:
class Foo(object):
def __init__(self, foonum):
super(Foo, self).__init__()
self.foonum = foonum
class Bar(object):
def __init__(self, barnum):
super(Bar, self).__init__()
self.barnum = barnum
class DiamondProblem(Foo, Bar):
# Arg order don't matter, since we call the `__init__`'s ourself.
def __init__(self, barnum, mynum, foonum):
Foo.__init__(self, foonum)
Bar.__init__(self, barnum)
self.mynum = mynum
How about this?
class A:
def __init__(self):
self.a = 'a'
def getatt(self):
return self.a
class B:
def __init__(self, parent) :
self.parent = parent
def __getattr__(self, attr):
return getattr(self.parent, attr)
def getattB(self):
return self.parent.getatt()
insta = A()
instb = B(insta)
print instb.getattB()
print instb.getatt()
But method in class A can not access attr in class B.
Another way:
import functools
class A:
def __init__(self):
self.a = 'a'
def getatt(self):
return self.a
class B:
def __init__(self, parent):
for attr, val in parent.__dict__.iteritems():
if attr.startswith("__"): continue
self.__dict__[attr] = val
for attr, val in parent.__class__.__dict__.iteritems():
if attr.startswith("__"): continue
if not callable(val): continue
self.__dict__[attr] = functools.partial(val, self)
def getattB(self):
return self.getatt()
insta = A()
instb = B(insta)
print instb.__dict__
print instb.getattB()
print instb.getatt()
Slow with init but call fast.
Since B is not a subclass of A, there is no path in B to getatt() in A
I guess i have a easier method
class fruit1:
def __init__(self):
self.name = "apple"
self.color = "blue"
class fruit2:
def __init__(self):
self.name = "banana"
self.size = 100
def merge(ob1, ob2):
ob1.__dict__.update(ob2.__dict__)
return ob1
f1 = fruit1()
f2 = fruit2()
fruit = merge(f1, f2)
print("name:",fruit.name," color:",fruit.color, " size:",fruit.size)
#output: name: banana color: blue size: 100
I'm not certain what you are trying to do, but the code below is giving my the output I think you are expecting. notice:
a is initialized outside the constructor in A
B is declared as a subclass of A
Code:
class A:
a='' #Initialize a
def __init__(self):
self.a = 'a'
def getatt(self):
return self.a
class B(A): #Declare B as subclass
def __init__(self, parent) :
self = parent
print self.getatt()
def getattB(self):
return self.getatt()
insta = A()
instb = B(insta)
print instb.getattB()
Helper function below conducts the merge of the dataclass instances, the attributes orders is derived from *args order:
from dataclasses import dataclass
#dataclass
class A:
foo: str
bar: str
def merge_dataclasses(*args):
if len({e.__class__.__name__ for e in args}) > 1:
raise NotImplementedError('Merge of non-homogeneous entries no allowed.')
data = {}
for entry in args[::-1]:
data.update(vars(entry))
return entry.__class__(**data)
print(merge_dataclasses(A(foo='f', bar='bar'), A(foo='b_foo', bar='b_bar')))
One easy way to merge two or more classes is through the tool set dyndesign:
from dyndesign import mergeclasses
class Base:
def __init__(self, init_value):
self.param = init_value
def m1(self):
print(f"Method `m1` of class `Base`, and {self.param=}")
def m2(self):
print(f"Method `m2` of class `Base`")
class Ext:
def m1(self):
print(f"Method `m1` of class `Ext`, and {self.param=}")
MergedClass = mergeclasses(Base, Ext)
merged_instance = MergedClass("INITIAL VALUE")
merged_instance.m1()
# Method `m1` of class `Ext`, and self.param='INITIAL VALUE'
merged_instance.m2()
# Method `m2` of class `Base`
Emphasizing ThorSummoner's's answer and Hong's comment; this method appears to be cleaner than the excepted answer. Notice Hong's use of super().init(self) in all but the last object added to the merge class.
class Foo(object):
def __init__(self, foonum):
super(Foo, self).__init__(self)
self.foonum = foonum
class Bar(object):
def __init__(self, barnum):
super(Bar, self).__init__(self)
self.barnum = barnum
class Oops(object):
def __init__(self, oopsnum):
super(Oops, self).__init__()
self.oopsnum = oopsnum
class DiamondProblem(Foo, Bar, Oops):
def __init__(self, mynum, foonum, barnum, oopsnum):
Foo.__init__(self, foonum)
Bar.__init__(self, barnum)
Oops.__init__(self, oopsnum)
self.mynum = mynum
def main():
dia = DiamondProblem(1, 10, 20, 30)
print(f"mynum: {dia.mynum}")
print(f"foonum: {dia.foonum}")
print(f"barnum: {dia.barnum}")
print(f"oopsnum: {dia.oopsnum}")

How to implement this mechanism:

i want to implement a dynamic relation mechanism with python something like:
a:=10
b:=30
c:=a+b
print c
a+=20
print c
output:
40
60
c is always result of a+b.
so if a or b change, then c automatically updates value. i write a code in C# and do this by set and get mechanism. now want to translate it to python code for using in another program (FontLab Studio 5). I'm not so familiar with Python. does he have a get,set feature like C#? if not ho to implement one?
This is probably overkill, but it illustrates how you should create getters/setters in Python and achieve the functionality you want:
class Calc(object):
def __init__(self, a = 0, b = 0):
self._a = a
self._b = b
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
#property
def c(self):
return self._a + self._b
def __str__(self):
return str(self.c)
calc = Calc()
calc.a = 1
calc.b = 2
print calc.c
calc.a += 10
print calc.c
If you don't want to make a and b a property, the code can be simplified:
class Calc(object):
def __init__(self, a = 0, b = 0):
self.a = a
self.b = b
#property
def c(self):
return self.a + self.b
def __str__(self):
return str(self.c)
In your situation, c actually is a function which must be called.
You could use something like this:
a = 10
b = 30
c = lambda: a + b
print c()
a += 20
print c()
If you dislike that the method call is made explicit for c, you could use a general Calc object, which hides this implementation:
class Calc(object):
def __init__(self):
object.__setattr__(self, '_params', dict())
def __getattr__(self, name):
param = self._params[name]
if callable(param):
return param()
else:
return param
def __setattr__(self, name, value):
self._params[name] = value
def __delattr__(self, name):
del self._params[name]
And then you could do:
c = Calc()
c.a = 10
c.b = 30
c.c = lambda: c.a + c.b
print c.c
c.a += 20
print c.c
New-style Python classes support properties.
something like this:
class C:
def __init__(self):
self.x = 0
self.y = 0
def get(self):
return self.x + self.y
def __str__(self):
return self.__unicode__()
def __unicode__(self):
return str(self.get())
c = C()
c.x = 1
print c
c.y =2
print c
With new style classes and annotations you can probably make it better.

Categories

Resources