You are viewing cniehaus

Previous Entry | Next Entry

Taken in Portugal
Hello

I am having a Python problem. I have two classes, both inheriting the class 'Animal'. I want a counter, that means Animal.count_of() should return the number of objects.

Please have a look at this code:


class Animal(object):
    number = 0
   
    def __init__(self):
        Animal.number += 1
       
    def __del__(self):
        Animal.number -= 1
       
    def count_of(self):
        print Animal.number
       
class Laus(Animal):
    def __init__(self):
        Animal.__init__(self)
   
class Bug(Animal):
    def __init__(self):
        Animal.__init__(self)

   
l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()



This is not working because both classes are using the same class variable (number) which means the number in the last line is 5, not 1. I could obviously have one class variable for Bug and one for Laus, but that would mean I'd have to have two .count_of()-methods as well (and more would have to be duplicated, I only pasted a stripped down code).

Is there a trick to have one base-class Animal with a counter or do I really have to do everything twice?

Comments

( 31 comments — Leave a comment )
(Anonymous)
Apr. 16th, 2009 02:00 pm (UTC)
think of what whould be expected if the hirarch gets deeper

sub types of mamels and zebras and ...

what whould be the corect count?
(Anonymous)
Apr. 16th, 2009 02:05 pm (UTC)
use self instead of Animal?
def __init__(self):
self.number += 1
...this is just like in any other OO language. (others may call it "this").
ralesk
Apr. 16th, 2009 03:36 pm (UTC)
Re: use self instead of Animal?
Will increase the instance’s own counter rather than the instance’s class’s counter (and thus print 1 five times instead of telling us that there are 4 Laus and 1 Bug).
(Anonymous)
Apr. 16th, 2009 02:11 pm (UTC)
Metaclasses
It is possible to have a metaclass for countable classes, but that's quite magic.

I'd love a simple option myself.
(Anonymous)
Apr. 16th, 2009 02:11 pm (UTC)
Frank
What about making your counter a dictionary { classname : count }
then your __del__ and __init__ methods will be a bit more complex, should look like:

number = {}

def __init__(self):
    cls = str(type(self))
    if (not cls in number.keys()):
        number[cls]=0
    number[cls] = number[cls] + 1


(untested code)
(Anonymous)
Apr. 16th, 2009 06:11 pm (UTC)
Re: Frank
The idea is good, but your implementation doesn't work. I would change the code to this:


# -*- coding: utf-8 -*-

import collections

class Animal(object):
numbers = collections.defaultdict(int)

@classmethod
def __init__(self):
self.numbers[self.__name__] += 1

@classmethod
def __del__(self):
self.numbers[self.__name__] -= 1

@classmethod
def count(self):
return self.numbers[self.__name__]

class Dog(Animal):
pass

class Cat(Animal):
pass

d1 = Dog()
print "Dogs:", d1.count()
d2 = Dog()
print "Dogs:", d1.count()

c1 = Cat()
print "Cats:", c1.count()
c2 = Cat()
print "Cats:", c1.count()
(Anonymous)
Apr. 16th, 2009 02:18 pm (UTC)
Unfortunately so

I'm afraid that what you describe is exactly what should be happening.

Animal.number is a count of animals. Therefore it will also count any classes derived from Animal, since they are animals too.

There is probably a hoop you can jump through to get what you want. You'll have to forgive me though, the syntax I know is C++ for object oriented work; I'm sure there are python equivalents to the ideas...

What you want is a virtual member that returns a reference to a counter for that type. So (access specifiers removed for brevity):

class Animal {
  Animal() { getCounter()++; }
private:
 virtual int &getCounter() = 0;
};

class Laus {
  ~Laus() { getCounter()--; }
  int &getCounter() { return LausCounter; }
private:
  static LausCounter;
};
int Laus::LausCounter = 0;

class Bug {
  ~Bug() { getCounter()--; }
  int getCounter() { return BugCounter; }
private:
  static int BugCounter;
};
int Bug::BugCounter = 0;

With this, it is impossible to create an Animal object directly since getCounter() is abstract. When a derived class wants to provide a counter, it implements getCounter() to return a reference to a static member variable. Note, that a derived class of Laus or Bug would use LausCounter or BugCounter respectively unless that derived class implemented its own getCounter().

