Count of full objects and subobjects in Python - python

I would like to maintain count of A and B objects, B is subclassed from A. So the counts should be specific to A and B. For example, if I create 3 A objects and 2 B objects, by virtue of constructor call, count for A becomes 3+2=5, but I would like to keep as 3 (not when used as a subobject as part of B). Please comment on the following code snippet:
class A:
acount = 0 # class variable
def __init__(self, isFullA = True):
if (isFullA):
self.iamFullA = True
A.acount += 1
else:
self.iamFullA = False
def __del__(self):
if (self.iamFullA):
A.acount -= 1
class B(A):
bcount = 0 # class variable
def __init__(self, isFullB = True):
A.__init__(self,False)
if (isFullB):
self.iamFullB = True
B.bcount += 1
else:
self.iamFullB = False
def __del__(self):
if (self.iamFullB):
B.bcount -= 1
#MAIN
L=[]
for i in range(3):
L.append(A())
for i in range(2):
L.append(B())
print "A.acount = " + str(A.acount)
print "B.bcount = " + str(B.bcount)
The output is:
A.acount = 3
B.bcount = 2

You're making it way to complicated - all you need is to have a distinct count class attribute for each class:
class A(object):
_counter = 0
#classmethod
def _inc(cls):
cls._counter += 1
#classmethod
def _dec(cls):
cls._counter -= 1
#classmethod
def get_count(cls):
return cls._counter
def __init__(self):
self._inc()
def __del__(self):
self._dec()
class B(A):
_counter = 0
def __init__(self, wot):
super(B, self).__init__()
self.wot = wot
L=[]
for i in range(3):
L.append(A())
for i in range(2):
L.append(B(i))
print "A.count = {}".format(A.get_count())
print "B.count = {}".format(B.get_count())
Note that I used classmethods to ensure we're accessing the class attribute, as self._counter += 1 in the __init__ would create an instance attribute. You could also get the right behaviour using type(self)._counter += 1 (or self.__class__._counter += 1) but that's a bit ugly imho.
If this is for an API other devs will build upon, you may want to use a custom metaclass to ensure each subclass has it's own _counter, ie:
class CounterType(type):
def __new__(meta, name, bases, attribs):
if "_counter" not in attribs:
attribs["_counter"] = 0
return type.__new__(meta, name, bases, attribs)
class CounterBase(object):
__metaclass__ = CounterType
#classmethod
def _inc(cls):
cls._counter += 1
#classmethod
def _dec(cls):
cls._counter -= 1
#classmethod
def get_count(cls):
return cls._counter
def __init__(self):
self._inc()
def __del__(self):
self._dec()
class A(CounterBase):
pass
class B(A):
def __init__(self, wot):
super(B, self).__init__()
self.wot = wot
L=[]
for i in range(3):
L.append(A())
for i in range(2):
L.append(B(i))
print "A.count = {}".format(A.get_count())
print "B.count = {}".format(B.get_count())

Related

trying to change the value of attribute of one class from another class but one value changes other value remains the same

All I want to do is make the value of 'a' from second class negative each time change function is called from First class. I don't want to directly access the 'a' variable through the instance of second class. I want to change the value of 'a' only through a different class.
class First():
def __init__(self):
self.value = 1
self.s = Second(self)
def change(self):
trigger = input("continue? y/n?")
self.value *= -1
print(self.value)
print(self.s.a)
if trigger == "y":
self.change()
class Second():
def __init__(self, num):
self.a = num.value
f = First()
f.change()
Make the Second class's a attribute a property that relies on a module level variable that the First class can change without going through the Second class.
class Thing:
def __init__(self,val):
self.val = val
class First():
def __init__(self):
self.value = 1
self.s = Second()
def change(self):
trigger = input("continue? y/n?")
self.value *= -1
#print(self.value)
#print(self.s.a)
if trigger == "y":
#self.change()
num.val = -num.val
class Second():
def __init__(self):
#self.a = num.value
pass
#property
def a(self):
return num.val
#a.setter
def a(self,val):
num.val = val
num = Thing(2)
f = First()
print(f'f.s.a = {f.s.a}')
f.change()
print(f'f.s.a = {f.s.a}')
That solution would make all Second instances have the same num, therfore the same a.
Maybe you meant for each Second instance to have its own unique a.
class Thing:
def __init__(self,val):
self.val = val
d = {}
class First():
def __init__(self):
self.value = 1
self.s = Second(2)
def change(self):
trigger = input("continue? y/n?")
self.value *= -1
#print(self.value)
#print(self.s.a)
if trigger == "y":
#self.change()
d[self.s].val = d[self.s].val * -1
class Second():
def __init__(self,val):
num = Thing(val)
d[self] = num
#property
def a(self):
return d[self].val
#a.setter
def a(self,val):
d[self].val = val
f = First()
print(f'f.s.a = {f.s.a}')
f.change()
print(f'f.s.a = {f.s.a}')

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())

