Using a subclass for __dict__
Peter Otten
__peter__ at web.de
Thu Feb 13 14:10:30 EST 2014
Demian Brecht wrote:
> Using bases in a metaclass, I've been able to easily figure out when
> an attribute is being added to an instance of a class. However, what
> I'm /actually/ looking to do is to intercept when attributes are being
> added to a class (not an instance of). I thought that I'd be able to
> do so by passing in a subclass of dict to type.__new__ of the
> metaclass's __new__ and implementing a custom __setitem__, but
> apparently that's not the case.
>
> If you're curious as to why I'm doing this, I'm trying to clean up
> https://gist.github.com/demianbrecht/6944269 and get rid of the
> late_bind method there in favour of using the appropriate magic
> methods. The intention is to piggyback off of abc's
> __abstractmethods__ in order to implement a "looks_like" method that
> checks that one class conforms to an unrelated class's interface
> rather than using inheritance to enforce interface implementations at
> class creation time. This is /only/ intended to be a proof of concept
> for demonstration purposes and nothing that I'd ever actually
> implement, so no need for flaming the concept :)
>
> # test.py
>
> class Dict(dict):
> def __setattr__(self, key, value):
> print 'Dict.__setattr__'
> dict.__setattr__(self, key, value)
>
> def __setitem__(self, key, value):
> print 'Dict.__setitem__'
> dict.__setitem__(self, key, value)
>
> class Bar(object):
> def __setattr__(self, key, value):
> print 'Bar.__setattr__'
> object.__setattr__(self, key, value)
>
> class metafoo(type):
> def __new__(mcls, name, bases, dct):
> return type.__new__(mcls, name, (Bar,), Dict(dct))
>
> class Foo(object):
> __metaclass__ = metafoo
>
>>>> import test
>>>> f = test.Foo()
>>>> f.foo = 'bar'
> Bar.__setattr__ # expected
>>>> test.Foo.foo = 'bar'
> # I'm expecting Dict.__setattr__ here, but... *crickets*
>
> Am I missing something here, or do I just have to live with what I
> currently have in my gist?
I don't really understand what you are trying to do in the gist, perhaps you want
<http://docs.python.org/dev/reference/datamodel.html#customizing-instance-and-subclass-checks>
?
Anyway, intercepting __setattr__() in the class becomes easy once you remember that a class is just
an instance of its metaclass:
>>> class FooMeta(type):
... def __setattr__(self, name, value):
... print value, "-->", name
... super(FooMeta, self).__setattr__(name, value)
...
>>> class Foo:
... __metaclass__ = FooMeta
...
>>> Foo.bar = "baz"
baz --> bar
Python 3 has a bit more to offer in this area, see
<http://docs.python.org/dev/reference/datamodel.html#preparing-the-class-namespace>
More information about the Python-list
mailing list