Python subclass counter - python

I have this python code. The result is TopTest: attr1=0, attr2=1 for X which is fine but the result is SubTest: attr1=2, attr2=3 for Y which I don't quite understand.
Basically, I have a class attribute, which is a counter, and it runs in the __init__ method. When I launch Y, the counter is set to 2 and only after are the attributes are assigned. I don't understand why it starts at 2. Shouldn't the subclass copy the superclass and the counter restart at 0?
class AttrDisplay:
def gatherAttrs(self):
attrs = []
for key in sorted(self.__dict__):
attrs.append('%s=%s' % (key, getattr(self, key)))
return ', '.join(attrs)
def __repr__(self):
return '[%s: %s]' % (self.__class__.__name__, self.gatherAttrs())
class TopTest(AttrDisplay):
count = 0
def __init__(self):
self.attr1 = TopTest.count
self.attr2 = TopTest.count+1
TopTest.count += 2
class SubTest(TopTest):
pass
X, Y = TopTest(), SubTest()
print(X)
print(Y)

You access and use explicitly TopTest.count, and your subclass will stick to this explicitness. You might want to consider to use type(self).count instead, then each instance will use its own class's variable which can be made a different one in each subclass.
To make your subclass have its own class variable, just add a count = 0 to its definition:
class SubTest(TopTest):
count = 0

It looks like you want to keep a counter for each instance of each subclass of TopTest, but you do not want to repeat yourself by declaring a new count class variable for each subclass. You can achieve this using a Metaclass:
class TestMeta(type):
def __new__(cls, name, bases, attrs):
new_class = super().__new__(cls, name, bases, attrs)
new_class.count = 0
return new_class
class TopTest(AttrDisplay, metaclass=TestMeta):
def __init__(self):
self.attr1 = self.count
self.attr2 = self.count + 1
self.increment_count(2)
#classmethod
def increment_count(cls, val):
cls.count += val
class SubTest(TopTest):
pass
The count attribute of your x and y objects should now be independent, and subsequent instances of TopTest and SubTest will increment the count:
>>> x, y = TopTest(), SubTest()
>>> x.attr2
1
>>> y.attr2
1
>>> y2 = SubTest()
>>> y2.attr2
3
However, metaclasses can be confusing and should only be used if they are truly necessary. In your particular case it would be much simpler just to re-define the count class attribute for every subclass of TopTest:
class SubTest(TopTest):
count = 0

You're close - when you look up a property of an object, you're not necessarily looking up a property belonging to the object itself. Rather, lookups follow Python's method resolution order, which... isn't entirely simple. In this case, however, only three steps are performed:
Check if Y has a property named count.
It doesn't, so check if its class SubTest has a property named count.
It doesn't, so check if its parent TopTest has a property named count. It does, so access that.
Simply put, when you access Y.count, you're actually accessing TopTest.count.
There's also the fact that you have a bug in your code - SubTest increments TopTest's count and not its own. The title of your question says "subclass counter", but since you're counting in __init__() I assume you're looking for an instance counter (to count subclasses I'm fairly certain you'd need to use metaclasses). This is a perfect use case for self.__class__, a property which contains an object's class! In order to use it:
def __init__(self):
self.attr1 = self.__class__.count
self.attr2 = self.__class__.count + 1
self.__class__.count += 2
Using that, SubTest.count will be incremented instead of TopTest.count when you call SubTest().

When a new instance of SubTest is created TopTest.__init__() is called - since SubTest inherited TopTest.__init__() - which increments TopTest.count by two.
And since SubTest never defines a class level count variable, when SubTest.count is executed, Python falls back and uses TopTest.count.
This behavior can be fixed by redefining count local to SubTest.
class SubTest(TopTest):
count = 0

If you want each class to have it's own class variable implicitly, you can use a metaclass to add in this variable.
class MetaCount(type):
def __new__(cls, name, bases, attrs):
new_cls = super(MetaCount, cls).__new__(cls, name, bases, attrs)
new_cls.count = 0
return new_cls
class Parent(metaclass=MetaCount):
def __init__(self):
self.attr1 = self.count
self.attr2 = self.count + 1
type(self).count += 2 # self.count += 2 creates an *instance* variable
class Child(Parent):
pass
p, c = Parent(), Child()
print(p.count) # 2
print(c.count) # 2

Related

Prevent user of changing variable type in python