Also note that the decrement of the counter must be done in the derived class, since the Animal destructor is called _after_ the derived class destructor, hence Animal::getCounter() points at nothing during Animal::~Animal().

All in all, it's a lot of hoop jumping for what would more easily be implemented as independent counters. I'm assuming though that you were asking the question framed simply for a more complex reality.

(Anonymous)
Apr. 16th, 2009 02:21 pm (UTC)
python
How about this:


class Animal(object):
def __init__(self):
self.number += 1

def __del__(self):
self.number -= 1

def count_of(self):
print self.number

class Laus(Animal):
number = 0

class Bug(Animal):
number = 0


Hmm. I can't get the indention right. sorry :/
(Anonymous)
Apr. 16th, 2009 02:25 pm (UTC)
Proposal
I suppose this should work :

[code]
class Animal(object):
number = 0

def __init__(self):
self.__class__.number += 1

def __del__(self):
self.__class__.number -= 1

def count_of(self):
print self.__class__.number

class Laus(Animal):
number = 0

class Bug(Animal):
number = 0
[/code]

There may be a more elegant method, though !

Btw I think there is no need to implement __init__ in derived class, since it is used "as is" inherited from base class...
parker.coates.myopenid.com
Apr. 16th, 2009 02:31 pm (UTC)
How about this?
class Animal(object):
    def __init__(self):
        try :
            self.__class__.number += 1
        except AttributeError :
            self.__class__.number = 1
            
    def __del__(self):
        self.__class__.number -= 1
        
    @classmethod
    def count_of(cls):
        print cls.number
       
class Laus(Animal):
   pass

class Bug(Animal):
   pass

l1 = Laus()
Laus.count_of()
l2 = Laus()
Laus.count_of()
l3 = Laus()
Laus.count_of()
l4 = Laus()
Laus.count_of()

b1 = Bug()
Bug.count_of()
balinares
Apr. 16th, 2009 02:33 pm (UTC)
Instead of Animal.number, use self.__class__.number.

>>> class Animal:
...   def __init__( s ):
...     if not hasattr( s.__class__, "number" ): s.__class__.number = 0
...     s.__class__.number += 1
...   def __del__( s ):
...     s.__class__.number -= 1
...
>>> class Bug( Animal ): pass
...
>>> class Horse( Animal ): pass
...
>>> h1=Horse()
>>> h2=Horse()
>>> Horse.number
2
>>>
>>> b1=Bug()
>>> b2=Bug()
>>> Bug.number
2
>>> Horse.number
2
>>> del b2
>>>
>>> Horse.number
2
>>> Bug.number
1
>>>
ralesk
Apr. 16th, 2009 03:37 pm (UTC)
Tsk, you were faster than me :P
(Anonymous)
Apr. 16th, 2009 02:34 pm (UTC)
almost:
class Animal(object):
def __init__(self):
self.__class__.number += 1

def __del__(self):
self.number -= 1

def count_of(self):
print self.number

class Laus(Animal):
number = 0

class Bug(Animal):
number = 0
(Anonymous)
Apr. 16th, 2009 02:38 pm (UTC)
class A(object):
number = 0
def count(self):
return self.__class__.number
def inc(self):
self.__class__.number += 1

class B(A):
pass

a = A()
b = B()

b.inc()

print a.count(), b.count()
(Anonymous)
Apr. 16th, 2009 02:40 pm (UTC)
Half-way solution
The following, although not the nicest solution, seems to work:

class Animal(object):

def __init__(self):
self.__class__.number += 1

def __del__(self):
self.__class__.number -= 1

def count_of(self):
print self.__class__.number

class Laus(Animal):
number = 0
# No need to redeclare __init__ if only calling the __init__ of the parent object

class Bug(Animal):
number = 0


l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()



Since only the class variable is moved into the subclasses, the count_of() function can still be shared.

It's not the best solution, but it's a bit closer to what you want (I think)
(Anonymous)
Apr. 16th, 2009 02:40 pm (UTC)
Here it is :)
class Animal(object): number = 0 def __init__(self): self.__class__.number += 1 def __del__(self): self.__class__.number -= 1 def count_of(self): print self.__class__.number
(Anonymous)
Apr. 16th, 2009 02:42 pm (UTC)
Suggestion
Try passing number as an argument to __init__ , __del__ and count_of:

def __init__(self, number):
self.number += 1
def __del__(self, number):
self.number -= 1

