开发者

In Python, how can I use an attribute as both and attribute and a method/callable?

开发者 https://www.devze.com 2023-04-12 01:58 出处:网络
This is kindof an experiment. I\'m interested in an API that supports both of these syntaxes: obj.thing

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
0

精彩评论

暂无评论...
验证码 换一张
取 消