?

Log in

No account? Create an account

Previous Entry | Next Entry

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 )
Page 1 of 2
<<[1] [2] >>
(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
Page 1 of 2
<<[1] [2] >>
( 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