I have main class with a lot of attributes that are initially defined as an object of a Prop class. This Prop class has two attributes: its value and the options, which is a list of acceptable values for the attribute.
class Prop():
def __init__(self, value, *options):
self.value = value
self.options = options
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
The first important thing here is that _propx has to be an instance variable, since I will create more than one instance of Main.
The values of a Prop instance can either be a string or an integer, but the problem with this code is that I have to be sure that the user will do something like main._prop1.value = 1 and not main._prop1 = 1 otherwise it would break my code when doing _prop1.options. I don't want to use traits, thus I decided to make each _propx instance a kind of property, but I'm talking about a lot of instances and I don't want to define each setter especially because they will be all the same.
I found two solutions to solve this problem, the first is by using the same setter to all properties:
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
def set_prop(attr):
def set_value(self, value):
self.__dict__[attr].value = value
return set_value
prop1 = property(fset=set_prop('_prop1'))
prop2 = property(fset=set_prop('_prop2'))
The second is by using an auxiliary class and redefine its __set__:
class Aux():
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' + name
def __set__(self, obj, value):
print(self, obj, value, self.private_name)
obj.__dict__[self.private_name].value = value
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
prop1 = Aux()
prop2 = Aux()
the first on seems cleaner, but I have to pass the private name of each variable and I have to write the setter in the Main which I don't like because I would it to be as clean as possible. By other hand, in the second I have to use an auxiliary class.
My question is: is there a way of defining the setter in the Prop class? The reason why I couldn't find a way of doing this is that the Aux.__set__ seems to work only when I create an Aux instance as a class variable (static variable). This is also why I have to create a private and a public variable for each property. Is there a way of using __set__ to an instance (non-static) variable?

Deleting Instance of Object in Python

A class is defined like this:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Example:
__metaclass__ = IterRegistry
_registry =[]
def __init__(self,iD):
self.iD = iD
self.count = 40
self._registry.append(self)
def reduceCount(self):
self.count -= 1
Over the course of the program more and more instances of the class are created. I have an timer running, which then runs a for Loop over all the instances and reduces the count by 1 for each.
def timer():
for i in Example:
i.reduceCount()
if i.count < 0:
#then delete the instance
My question is how do I delete this instance?
You may use a del statement.
del can also be used to delete entire variables:
>>> del a
Referencing the name a hereafter is an error (at least until another value is assigned to it).
But at the end of the day, this is just a suggestion to the garbage collector. Object may be deleted right away, but it also may be deleted long after execution of this statement. It's not guaranteed by a language specification.
To answer my question I followed #Unholysheep advice and removed it from the registry in the class. To do this I had to change the code slightly by changing _reigstry to registry, so I can acess it from the rest of the program.
class IterRegistry(type):
def __iter__(cls):
return iter(cls.registry)
class Example:
__metaclass__ = IterRegistry
registry =[]
def __init__(self,iD):
self.iD = iD
self.count = 40
self.registry.append(self)
def reduceCount(self):
self.count -= 1
This now allows me to remove the instance from inside the loop by:
def timer():
for i in Example:
i.reduceCount()
if i.count < 0:
#then delete the instance
Example.registry.remove(i)

Using __delitem__ with a class object rather than an instance in Python

I'd like to be able to use __delitem__ with a class-level variable.
My use case can be found here (the answer that uses _reg_funcs) but it basically involves a decorator class keeping a list of all the functions it has decorated. Is there a way I can get the class object to support __delitem__? I know I could keep an instance around specially for this purpose but I'd rather not have to do that.
class Foo(object):
_instances = {}
def __init__(self, my_str):
n = len(self._instances) + 1
self._instances[my_str] = n
print "Now up to {} instances".format(n)
#classmethod
def __delitem__(cls, my_str):
del cls._instances[my_str]
abcd = Foo('abcd')
defg = Foo('defg')
print "Deleting via instance..."
del abcd['abcd']
print "Done!\n"
print "Deleting via class object..."
del Foo['defg']
print "You'll never get here because of a TypeError: 'type' object does not support item deletion"
When you write del obj[key], Python calls the __delitem__ method of the class of obj, not of obj. So del obj[key] results in type(obj).__delitem__(obj, key).
In your case, that means type(Foo).__delitem__(Foo, 'abcd'). type(Foo) is type, and type.__delitem__ is not defined. You can't modify type itself, you'll need to change the type of Foo itself to something that does.
You do that by defining a new metaclass, which is simply a subclass of type, then instructing Python to use your new metaclass to create the Foo class (not instances of Foo, but Foo itself).
class ClassMapping(type):
def __new__(cls, name, bases, dct):
t = type.__new__(cls, name, bases, dct)
t._instances = {}
return t
def __delitem__(cls, my_str):
del cls._instances[my_str]
class Foo(object):
__metaclass__ = ClassMapping
def __init__(self, my_str):
n = len(Foo._instances) + 1
Foo._instances[my_str] = n
print "Now up to {} instances".format(n)
Changing the metaclass of Foo from type to ClassMapping provides Foo with
a class variable _instances that refers to a dictionary
a __delitem__ method that removes arguments from _instances.

Python: How to do extra stuff when a specific attribute of an object is accessed?

