Proper class initialization

  • Thread starter Christoph Zwerschke
  • Start date
C

Christoph Zwerschke

Usually, you initialize class variables like that:

class A:
sum = 45

But what is the proper way to initialize class variables if they are the
result of some computation or processing as in the following silly
example (representative for more:

class A:
sum = 0
for i in range(10):
sum += i

The problem is that this makes any auxiliary variables (like "i" in this
silly example) also class variables, which is not desired.

Of course, I could call a function external to the class

def calc_sum(n):
...

class A:
sum = calc_sum(10)

But I wonder whether it is possible to put all this init code into one
class initialization method, something like that:

class A:

@classmethod
def init_class(self):
sum = 0
for i in range(10):
sum += i
self.sum = sum

init_class()

However, this does not work, I get
TypeError: 'classmethod' object is not callable

Is there another way to put an initialization method for the class A
somewhere *inside* the class A?

-- Christoph
 
J

Jack Diederich

Usually, you initialize class variables like that:

class A:
sum = 45

But what is the proper way to initialize class variables if they are the
result of some computation or processing as in the following silly
example (representative for more:

class A:
sum = 0
for i in range(10):
sum += i

The problem is that this makes any auxiliary variables (like "i" in this
silly example) also class variables, which is not desired.

Of course, I could call a function external to the class

def calc_sum(n):
...

class A:
sum = calc_sum(10)

But I wonder whether it is possible to put all this init code into one
class initialization method, something like that:

Yes, it is called a meta class.
class A:

@classmethod
def init_class(self):
sum = 0
for i in range(10):
sum += i
self.sum = sum

init_class()

However, this does not work

What we normally think of as an instance is an instance of a class.
Classes are actually instances of metaclasses. What you are looking for
is __init__, but not the __init__ defined after 'class A...' you want
the __init__ that is called when you type 'class A....'

Python 2.4.1 (#2, Mar 30 2005, 21:51:10)
[GCC 3.3.5 (Debian 1:3.3.5-8ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information..... def __init__(cls, *ignored):
.... cls.sum = 0
.... for (i) in range(10):
.... cls.sum += i
.... .... __metaclass__ = MyMeta
.... Traceback (most recent call last):

For something as simple as this example it is easier and cleaner
to move the loop into a function and do the one-liner assignment.
Because the metaclass can do _anything_ to the class the reader
is obliged to go read its code. The simple assignment from a
function obviously has no side effects.

Hope that helps,

-jackdied
 
L

Larry Bates

Christoph said:
Usually, you initialize class variables like that:

class A:
sum = 45

But what is the proper way to initialize class variables if they are the
result of some computation or processing as in the following silly
example (representative for more:

class A:
sum = 0
for i in range(10):
sum += i

The problem is that this makes any auxiliary variables (like "i" in this
silly example) also class variables, which is not desired.

Of course, I could call a function external to the class

def calc_sum(n):
...

class A:
sum = calc_sum(10)

But I wonder whether it is possible to put all this init code into one
class initialization method, something like that:

class A:

@classmethod
def init_class(self):
sum = 0
for i in range(10):
sum += i
self.sum = sum

init_class()

However, this does not work, I get
TypeError: 'classmethod' object is not callable

Is there another way to put an initialization method for the class A
somewhere *inside* the class A?

-- Christoph

Although I've never had the need for something like this,
this works:

class A:
sum=0
for i in range(10):
sum+=i
del i

or moving the initialization into __init__ method isn't
terribly inefficient unless are are creating LOTS of
instances of the same class.

class A:
def __init__(self):
self.sum=0
for i in range(10):
self.sum+=i


or you can do the do it before you instantiate the class

class A:
def __init__(self, initialvalue=None):
if initialvalue is not None: self.sum=initialvalue
else: self.sum=0


for i in range(10):
sum+=i

b=A(sum)
c=A(sum)

-Larry Bates
 
C

Christoph Zwerschke

Jack said:
... __metaclass__ = MyMeta

Thanks. I was not aware of the __metaclass__ attribute. Still a bit
complicated and as you said, difficult to read, as the other workarounds
already proposed. Anyway, this is probably not needed so often.

-- Christoph
 
S

Steven Bethard

Christoph said:
But I wonder whether it is possible to put all this init code into one
class initialization method, something like that:

class A:

@classmethod
def init_class(self):
sum = 0
for i in range(10):
sum += i
self.sum = sum

init_class()

I don't run into this often, but when I do, I usually go Jack
Diederich's route::

class A(object):
class __metaclass__(type):
def __init__(cls, name, bases, classdict):
cls.sum = sum(xrange(10))

But you can also go something more akin to your route::

class A(object):
def _get_sum():
return sum(xrange(10))
sum = _get_sum()

Note that you don't need to declare _get_sum() as a classmethod, because
it's not actually a classmethod -- it's getting used before the class
yet exists. Just write it as a normal function and use it as such --
that is, no ``self`` or ``cls`` parameter.

STeVe
 
S

Steven Bethard

Leif said:
What's wrong with sum = sum(xrange(10))?

Nothing, except that it probably doesn't answer the OP's question. The
OP presented a "silly example":

class A:
sum = 0
for i in range(10):
sum += i

I assume the intention was to indicate that the initialization required
multiple statements. I just couldn't bring myself to write that
horrible for-loop when the sum() function is builtin. ;)

STeVe
 
C

Christoph Zwerschke

Steven said:
I assume the intention was to indicate that the initialization required
multiple statements. I just couldn't bring myself to write that
horrible for-loop when the sum() function is builtin. ;)

Yes, this was just dummy code standing for something that really
requires multiple statements and auxiliary variables.

-- Christoph
 
C

Christoph Zwerschke

Steven said:
I don't run into this often, but when I do, I usually go Jack
Diederich's route::

class A(object):
class __metaclass__(type):
def __init__(cls, name, bases, classdict):
cls.sum = sum(xrange(10))

Good idea, that is really nice and readable. Now all the init code is
defined inline at the top of the class definition.
But you can also go something more akin to your route::

class A(object):
def _get_sum():
return sum(xrange(10))
sum = _get_sum()

Note that you don't need to declare _get_sum() as a classmethod, because
it's not actually a classmethod -- it's getting used before the class
yet exists. Just write it as a normal function and use it as such --
that is, no ``self`` or ``cls`` parameter.

Often it's so simple ;-) But the disadvantage is then that you cannot
set the class variables directly inside that function. So if you have to
initialize several of them, you need to define several functions, or
return them as a tuple and it gets a bit ugly. In this case, the first
solution may be better.

-- Christoph
 
K

Kent Johnson

Steven said:
I don't run into this often, but when I do, I usually go Jack
Diederich's route::

class A(object):
class __metaclass__(type):
def __init__(cls, name, bases, classdict):
cls.sum = sum(xrange(10))

I think you should call the superclass __init__ as well:

class __metaclass__(type):
def __init__(cls, name, bases, classdict):
super(__metaclass__, cls).__init__(name, bases, classdict)
cls.sum = sum(xrange(10))

Kent
 
G

gry

Christoph said:
Usually, you initialize class variables like that:

class A:
sum = 45

But what is the proper way to initialize class variables if they are the
result of some computation or processing as in the following silly
example (representative for more:

class A:
sum = 0
for i in range(10):
sum += i

The problem is that this makes any auxiliary variables (like "i" in this
silly example) also class variables, which is not desired.

Of course, I could call a function external to the class

def calc_sum(n):
...

class A:
sum = calc_sum(10)

But I wonder whether it is possible to put all this init code into one
class initialization method, something like that:

class A:

@classmethod
def init_class(self):
sum = 0
for i in range(10):
sum += i
self.sum = sum

init_class()

However, this does not work, I get
TypeError: 'classmethod' object is not callable

Is there another way to put an initialization method for the class A
somewhere *inside* the class A?
Hmm, the meta-class hacks mentioned are cool, but for this simple a
case how about just:

class A:
def __init__(self):
self.__class__.sum = self.calculate_sum()
def calculate_sum(self):
do_stuff
return sum_value

Instead of __class__ you could say:
A.sum = self.calculate_sum()
but that fails if you rename the class. I believe either works fine
in case of classes derived from A.

-- George Young
 
S

Steven Bethard

Kent said:
I think you should call the superclass __init__ as well:

class __metaclass__(type):
def __init__(cls, name, bases, classdict):
super(__metaclass__, cls).__init__(name, bases, classdict)
cls.sum = sum(xrange(10))

Yes, thanks for the catch.

I'd like to use super there, but I think you'll find that it doesn't
work because of the order in which A and __metaclass__ get created:
.... class __metaclass__(type):
.... def __init__(cls, name, bases, cdict):
.... super(__metaclass__, cls).__init__(name, bases, cdict)
.... cls.sum = sum(xrange(10))
....
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 4, in __init__
NameError: global name '__metaclass__' is not defined

.... class __metaclass__(type):
.... def __init__(cls, name, bases, classdict):
.... super(A.__metaclass__, cls).__init__(name, bases, classdict)
.... cls.sum = sum(xrange(10))
....
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 4, in __init__
NameError: global name 'A' is not defined


I played around with a few solutions to this problem, but it seems like
the cleanest one is just to avoid super:
.... class __metaclass__(type):
.... def __init__(cls, name, bases, cldict):
.... type.__init__(cls, name, bases, cldict)
.... cls.sum = sum(xrange(10))
....

Of course, this means you can't use some sorts of multiple inheritance
with A.__metaclass__...

STeVe

P.S. Here's the best way I found that still uses super:
.... class __metaclass__(type):
.... def __init__(cls, name, bases, cldict):
.... try:
.... meta = A.__metaclass__
.... except NameError:
.... meta = cldict['__metaclass__']
.... super(meta, cls).__init__(name, bases, cldict)
.... cls.sum = sum(xrange(10))
....
 
C

Christoph Zwerschke

Hmm, the meta-class hacks mentioned are cool, but for this simple a
case how about just:

class A:
def __init__(self):
self.__class__.sum = self.calculate_sum()
def calculate_sum(self):
do_stuff
return sum_value

If you do it like that, Steven's second suggestion was better:

class A:
def calculate_sum():
do_stuff
return sum_value
sum = calculate_sum()

That's not only easier simpler, it also avoids calling calculate_sum()
every time you create an instance.

-- Christoph
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top