views:

176

answers:

3

Here is my problem. I want the following class to have a bunch of property attributes. I could either write them all out like foo and bar, or based on some other examples I've seen, it looks like I could use a class decorator, a metaclass, or override the __new__ method to set the properties automagically. I'm just not sure what the "right" way to do it would be.

class Test(object):
    def calculate_attr(self, attr):
        # do calculaty stuff
        return attr

    @property
    def foo(self):
        return self.calculate_attr('foo')

    @property
    def bar(self):
        return self.calculate_attr('bar')
+2  A: 

A metaclass's __new__ does not become the __new__ for the class you make—it's used to make the class itself. The actually class object is returned by the metaclass. A new instance of a class is returned by __new__.

Consider the following (insane) code:

def MyMetaClass(name, bases, dict):
    print "name", name
    print "bases", bases
    print "dict", dict
    return 7

class C('hello', 'world'):
    __metaclass__ = MyMetaClass

    foo = "bar"

    def baz(self, qux):
        pass

print "C", C

(I used a function instead of a class as the metaclass. Any callable can be used as a metaclass, but many people choose to right theirs as classes that inherit from type with new overrided. The differences between that an a function are subtle.)

It outputs

name C
bases ('hello', 'world')
dict {'baz': <function baz at 0x4034c844>, '__module__': '__main__', 'foo': 'bar', '__metaclass__': <function MyMetaClass at 0x40345c34>}
C 7

Does that help you better make sense of what metaclasses are?

You will very seldom need to define a metaclass of your own.

Mike Graham
+1  A: 

Metaclass is used when new class - not instance - is created. This way you can for example register classes (django does it and uses it for example to create tables in the database). Since class is an instruction you can think about as a decorator for a class.

gruszczy
+5  A: 

Magic is bad. It makes your code harder to understand and maintain. You virtually never need metaclasses or __new__.

It looks like your use case could be implemented with pretty straightforward code (with only a small hint of magic):

class Test(object):
    def calculate_attr(self, attr):
        return something

    def __getattr__(self, name):
        return self.calculate_attr(name)
Mike Graham
@You virtually never need metaclasses or `__new__`: I strongly disagree. At my company we had already two occasions, when we had good motivation for using metaclasses. First was creating abstraction layer for a custom database we had used and the other was creating our own Model system for ldap. I can't really understand, where comes this resentment for metaclasses from. It's an easy concept to grasp and really useful in certain situations.
gruszczy