Let's say I have a class in Python:
class Foo(object):
a = 1
b = 2
I'd like to do some extra stuff when I access 'a' but NOT 'b'. So, for example, let's assume that the extra stuff I'd like to do is to increment the value of the attribute:
> f = Foo()
> f.a # Should output 2
> f.a # Should output 3
> f.a # Should output 4
> f.b # Should output 2, since I want the extra behavior just on 'a'
It feels like there is a way through __getattr__ or __getattribute__, but I couldn't figure that out.
The extra thing can be anything, not necessarily related to the attribute (like print 'Hello world').
Thanks.
What you are looking for is a property, which can be used nicely as a decorator:
class Foo(object):
_a = 2
#property
def a(self):
Foo._a += 1
return Foo._a - 1
b = 2
The function is called whenever you try to access foo_instance.a, and the value returned is used as the value for the attribute. You can also define a setter too, which is called with the new value when the attribute is set.
This is presuming you want the odd set-up of class attributes you only ever access from instances. (_a and b here belong to the class - that is, there is only one variable shared by all instances - as in your question). A property, however, is always instance-owned. The most likely case is you actually want:
class Foo(object):
def __init__(self):
self._a = 2
self.b = 2
#property
def a(self):
self._a += 1
return self._a - 1
Where they are instance attributes.
If you really do want the equivalent of #property for a class variable, you have to build the descriptor yourself.
You almost certainly don't want to do this—see Lattyware's answer for how to make normal instance variables, and turn one of them into a #property.
But here's how you could do it:
class IncrementOnGetDescriptor(object):
def __init__(self, initval=None):
self.val = initval
def __get__(self, obj, objtype):
self.val += 1
return self.val - 1
def __set__(self, obj, val):
self.val = val
class Foo(object):
a = IncrementOnGetDescriptor(2)
b = 2
Now you can test it:
>>> f = Foo()
>>> f.a
2
>>> Foo.a
3
>>>> f.a
4
Turning this into a #classproperty decorator is left as an exercise for the reader.
PS, this still isn't exactly like a normal class variable. Setting Foo.a = 10 will replace your magic auto-incrementing value with a normal 10, while setting foo.a = 10 will update the class with an auto-incrementing 10 instead of storing an instance variable in f. (I originally had the __set__ method raise AttributeError, because normally you'd want an auto-incrementing magic variable be read-only, but I decided to show the more complex version just to show all the issues you have to deal with.)

Does python has methods similar to __setattr__ but for python class?

Currently __setattr__ only works for instance. Is there any similar method for class? I am asking this question because I want to collect the list of defined attribute in order when user define it in class as below:
class CfgObj(object):
_fields = []
def __setattr__(self, name, value):
self._fields.append([name, value])
object.__setattr__(self, name, value)
class ACfg(CfgObj):
setting1 = Field(str, default='set1', desc='setting1 ...')
setting2 = Field(int, default=5, desc='setting2...')
I know the above code will not work as expected because the __setattr__ only called by instance as below:
acfg = ACfg()
acfg.c = 1
acfg._fields == [['c', 1]]
So, is there any equivalent __setattr__ for python class? The main purpose is to collect the define attribute in order when user define it in class.
Yes, but that's not how you want to do it.
class MC(type):
def __init__(cls, name, bases, dct):
print dct
super(MC, cls).__init__(name, bases, dct)
class C(object):
__metaclass__ = MC
foo = 42
If you define __setattr__() on the metaclass of a class, it will be called when setting attributes on the class, but only after creating the class:
>>> class Meta(type):
... def __setattr__(cls, name, value):
... print "%s=%r" % (name, value)
...
>>> class A(object):
... __metaclass__ = Meta
...
>>> A.a = 1
a=1
But it won't work at the time of class definition, so it's probably not what you want.
Getting the class attributes in the metaclass __init__() works, but you loose the order of definition (and multiple definitions as well).
What I would do to solve your problem - but not your question - is to set the timestamp of the field creation create a counter of Field objects and set the current value of the counter to the created one:
class Field(object):
count = 0
def __init__(self, value, default=None, desc=None):
self.value = value
self.default = default
self.desc = desc
# Here comes the magic
self.nth = Field.count
Field.count += 1
# self.created_at = time.time()
Then I would create a method for returning all fields ordered by its counter value:
class CfgObj(object):
def params(self):
ns = dir(self)
fs = [getattr(self, field)
for field in ns
if isinstance(getattr(self, field), Field)]
# fs = sorted(fs, key=lambda f: f.created_at)
fs = sorted(fs, key=lambda f: f.nth)
return fs
Its usage is intuitive:
class ACfg(CfgObj):
setting1 = Field(str, default='set1', desc='setting1 ...')
setting2 = Field(int, default=5, desc='setting2...')
print ACfg().params()
Clearly the fields are ordered by time of object creation, not field creation, but it can be enough for you. Is it?

Categories

Resources