def count_of(self, number):
print self.number
ralesk
Apr. 16th, 2009 02:42 pm (UTC)
    def __init__(self):
        self.__class__.number += 1

    def __del__(self):
        self.__class__.number -= 1

    def count_of(self):
        print self.__class__.number
(Anonymous)
Apr. 16th, 2009 02:54 pm (UTC)
answer
Try with the special attribute __class__

class Animal(object):
number = 0

def __init__(self):
self.__class__.number += 1

def __del__(self):
self.__class__.number -= 1

def count_of(self):
print self.__class__.number

(Anonymous)
Apr. 16th, 2009 02:56 pm (UTC)
What about accessing the count like this?

self.__class__.number

Long
(Anonymous)
Apr. 16th, 2009 03:03 pm (UTC)
Counting all derived objects
Maybe you are interested in counting all the derived objects as well:
def count_of(self):
return sum(map(lambda c: c.number, Animal.__subclasses__()))

Long
(Anonymous)
Apr. 16th, 2009 02:57 pm (UTC)
Some more explanation
Maybe some explanation is needed here. By using "Animal" in your constructor, destructor and count_of methods, you're explicitly using the attribute of the class "Animal" ; actually, you could modify it from another class as well, using the same syntax. The class attribute declared in "Animal" is inherited, but not shared. In your code, it is initialized, but never used. Using self.__class_ instead of Animal makes it available. Of course, if you want "Animal.count_of()" to reflect the count of all animals (including subclasses instances), you actually need to do something like this: def __init__(self): self.__class__.number += 1 if self.__class__ != Animal: Animal.number += 1 def __del__(self): self.__class__.number -= 1 if self.__class__ != Animal: Animal.number -= 1 One more comment: the method count_of() should be a class method. I'm no Python guru, so if someone sees a mistake, please jump in ! gael.deest@dont.want.spammm.gmail.com
(Anonymous)
Apr. 16th, 2009 02:58 pm (UTC)
use a dict
You could use a dict in combination with self.__class__ to get the current class name.

class Animal(object):
numbers = {}

def __init__(self):
if not self.__class__ in Animal.numbers:
Animal.numbers[self.__class__] = 0
else:
Animal.numbers[self.__class__] += 1

def __del__(self):
Animal.numbers[self.__class__] -= 1

def count_of(self):
print "count of ", self.__class__ , " is:", Animal.numbers[self.__class__]

class Laus(Animal):
def __init__(self):
Animal.__init__(self)

class Bug(Animal):
def __init__(self):
Animal.__init__(self)


l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

print "\nshould print 0:"
b1 = Bug()
b1.count_of()

Which will print:

count of
[Error: Irreparable invalid markup ('<class '__main__.laus'>') in entry. Owner must fix manually. Raw contents below.]

You could use a dict in combination with self.__class__ to get the current class name.

class Animal(object):
numbers = {}

def __init__(self):
if not self.__class__ in Animal.numbers:
Animal.numbers[self.__class__] = 0
else:
Animal.numbers[self.__class__] += 1

def __del__(self):
Animal.numbers[self.__class__] -= 1

def count_of(self):
print "count of ", self.__class__ , " is:", Animal.numbers[self.__class__]

class Laus(Animal):
def __init__(self):
Animal.__init__(self)

class Bug(Animal):
def __init__(self):
Animal.__init__(self)


l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

print "\nshould print 0:"
b1 = Bug()
b1.count_of()

Which will print:

count of <class '__main__.Laus'> is: 0
count of <class '__main__.Laus'> is: 1
count of <class '__main__.Laus'> is: 2
count of <class '__main__.Laus'> is: 3

should print 0:
count of <class '__main__.Bug'> is: 0
(Anonymous)
Apr. 16th, 2009 03:13 pm (UTC)
I'd use a dict
I tend to use a dict in cases like these; my python-fu is very rusty, but I'd start with something like this:

class Animal(object):
numbers = {}
def __init__(self):
if self.__class__.__name__ in Animal.numbers:
Animal.numbers[self.__class__.__name__] += 1
else:
Animal.numbers[self.__class__.__name__] = 1

def count_of(self):
print Animal.numbers[self.__class__.__name__]

class Laus(Animal):
def __init__(self):
Animal.__init__(self)

class Bug(Animal):
def __init__(self):
Animal.__init__(self)

l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()


