Metaclasses and class variables

  • Thread starter Jan-Ole Esleben
  • Start date
J

Jan-Ole Esleben

Hi!

I am new to this list, and maybe this is a stupid question, but I
can't seem to find _any_ kind of answer anywhere.

What I want to do is the following:
I want to insert a class variable into a class upon definition and
actually use it during definition.

Manually, that is possible, e.g.:

class A:
classvar = []
classvar.append(1)
classvar.append(2)

I don't want to explicitly set the variable, though. My idea was to
write the following:

class Meta(type):
def __new__(cls, name, bases, d):
d['classvar'] = []
return type.__new__(cls, name, bases, d)

class Test:
__metaclass__ = Meta

classvar.append(1)
classvar.append(2)

However, Python complains that the variable isn't defined; it can be
found in the class dictionary _after_ definition, though, and then it
can also be used. But where's the conceptual difference (to the manual
approach)?

Thanks in advance,
Ole
 
C

Christopher Subich

Jan-Ole Esleben said:
class Meta(type):
def __new__(cls, name, bases, d):
d['classvar'] = []
return type.__new__(cls, name, bases, d)

The problem is that __new__ is called upon object construction, not
class definition, but you're trying to set the class variables at
definition-time.
 
J

Jan-Ole Esleben

I thought __new__ was called upon construction of the _class_ object
that "Meta" is the type of. Then it would be available at the time of
the definition of my class. Or am I mistaken?

Ole

2005/8/4 said:
Jan-Ole Esleben said:
class Meta(type):
def __new__(cls, name, bases, d):
d['classvar'] = []
return type.__new__(cls, name, bases, d)

The problem is that __new__ is called upon object construction, not
class definition, but you're trying to set the class variables at
definition-time.
 
T

Thomas Heller

Jan-Ole Esleben said:
Hi!

I am new to this list, and maybe this is a stupid question, but I
can't seem to find _any_ kind of answer anywhere.

What I want to do is the following:
I want to insert a class variable into a class upon definition and
actually use it during definition.

Manually, that is possible, e.g.:

class A:
classvar = []
classvar.append(1)
classvar.append(2)

I don't want to explicitly set the variable, though. My idea was to
write the following:

class Meta(type):
def __new__(cls, name, bases, d):
d['classvar'] = []
return type.__new__(cls, name, bases, d)

class Test:
__metaclass__ = Meta

classvar.append(1)
classvar.append(2)

However, Python complains that the variable isn't defined; it can be
found in the class dictionary _after_ definition, though, and then it
can also be used. But where's the conceptual difference (to the manual
approach)?

classvar is defined AFTER the class has been created. So, this should
work:

class Test:
__metaclass__ = Meta

Test.classvar.append(1)

Thomas
 
K

Kent Johnson

Christopher said:
Jan-Ole Esleben said:
class Meta(type):
def __new__(cls, name, bases, d):
d['classvar'] = []
return type.__new__(cls, name, bases, d)


The problem is that __new__ is called upon object construction, not
class definition, but you're trying to set the class variables at
definition-time.

The metaclass __new__() is called on class creation (the class is the object being constructed) but not until after the body of the class definition is executed; the dictionary passed to __new__() contains the methods and class variables defined by the class statement.

See http://www.python.org/2.2/descrintro.html#metaclasses

Kent
 
J

Jan-Ole Esleben

Yes, that works, but it is unfortunately not an option (at least not a
good one).

