>>> class Foo(object):
def __new__(cls, kls):
class Bar(object):
def __init__(self, length, width, height):
try:
self.length = kls.length
except AttributeError:
self.length = length
try:
self.width = kls.width
except AttributeError:
self.width = width
self.heigth = height
return Bar
>>> @Foo
class Buzz(object):
def __init__(self, length, width):
self.length = length
self.width = width
>>> Buzz(1, 2, 3)
<__main__.Bar object at 0x00BD5390>
>>> b = Buzz(1, 2, 3)
>>> b.length
1
>>> b.width
2
>>> b.height
3
As you can see, 'b' is an instance of Bar, but that can be fixed by decorating Bar with the wraps decorator from the functools module.
Hmm, I just realized I'm turning class attributes into instance attributes with this code which is not something someone usually wants. I also realized that functools.wraps needs to be passed some extra arguments to work on classes, because the __doc__ attribute is read only and dictproxy objects don't have an update method.
This causes something which might become a problem: the wrapped object's dict contains the wrapper's class attributes (the ones used to preserve the default values).
Anyway, here's a different version that keeps instance attributes the way they are and also preserves their default values:
>>> class Foo(object):
def __new__(self, kls):
@functools.wraps(kls, assigned=('__module__', '__name__'), updated=())
class Bar(object):
try:
length = kls().length
except AttributeError:
length = 0
try:
width = kls().width
except AttributeError:
width = 0
def __init__(self, height=0, length=length, width=width):
self.length = length
self.width = width
self.height = height
return Bar
>>> @Foo
class Derp(object):
def __init__(self, length=8, width=9):
self.length = length
self.width = width
>>> Derp.__name__
'Derp'
It's not very pretty, and I can't think of a use case right now, but it's interesting as an experiment.
No comments:
Post a Comment