In Python, how can I use an attribute as both and attribute and a method/callable?
This is kindof an experiment. I'm interested in an API that supports both of these syntaxes:
obj.thing
--> returns default value
obj.thing(2, 'a')
--> returns value derived from *args and **kwargs
"thing
" is the same object in both cases; I'd like the calling of thing to be optional (or implicit, if there are not () after it).
I tried over-riding __repr__, but that's just the visual representation of the the object itself, and what is actually returned is an instance of the containing object (here, 'obj'). So, no good.
I'm thinking that there would be an attribute set on an object that was a callable (don't care if it's an instance, a def, or just __call__ on the object) that has enough default values:
class CallableDefault(object):
__call__(self, num=3, letter="z"):
return letter * num
class DumbObject(object):
foo = CallableDefault()
obj = DumbObject()
so, ideally, doing obj
alone would return "zzz", but one could also do obj(7,'a')
开发者_Python百科and get 'aaaaaaa'.
I'm thinking decorators might be the way to do this, but I'm not great with decorators. One could override the getattr() call on the containing class, but that would mean that it has to be in a containing class that supports this feature.
What you describe could work, but notice that now the value of the attribute is constrained to be an object of your CallableDefault
class. This probably won't be very useful.
I strongly suggest that you don't try to do this. For one thing, you're spending a lot of time trying to trick Python into doing something it doesn't want to do. For another, the users of your API will be confused because it acts differently than every other Python code they've ever seen. They will be confused.
Write a Python API that works naturally in Python.
What happens when you do either
obj.thing
or
obj.thing(2, 'a')
is Python goes looking for thing
on obj
; once it has thing
it either returns it (first case above), or calls it with the parameters (second case) -- the critical point being that the call does not happen until after the attribute is retrieved -- and the containing class has no way of knowing if the thing it returns will be called or not.
You could add a __call__
method to every type you might use this way, but that way lies madness.
Update
Well, as long as you're comfortable with insanity, you could try something like this:
class CallableStr(str):
def __call__(self, num, letter):
return num*letter
class CallableInt(int):
def __call__(self, num, pow):
return num ** pow
class Tester(object):
wierd = CallableStr('zzz')
big = CallableInt(3)
t = Tester()
print repr(t.wierd)
print repr(t.wierd(7, 'a'))
print repr(t.big)
print repr(t.big(2, 16))
One nice thing about this magic object is that it becomes normal soon as you use it in a calculation (or call):
print type(t.big), type(t.big + 3), t.big + 3
print type(t.big), type(t.big(2, 3) + 9), t.big(2, 3) + 9
which results in
<class '__main__.CallableInt'> <type 'int'> 6
<class '__main__.CallableInt'> <type 'int'> 17
精彩评论