Is there no way to create a class variable that exists during
definition of the class? (I cannot imagine there isn't, since
technically it's possible and manually it can be done...)

Ole
 
M

Mike C. Fletcher

Jan-Ole Esleben said:
Yes, that works, but it is unfortunately not an option (at least not a
good one).

Is there no way to create a class variable that exists during
definition of the class? (I cannot imagine there isn't, since
technically it's possible and manually it can be done...)

Ole
The metaclass hook occurs *after* class definition, anything using a
side-effect of a metaclass hook then, *must* occur after the execution
of the metaclass hook. At the time you want to write classvar.append
the "class" is only a namespace, so, if you really need this feature
you'll need to look elsewhere for at least *part* of the solution.

A global "classvar" that, when appended to, caches values until your
metaclass is called and transfers the cache to the class should *work*,
but egads that's ugly compared to just classvar = [] . I guess what I'd
ask is *why* is avoiding that single line so important. It could be
there's a reasonable answer, but the amount of machinery required to
avoid it is going to be significant.

class meta( type ):
newClassVar = []
def __new__( cls, name, bases, dictionary ):
dictionary[ 'classvar' ] = cls.newClassVar[:]
del cls.newClassVar[:]
return super( meta, cls ).__new__( cls, name, bases, dictionary )
__metaclass__ = meta
classvar = meta.newClassVar

or something along those lines...

Um, ick, but HTH,
Mike

--
________________________________________________
Mike C. Fletcher
Designer, VR Plumber, Coder
http://www.vrplumber.com
http://blog.vrplumber.com
 
J

Jan-Ole Esleben

Thanks! It's a bit icky, yes, but I've been so wrapped up in
complicated thinking that I didn't see this. It's actually quite an
OK solution (I need it because I have an internal representation for
method interfaces that needs to be saved somewhere without the user
having to worry about it, and without them having to set variables.
Method interfaces are class specific, and the only other thing I could
do would be to have a dictionary of classes somewhere, but as the
class itself uses its interface I can't see any really sensible way to
go about this differently).

Ole


2005/8/4 said:
Jan-Ole Esleben said:
Yes, that works, but it is unfortunately not an option (at least not a
good one).

Is there no way to create a class variable that exists during
definition of the class? (I cannot imagine there isn't, since
technically it's possible and manually it can be done...)

Ole
The metaclass hook occurs *after* class definition, anything using a
side-effect of a metaclass hook then, *must* occur after the execution
of the metaclass hook. At the time you want to write classvar.append
the "class" is only a namespace, so, if you really need this feature
you'll need to look elsewhere for at least *part* of the solution.

A global "classvar" that, when appended to, caches values until your
metaclass is called and transfers the cache to the class should *work*,
but egads that's ugly compared to just classvar = [] . I guess what I'd
ask is *why* is avoiding that single line so important. It could be
there's a reasonable answer, but the amount of machinery required to
avoid it is going to be significant.

class meta( type ):
newClassVar = []
def __new__( cls, name, bases, dictionary ):
dictionary[ 'classvar' ] = cls.newClassVar[:]
del cls.newClassVar[:]
return super( meta, cls ).__new__( cls, name, bases, dictionary )
__metaclass__ = meta
classvar = meta.newClassVar

or something along those lines...

Um, ick, but HTH,
Mike

--
________________________________________________
Mike C. Fletcher
Designer, VR Plumber, Coder
http://www.vrplumber.com
http://blog.vrplumber.com
 
B

Bengt Richter

Thanks! It's a bit icky, yes, but I've been so wrapped up in
complicated thinking that I didn't see this. It's actually quite an
OK solution (I need it because I have an internal representation for
method interfaces that needs to be saved somewhere without the user
having to worry about it, and without them having to set variables.
Method interfaces are class specific, and the only other thing I could
do would be to have a dictionary of classes somewhere, but as the
class itself uses its interface I can't see any really sensible way to
go about this differently).

Ole


2005/8/4 said:
Jan-Ole Esleben wrote:
=20
Yes, that works, but it is unfortunately not an option (at least not a
good one).

Is there no way to create a class variable that exists during
definition of the class? (I cannot imagine there isn't, since
technically it's possible and manually it can be done...)

Ole
The metaclass hook occurs *after* class definition, anything using a
side-effect of a metaclass hook then, *must* occur after the execution
of the metaclass hook. At the time you want to write classvar.append
the "class" is only a namespace, so, if you really need this feature
you'll need to look elsewhere for at least *part* of the solution.
=20
A global "classvar" that, when appended to, caches values until your
metaclass is called and transfers the cache to the class should *work*,
but egads that's ugly compared to just classvar =3D [] . I guess what I'= d
ask is *why* is avoiding that single line so important. It could be
there's a reasonable answer, but the amount of machinery required to
avoid it is going to be significant.
=20
class meta( type ):
newClassVar =3D []
def __new__( cls, name, bases, dictionary ):
dictionary[ 'classvar' ] =3D cls.newClassVar[:]
del cls.newClassVar[:]
return super( meta, cls ).__new__( cls, name, bases, dictionary )
__metaclass__ =3D meta
classvar =3D meta.newClassVar
=20
or something along those lines...
=20
Um, ick, but HTH,
Mike
=20
--
What about using a closure variable instead of the global mentioned above? E.g.,
... classvar = []
... class TheClass(object):
... classvar.append(1)
... classvar.append(2)
... TheClass.classvar = classvar
... return TheClass
... [1, 2]

I'm wondering what the real problem is. If you are creating some kind of class
variable that accumulates things related to methods, why can't the metaclass do
the whole job after the methods have all been defined cleanly?

OTOH, if not, have you considered that decorators execute a def-time of the decorated
functions -- which for methods defined in class scope will be during class definition
execution. E.g., you could make classvars accessible through a base class and pump into
via decorators (note that you can decorate with Base.deco or bind it by itself as below)
... classvar = []
... @classmethod
... def deco(cls, f):
... cls.classvar.append((f.func_name, f.func_code.co_argcount))
... ... @deco
... def foo(): pass
... @Base.deco
... def bar(x, y): print 'two args:', x, y
... [('foo', 0), ('bar', 2)]

Incidentally, you can see that deco and classvar belong to the Base class, though
accessible as instance and class attributes
>>> tc = TheClass()
>>> tc.classvar [('foo', 0), ('bar', 2)]
>>> TheClass.mro()
>>> vars(tc) {}
>>> vars(TheClass).keys() ['__module__', 'foo', 'bar', '__doc__']
>>> vars(Base).keys()
['__module__', 'deco', 'classvar', '__dict__', '__weakref__', '__doc__']

HTH. Add metaclasses and stir to taste ;-)

Regards,
Bengt Richter
 

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,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top