Using a subclass for __dict__

Peter Otten __peter__ at web.de
Thu Feb 13 20:10:30 CET 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