views:

226

answers:

7

How do I put off attribute access in Python?

Let's assume we have:

    def foo():
        ...

    class Bar:
        ...

    bar = Bar()

Is it possible to implement Bar so that any time bar is accessed, a value returned by the callback foo() would be provided?

bar name already exists in the context. That's why it's access semantics should be preserved (it cannot be a callable, turning bar into a property of a class, using SomeClass.bar instead of bar also won't work). I need to keep everything as-is, but change the program so that bar would refer to on-the-fly generated data by foo().

UPD: Thanks all for your answers, from which it seems impossible to do this type of thing in Python. I'm gonna find a workaround.

+5  A: 

I guess you want to link some attribute "data" to foo:

class Bar:
    data = property(lambda self: foo())


bar = Bar()
bar.data # calls foo()
ebo
Also, it should be noted, that memoization of foo() value (considering the function is pure) should help to not calculate bar.data on second access. http://code.activestate.com/recipes/52201/
drdaeman
+2  A: 

You're basically asking for a way to hijack a variable (how would you reassign it?) in the module namespace, which is not possible in Python.

You'll have to use attribute accessors of a class if you want the described behavior:

class MyClass(object):
    def __getattr__(self, attr):
        if attr == 'bar':
            print 'getting bar... call the foo()!'
        else:
            return object.__getattribute__(self, attr)

    def __setattr__(self, attr, val):
        if attr == 'bar':
            print 'bar was set to', val
        else:
            object.__setattr__(self, attr, val)
Blixt
A: 

"any time bar is accessed"... What kinds of accesses are your callers going to be making? (E.g. are they doing "1+bar", are they doing "bar[5:]", are they doing "bar.func()", etc). Will they call the Bar() constructor each time?

Right now, your question is so fuzzy and general that I think it's impossible. But if you're a bit more specific then we might be able to help.

user9876
A: 

Is this what you're looking for?

>>> def foo(): print('foo() was called');
...
>>> class Bar:
...     pass;
...
>>> bar = Bar();
>>> bar.data = foo;
>>> bar.data()
foo() was called
>>>
AgentLiquid
This throws because foo does not take a self parameter and it's not an attribute.
ebo
A: 

Thanks for the clarification!

This just can't work! A variable is a variable in most languages, not a function-call. You can do much in Python, but you just can't do that.

The reason is, that you have always some intrinsic language rules. One rule in Python is, that variables are variables. When you read a variable (not modifying it or anything else) you can rely, that it will be the same in the next code-line.

Monkeypatching it to be a function call would just change this rule.

What you want, could only be done by a still more dynamic language. Something like a macro-processing system or a language that do not have variables but something like labels that can be attached to anything. But this would also make compiler-creation for it much more difficult -- hence fully dynamic. The compiler would have to take all coding in the program into account.

Juergen
Python's property() function, as well as the ability to override comparison operators explicitly allows you to allow variables to be treated any way you'd like. Not sure why this is the accepted solution...
Chris S
@Chris: Because it is correct. The question was about a variable not an attribute of an object. You should read the question and also see the difference between a plain variable and an attribute of an object. Please consider this -- or provide an example how you want to provide a solution. You just can't without changing the python interpreter itself. Also look at comments on the original question.
Juergen
@Chris: Additional info: You also can't hijack a question and interpret it as you want. When somebody asks to have a call semantics on "a = bar" you can't just change it to "a = bar()" or even "a = bar.data" just because it fits your answer. Somebody asked a question and this is the answer! Do not change the question to fit yours!
Juergen
A: 

You can override Bar's __new__ method to execute arbitrary code and return a new instance. See: http://docs.python.org/reference/datamodel.html#object.__new__

Here's a contrived example:

>>> class Bar(object):
...     def __new__(cls):
...             print "Calling Bar.__new__"
...             return super(Bar, cls).__new__(cls)
...
>>> bar = Bar()
Calling Bar.__new__
>>> bar
<__main__.Bar object at 0x0222B3D0>

EDIT: I misunderstood what you wanted, your clarification cleared that up. I don't think what you want is possible.

dbb
A: 

If you want the results of foo(), the easiest way is to do this:

foo()

Anything else is just unnecessary complication. I suspect that you have oversimplified your example until it doens't make sense.

Edit: OK, so you are trying to change somebody elses code. No, it's not possible to transform a variable to a method in a generic way. You could replace it with an object that will return different variables of it's used in a way that consistently will cause some sort of python method to be called.

barrio.py: bar = "Some sort of string"

fool.py:

import random

class Foo:
    def __str__(self):
        return str(random.random())

import barrio
barrio.bar = Foo()

Now run python:

>>> import fool
>>> import barrio

>>> print barrio.bar
0.783394625457
>>> print barrio.bar
0.662363816543
>>> print barrio.bar
0.342930701226
>>> print barrio.bar
0.781452467433

This works because print will call str on the object, since it's not a string. But in general, no, it's not possible.

Lennart Regebro