tomte@librics-tomte ~/scratch $ python test.py
1
2
3
4
1

kind regards,
Tom
(Anonymous)
Apr. 16th, 2009 03:16 pm (UTC)
Here
class Animal(object):
number = 0

def __init__(self):
self.__class__.number += 1

def delete(self):
self.__class__.number -= 1

def count_of(self):
print self.__class__.number

class Laus(Animal):

def __init__(self):
Animal.__init__(self)

class Bug(Animal):

def __init__(self):
Animal.__init__(self)


l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()
(Anonymous)
Apr. 16th, 2009 03:48 pm (UTC)
use @classmethod
class Animal(object): number = 0 def __init__(self): #Animal.number += 1 self.__init_() @classmethod def __init_(cls): cls.number += 1 def __del__(self): self.__del_() @classmethod def __del_(cls): cls.number -= 1 def count_of(cls): print cls.number class Laus(Animal): def __init__(self): Animal.__init__(self) class Bug(Animal): def __init__(self): Animal.__init__(self) l1 = Laus() l2 = Laus() l3 = Laus() l1.count_of() #should be 3 l1 = Laus() l1.count_of() # should also be 3, since earlier l1 is freed b1 = Bug() b1.count_of() # should be 1
(Anonymous)
Apr. 16th, 2009 03:51 pm (UTC)
The cleanest solution
I think the solution that most resembles your example and works like you intend is the following one:
class Animal:
    def __init__( self ):
        if not hasattr( self.__class__, "number" ): self.__class__.number = 0
        self.__class__.number += 1
    def __del__( self ):
        self.__class__.number -= 1
    def count_of(self):
        print self.__class__.number
            
class Laus( Animal ): pass

class Bug( Animal ): pass

l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()
(Anonymous)
Apr. 16th, 2009 06:18 pm (UTC)
Re: The cleanest solution
The cleanest solution is to remove the animal counting out of the Animal class. It doesn't have anything to do with animal properties, it doesn't allow any flexibility. You don't have counts of Bugs, Lauses... You even cannot iterate through the animals. The real solution is to put animals into some container (array) - like the ZOO - and get the count from there.
(Anonymous)
Apr. 17th, 2009 03:23 am (UTC)
Re: The cleanest solution
Yep, I agree. Use some kind of "container" like an array or even a vector.
So to use Qt pseudo-code

...
QVector[QString] *allanimals ; //substitutes brackets for vector arrows, html strikes again!
...
Then in the constructor for each animal you could try something like
allanimals->push_back("Bug");

so to find out total animals you could do
allanimals->size():
to find out how many bugs, you could make a function that iterates through and gets a count of how many "bugs" are in the allanimals vector.

The code/syntax for this is probably off, but you get the picture.
(Anonymous)
Apr. 16th, 2009 04:13 pm (UTC)
Try this
I think you want something like this:

class Animal(object):
number = 0

def __init__(self):
self.__class__.number += 1

def __del__(self):
self.__class__.number -= 1

def count_of(self):
print self.__class__.number

class Laus(Animal):
def __init__(self):
Animal.__init__(self)

class Bug(Animal):
def __init__(self):
Animal.__init__(self)


l1 = Laus()
l1.count_of()
l2 = Laus()
l1.count_of()
l3 = Laus()
l1.count_of()
l4 = Laus()
l1.count_of()

b1 = Bug()
b1.count_of()



Cheers,
Mircea
(Anonymous)
Apr. 16th, 2009 07:14 pm (UTC)
Use a class decorator and a few method decorators

incomplete and probably could be better still example:

def increment(func):
def incr(self, *_args, **_kw):
self.__class__.count = self.__class__.count + 1
return func(self, *_args, **_kw)
return incr

def decrement(func):
def decr( self, *_args, **_kw):
self.__class__.count = self.__class__.count - 1
return func(self, *_args, **_kw)
return decr

def countable(cls):
cls.count = 0
return cls

@countable
class Animal(object):
@increment
def __init__(self):
pass
@decrement
def __del__(self):
pass



a = Animal()

print a.count

huzzah!

( 31 comments — Leave a comment )

Profile

Taken in Portugal
cniehaus
cniehaus

Latest Month

July 2009
S M T W T F S
   1234
567891011
12131415161718
19202122232425
262728293031 

Page Summary

Powered by LiveJournal.com
Designed by Tiffany Chow