Filter attributes of a class based on the type

I would like to filter the attributes of an object of a class based on their types.
The answer will be something around inspect, “list comprehensions”, type(), __dict__ and dict() but I don't get it working.
class A():
def __init__(self, value):
self.x = value
def __str__(self):
return "value = {}\n".format(self.x)
class T():
def __init__(self):
self.a1 = A(1)
self.a2 = A(2)
self.b = 4
t = T()
And I would like to print only the attributes of the type A in the class T
class T():
def __init__(self):
self.a1 = A(1)
self.a2 = A(2)
self.b = 4
def __str__(self):
ret = ""
for i in [*magic*]:
ret += str(i)
return ret
Output should be something like:
value = 10
value = 15
You can use vars(self) to get a dictionary of the local attributes, then just test the values with isinstance():
def __str__(self):
ret = ""
for i in vars(self).values():
if isinstance(i, A):
ret += str(i)
return ret
vars() essentially returns self.__dict__ here but is cleaner.
Turning this into a list comprehension for one-liner appeal:
def __str__(self):
return ''.join([i for i in vars(self).values() if isinstance(i, A)])

How can I ignore a member when serializing an object with PyYAML?

How can ignore the member Trivial._ignore when serializing this object?
import yaml
class Trivial(yaml.YAMLObject):
yaml_tag = u'!Trivial'
def __init__(self):
self.a = 1
self.b = 2
self._ignore = 3
t = Trivial()
print(yaml.dump(t))
prints
!Trivial
_ignore: 3
a: 1
b: 2
def my_yaml_dump(yaml_obj):
my_ob = deepcopy(yaml_obj)
for item in dir(my_ob):
if item.startswith("_") and not item.startswith("__"):
del my_ob.__dict__[item]
return yaml.dump(my_ob)
something like this would ignore anything with a single (leading) underscore
I think this is what you want
class Trivial(yaml.YAMLObject):
yaml_tag = u'!Trivial'
def __init__(self):
self.a = 1
self.b = 2
self._ignore = 3
#classmethod
def to_yaml(cls, dumper, data):
# ...
my_ob = deepcopy(data)
for item in dir(my_ob):
if item.startswith("_") and not item.startswith("__"):
del my_ob.__dict__[item]
return dumper.represent_yaml_object(cls.yaml_tag, my_ob, cls,
flow_style=cls.yaml_flow_style)
although a better method (stylistically)
class SecretYamlObject(yaml.YAMLObject):
hidden_fields = []
#classmethod
def to_yaml(cls,dumper,data):
new_data = deepcopy(data)
for item in cls.hidden_fields:
del new_data.__dict__[item]
return dumper.represent_yaml_object(cls.yaml_tag, new_data, cls,
flow_style=cls.yaml_flow_style)
class Trivial(SecretYamlObject):
hidden_fields = ["_ignore"]
yaml_tag = u'!Trivial'
def __init__(self):
self.a = 1
self.b = 2
self._ignore = 3
print yaml.dump(Trivial())
this is adhering to pythons mantra of explicit is better than implicit

Python iterator as class member?

I have a scheme like:
class Base(object):
param, d=0, 1
def get_all(self):
while True:
a = self.get_xxx(param)
if not a:
break
handle(a)
param += d
class A(Base):
def get_xxx(param):
return some_method(param)
class B(Base):
def get_xxx(param):
return other_method(param)
Then, I was informed that for B, after each get_xxx param should be a+1 instead of param+d. That means I need to extract the param change logic at the end of get_all. I came up with a scheme using iterator:
class Base(object):
def get_all(self):
get_xxx = self.get_xxx()
while True:
a = get_xxx.next()
if not a:
break
handle(a)
class A(Base):
def get_xxx():
param, d = 0, 1
while True:
yield somemethod(param)
param += d
class B(Base):
def get_xxx():
param = 0
while True:
a = somemethod(param)
param = a + 1
yield a
Problem solved, but somehow I feel uncomfortable. So I wonder if there's a better solution? Many thanks!
I would make things like param and d instance attributes:
class Base(object):
def __init__(self):
self.param = 0
self.d = 1
then you don't have to pass anything explicitly into get_xxx(). You could replace
param += d
with
self.iterate_param(a):
in Base.get_all() then define iterate_param() appropriately in your two subclasses, i.e.
class A(Base):
...
def iterate_param(self, a):
self.param += self.d
class B(Base):
...
def iterate_param(self, a):
self.param = a + 1

Categories

Resources