stackoverflow question

Ethan Furman ethan at stoneleaf.us
Sat Mar 10 17:21:55 EST 2012


Owen Jacobson wrote:
> 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.

The idea is good. The devil is in the details, as usual. How is the 
metaclass going to know:
 1) which attributes to replace
 2) what to replace them with?
~Ethan~


More information about the Python-list mailing list

AltStyle によって変換されたページ (->オリジナル) /