stackoverflow question

Owen Jacobson angrybaldguy at gmail.com
Sat Mar 10 21:03:25 CET 2012


On 2012-03-09 22:10:18 +0000, Ethan Furman said:

> Hey all!
> 
> I posted a question/answer on SO earlier, but there seems to be some 
> confusion around either the question or the answer (judging from the 
> comments).
> 
> http://stackoverflow.com/q/9638921/208880
> 
> If anyone here is willing to take a look at it and let me know if I did 
> not write it well, I would appreciate the feedback.
> 
> 
> Here's the question text:
> ------------------------
> I'm writing a metaclass to do some cool stuff, and part of its 
> processing is to check that certain attributes exist when the class is 
> created.  Some of these are mutable, and would normally be set in 
> `__init__`, but since `__init__` isn't run until the instance is 
> created the metaclass won't know that the attribute *will* be created, 
> and raises an error.  I could do something like:
> 
>      class Test(meta=Meta):
>          mutable = None
>          def __init__(self):
>              self.mutable = list()
> 
> But that isn't very elegant, and also violates DRY.
> 
> What I need is some way to have:
> 
>      class Test(metaclass=Meta):
>          mutable = list()
> 
>      t1 = Test()
>      t2 = Test()
>      t1.mutable.append('one')
>      t2.mutable.append('two')
>      t1.mutable  # prints ['one']
>      t2.mutable  # prints ['two']
> 
> Any ideas on how this can be accomplished?
> 
> Also, the metaclass doing the checking doesn't care what type of object 
> the attribute is, only that it is there.
> ---------------------------

Why check what you can ensure? The __init__ function your metaclass 
passes to type() doesn't have to be the __init__ method your metaclass 
received. Consider the following:

>>> import functools as f
>>> 
>>> def make_init(real_init):
>>>     """Define an __init__ method that ensures ``self.mutable`` is set. If the
>>>     passed ``real_init`` function later replaces ``self.mutable``, that value
>>>     is preserved; otherwise, ``self.mutable`` is set to a new, empty list.
>>> 
>>>     Arguments to the generated ``__init__`` method are passed to the original
>>>     ``real_init`` unchanged.
>>>     """
>>>     def __init__(self, *args, **kwargs):
>>>         self.mutable = list()
>>>         if real_init is not None:
>>>             return real_init(self, *args, **kwargs)
>>>     if real_init is not None:
>>>         f.update_wrapper(__init__, real_init)
>>>     return __init__
>>> 
>>> class Meta(type):
>>>     def __new__(meta, name, parents, attributes):
>>>         attributes['__init__'] = make_init(attributes.get('__init__', None))
>>>         return type.__new__(Meta, name, parents, attributes)
>>> 
>>> class C(object):
>>>     __metaclass__ = Meta
>>> 
>>> a, b = C(), C()
>>> 
>>> a.mutable.append(3)
>>> b.mutable.append(5)
>>> 
>>> a.mutable
[3]
>>> b.mutable
[5]

All instances of classes whose metaclass is Meta will, guaranteed, have 
an instance field named 'mutable'. Its value is a list created at 
instance creation time, unless the instance's __init__ provides a 
different value.

What've I missed?

-o




More information about the Python-list mailing list