stackoverflow question
Owen Jacobson
angrybaldguy at gmail.com
Sat Mar 10 15:03:25 EST 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