Reputation: 12083
When using __new__ to customize the creation of a metaclass, we can pass attributes to the type().__new__ method which will be set on the object before it is returned, e.g.
class Foo(type):
def __new__(cls, name, bases, attrs):
attrs['customAttr'] = 'someVal'
return type.__new__(cls, name, bases, attrs)
So that:
>> Foo.__dict__
{'customeAttr': 'someVal', ...}
However I don't know how to do the same for a normal (non-meta) class, which causes a problem when using __setattr__:
class Bar(object):
def __new__(cls, someVal):
obj = object().__new__(cls) # cant pass custom attrs
obj.customAttr = someVal # obj is already a Bar and invokes __setattr__
return obj
def __setattr__(*args): raise Exception('read-only class')
So that unfortunately:
>>> Bar(42)
...
Exception: read-only class
In the __new__ of Bar I get back a fully fledged class instance from object() and any attribute access goes through normal lookup rules, in this case invoking __setattr__. Metaclass Foo avoids this as type() will set attributes before returning the instance during low-level creation whereas object() will not.
Is there a way of passing attributes to object() or is another another type I can use as the instance returned from __new__ that does allow attributes to be set before it becomes a full class instance? I am not interesting in solutions like setting __class__ after instance creation.
Upvotes: 2
Views: 2858
Reputation: 155323
You have to explictly bypass your own class's __setattr__
by calling the super
or root object
__setattr__
. So you'd change:
obj.customAttr = someVal
to:
object.__setattr__(obj, 'customAttr', someVal)
A less general approach (doesn't apply to __slots__
based classes) is to directly assign to __dict__
using dict
operations:
obj.__dict__['customAttr'] = someVal # Equivalently: vars(obj)['customAttr'] = someVal
The first approach is what the newly __slots__
-ed uuid.UUID
now uses; before it became __slots__
-ed, it used the second approach. In both cases this was needed because they used the same __setattr__
trick to make the type as immutable as possible (without going to the trouble of subclassing tuple
, a la typing.NamedTuple
).
Upvotes: 3